下载qoobee
分析程序:
1.运行程序
2.找到第一个功能:
3.进入adopt函数:
可以看到开辟了一个堆,这个堆是用来存储QooBee信息的,返回堆的指针,保存在main函数的ebp-10h
。
堆中信息和位置:
4.分析整个程序,发现show_info()
函数里有一个format string漏洞。description()
函数里有一个buffer overflow函数。但是函数有栈保护,需要知道canary,正好可以通过前面发现的format string漏洞得到(同一个程序的不同函数里的canary是相同的)。
5.通过format string漏洞获得canary和ebp的值:
1 | p.recvuntil('Your Choice: ') |
6.获得canary和ebp就可以利用buffer overflow的漏洞了,下一步就是获取system()
函数在内存中的地址。这里我们采用pwntools
提供的DynELF
模块来进行内存搜索。首先我们需要实现一个leak(address)
函数,通过这个函数可以获取到某个地址上最少1 byte的数据。
leak函数应该是这样的:
1 | def leak(address): |
7.随后将这个函数作为参数再调用d = DynELF(leak, elf=ELF('./qoobee'))
就可以对DynELF模块进行初始化了。然后可以通过调用system_addr = d.lookup('system', 'libc')
来得到libc.so中system()在内存中的地址。
1 | d = DynELF(leak, elf = ELF('./qoobee')) |
8.要注意的是,通过DynELF模块只能获取到system()在内存中的地址,但无法获取字符串“/bin/sh”在内存中的地址。所以我们在payload中需要调用read()将“/bin/sh”这字符串写入到程序的.bss
段中。.bss
段是用来保存全局变量的值的,地址固定,并且可以读可写。通过readelf -S qoobee
这个命令就可以获取到.bss
段的地址了。
$ readelf -S qoobee
There are 28 section headers, starting at offset 0x279c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
……
[22] .got PROGBITS 0804b5fc 0025fc 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 0804b600 002600 000070 04 WA 0 0 4
[24] .data PROGBITS 0804b670 002670 00000c 00 WA 0 0 4
[25] .bss NOBITS 0804b680 00267c 00000c 00 WA 0 0 32
……
9.因为我们在执行完read()之后要接着调用system(“/bin/sh”)
,并且read()这个函数的参数有三个,所以我们需要一个pop pop pop ret
的gadget用来保证栈平衡。
10.整个攻击过程如下:首先通过DynELF获取到system()的地址后,我们又通过read将“/bin/sh”写入到.bss
段上,最后再调用system(.bss)
,执行“/bin/sh”。
最终的exp
reference
http://drops.wooyun.org/papers/7551