首页
社区
课程
招聘
[原创] 看雪CTF2018-第三题-PWN_wow writeup
发表于: 2018-6-22 15:30 5648

[原创] 看雪CTF2018-第三题-PWN_wow writeup

2018-6-22 15:30
5648

0x00 初步分析程序
0x01 绕过反调试
0x02 寻找key解密程序
0x03 寻找程序漏洞点(栈溢出和格式化字符串漏洞)
0x04 利用printf泄露canary和libc地址
0x05 ret2libc技术get shell

按C转换部分正常数据为指令:

即mprotect(start=0x400000, len=0x1000, PROT_READ|PROT_WRITE|PROT_EXEC)

然后又执行了一个系统指令,这里IDA没有正确解析出来,但是可以由调用号0查表可知,这里是在调用sys_read命令:

完整的调用为:sys_read(0, char* szCh, 6) 其中文件描述符fd为0表示标准输入。

在读取了6个字符后,程序使用该输入和0x40087f开始的数据进行了异或的操作并写入,应该是在这里进行了程序的解密,0xfb0x90作为解密结束的标志:

由于程序开了DX,不能通过直接栈溢出写shellcode执行,因此这里用了ret2libc技术,跳转到libc中的system函数开启shell。

由于是64位下的程序,函数传参优先不通过栈,而是通过寄存器传参,第一个参数存放在rdi寄存器中,因此需要构造的pop rdi;ret的gadget将system函数的第一个参数"/bin/sh"传入,使用工具ROPgadget(https://github.com/JonathanSalwan/ROPgadget)在wow的bin里找pop rdi;ret的gadget,得到地址为0x400b23:

目录

0x00 初步分析程序
0x01 绕过反调试
0x02 寻找key解密程序
0x03 寻找程序漏洞点(栈溢出和格式化字符串漏洞)
0x04 利用printf泄露canary和libc地址
0x05 ret2libc技术get shell


0x00 初步分析程序

拿到程序,file发现是linux 64位下的程序,ldd查看调用的库信息:

 查看开启的保护机制,可以看到开启了canary和NX(堆栈不可执行):
 IDA打开:

welcome函数打印欢迎信息,在返回的时候调用了test函数:

test函数中,调用了ptrace对程序本身进程进行trace,因为一个进程最多只能被一个进程trace,而像IDA、gdb等调试工具在动态调试时都是通过ptrace实现的,因此此时ptrace返回值(rax寄存器中)为负值,根据检测逻辑会执行sys_exit退出,从而实现反调试:

在返回后程序跳转到0x400818处,这里使用了SMC技术,部分指令被加密了,IDA不能正常解析:

按C转换部分正常数据为指令:

可以看到调用了sys_mprotect指令,用于设置内存访问权限,这里权限位值为7,说明可读可写可执行,可参考mman.h头文件的相关定义:

即mprotect(start=0x400000, len=0x1000, PROT_READ|PROT_WRITE|PROT_EXEC)

然后又执行了一个系统指令,这里IDA没有正确解析出来,但是可以由调用号0查表可知,这里是在调用sys_read命令:


完整的调用为:sys_read(0, char* szCh, 6) 其中文件描述符fd为0表示标准输入。

在读取了6个字符后,程序使用该输入和0x40087f开始的数据进行了异或的操作并写入,应该是在这里进行了程序的解密,0xfb0x90作为解密结束的标志:

到这里,程序的初步分析完毕,下一步目标是找到合适的输入,对程序数据进行解密。

0x01 绕过反调试

在刚刚说到的test函数中,程序对自身调用了ptrace以达到反调试的目的,然后对返回值进行判断,小于0则执行sys_exit退出,因此这里只需要patch跳转指令(将jge改为jmp),强制跳转即可:


0x02 寻找key解密程序

绕过反调试后,就可以愉快地动态调试了,由于输入规定是明文,而且异或后的数据需要出现0xfb,0x90,因此需要结合脚本逐字符爆破可能的key值,过程需要结合动态调试对解密后的数据进行验证,最后脚本如下:
# coding:utf-8
# author: sherllyyang00@gmail.com
# encrypt文件为bin中0x40087f-0x400a78的数据
def decrypt(start,filename,key):
	with open("encrypt","rb") as f:
		data = f.read()[start:]
	res=[]
	for i in range(32,127):
		res=[]
		found = 0
		for d in data:
			byte = ord(d)^i^key
			res.append(chr(byte))
			if byte == 0x90 and res[-2] == '\xfb':
				print "[*] key: ",chr(i)
				found = 1
		if found == 1:
			# print len(res)
			# print res
			if i == ord('a'):
				pos1 = "".join(res).find("\xfb\x90")
				print pos1
				pos2 = res[pos1+2:].index('\x90')
				print pos2
				pos = pos1+2+pos2+1
				res = res[:pos1+2+pos2+1]
				with open(filename,"wb") as f:
					f.write("".join(res))
				return pos

def decrypt_end(start,filename,key):
	with open("encrypt","rb") as f:
		data = f.read()[start:]
	res=[]
	for i in range(32,127):
		res=[]
		found = 0
		for d in data:
			byte = ord(d)^i^key
			res.append(chr(byte))
			if byte == 0x90:
				print "[*] key: ",chr(i)
				found = 1
				print len(res)
				break
		if found == 1:
			# print len(res)
			# print res
			if i == ord('K'):
				pos = "".join(res).find("\x90")
				print pos
				res = res[:pos+1]
				with open(filename,"wb") as f:
					f.write("".join(res))
				return pos
	return 0
pos = decrypt(start=0,filename="decrypt_1",key=0) # 'e' 
pos = decrypt(start=74,filename="decrypt_2",key=ord('e')) # 'v' 
pos = decrypt(start=74+71,filename="decrypt_3",key=ord('e')^ord('v')) # 'X'  /'6' 'W' 'X'
pos = decrypt(start=74+71+71,filename="decrypt_4",key=ord('e')^ord('v')^ord('X')) # 'n'
pos = decrypt(start=74+71+71+71,filename="decrypt_5",key=ord('e')^ord('v')^ord('X')^ord('n')) # 'a'
pos = decrypt_end(start=74+71+71+71+62,filename="decrypt_6",key=ord('e')^ord('v')^ord('X')^ord('n')^ord('a')) # 'K'
# evXnaK
得到正确的key值为evXnaK,输入验证下: 
# coding:utf-8
# author: sherllyyang00@gmail.com
# encrypt文件为bin中0x40087f-0x400a78的数据
def decrypt(start,filename,key):
	with open("encrypt","rb") as f:
		data = f.read()[start:]
	res=[]
	for i in range(32,127):
		res=[]
		found = 0
		for d in data:
			byte = ord(d)^i^key
			res.append(chr(byte))
			if byte == 0x90 and res[-2] == '\xfb':
				print "[*] key: ",chr(i)
				found = 1
		if found == 1:
			# print len(res)
			# print res
			if i == ord('a'):
				pos1 = "".join(res).find("\xfb\x90")
				print pos1
				pos2 = res[pos1+2:].index('\x90')
				print pos2
				pos = pos1+2+pos2+1
				res = res[:pos1+2+pos2+1]
				with open(filename,"wb") as f:
					f.write("".join(res))
				return pos

def decrypt_end(start,filename,key):
	with open("encrypt","rb") as f:
		data = f.read()[start:]
	res=[]
	for i in range(32,127):
		res=[]
		found = 0
		for d in data:
			byte = ord(d)^i^key
			res.append(chr(byte))
			if byte == 0x90:
				print "[*] key: ",chr(i)
				found = 1
				print len(res)
				break
		if found == 1:
			# print len(res)
			# print res
			if i == ord('K'):
				pos = "".join(res).find("\x90")
				print pos
				res = res[:pos+1]
				with open(filename,"wb") as f:
					f.write("".join(res))
				return pos
	return 0
pos = decrypt(start=0,filename="decrypt_1",key=0) # 'e' 
pos = decrypt(start=74,filename="decrypt_2",key=ord('e')) # 'v' 
pos = decrypt(start=74+71,filename="decrypt_3",key=ord('e')^ord('v')) # 'X'  /'6' 'W' 'X'
pos = decrypt(start=74+71+71,filename="decrypt_4",key=ord('e')^ord('v')^ord('X')) # 'n'
pos = decrypt(start=74+71+71+71,filename="decrypt_5",key=ord('e')^ord('v')^ord('X')^ord('n')) # 'a'
pos = decrypt_end(start=74+71+71+71+62,filename="decrypt_6",key=ord('e')^ord('v')^ord('X')^ord('n')^ord('a')) # 'K'
# evXnaK
得到正确的key值为evXnaK,输入验证下: 

0x03  寻找程序漏洞点

在输入正确的key值后,得到解密后的程序如下:

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2018-6-23 23:53 被sherlly编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (4)
雪    币: 146
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习
2018-6-29 17:25
0
雪    币: 208
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
你好,start=74+71看不懂能解释下吗?
2018-6-29 20:44
0
雪    币: 155
活跃值: (120)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
4
elike 你好,start=74+71看不懂能解释下吗?
指的是上一步pos的值,因为每次解密的数据长度有限。74是第一次解密的数据长度,以此类推。
2018-6-30 10:19
0
雪    币: 208
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
sherlly 指的是上一步pos的值,因为每次解密的数据长度有限。74是第一次解密的数据长度,以此类推。
谢谢
2018-6-30 17:01
0
游客
登录 | 注册 方可回帖
返回
//