首页
论坛
课程
招聘
[原创]KCTF2022春季赛 第二题 末日邀请
2022-5-13 11:00 6022

[原创]KCTF2022春季赛 第二题 末日邀请

2022-5-13 11:00
6022

这是一道闯关题,没有加壳等手段,只是需要逆的逻辑比较多,稍显复杂,但只要花时间,就一定能做出来。我这里将程序分为不同的部分,依次对每个部分的算法逻辑进行还原,通过OllyDbg进行动态调试,判断每段逻辑后得到的数值,与自身逆出来的结果是否相符,若符合就继续逆下一个逻辑,直到最后一个逻辑逆完。然后再根据逻辑分析出输入的字符串。

Part 0x0

  1. sub_40100C用来显示文本内容
  2. sub_40103A用来读取输入字符串
  3. IDA分析出的 v9 >= 0,这里的v9是单字节有符号数,因此实际运算时表现为 v9 <= 0x80(用C写wp就没这么多事了)
  4. 部分操作是单字节运算,因此运算过后,与上0xFF
  5. 最终得到一个单字节的数,保存在v72中,后面会用到

Part 0x1

  1. 这部分是一个单走的函数sub_40106C,它用来在指定地址初始化一个大小为4096字节的缓冲区,里面包含了1024个生成的随机数
  2. 这里需要注意的是右移运算,实际用的是算术右移,因此需要注意判断操作数的正负情况,来进行对应的补符号位

Part 0x2

  1. 这里是对输入的字符串进行一个运算,还是算术右移需要注意,由于是右移8位,对于负数右移需要或上0xFF000000
  2. 这里会计算出一个值,并最终赋给变量v67,这个v67在最后一关前会进行判断,如果v67的值为0xf52e0765,则能成功闯关,得到唯一解

Part 0x3

  1. 这是一个操作字符串(会调用2次,第1次是传入的参数字符串)的函数,直接F5即可。减法运算需要注意只保留单字节结果

Part 0x4

  1. 这部分在栈中生成一个长度为200的数组,变量v66指向首地址
  2. 在Part 0x0中计算出来的值会赋值给变量v72,这里会将v72进行符号扩展后,赋值给变量v13(见红框)
  3. 在运算时,乘法运算依旧是带符号的,所以需要分情况讨论一下(见橙框)

Part 0x5

  1. 这里是一个判断,要求字符串前3个数异或后的结果,并与v66数组中靠后的3个数进行比较,若相同,才能进入下一关
  2. 经过测试,如果v72的值(在Part 0x0计算出)的小于0x80,即为正的情况下,那么v66中3个数的值异或的结果一定为0x7;否则将是一个接近-1(0xFFFFFFFF)的大数。
  3. 字符串输入的字符只能在(0-9a-zA-Z)中取,因此只要保证在v72小于0x80的情况下,凑出一个异或结果为0x7的数即可,这里我们选择"007",刚好可以过掉这一关

Part 0x6/0x7

  1. 这部分判断字符串的第4~7位,这里的字符串是在Part 0x3中经过处理后的字符串的值。这里只要简单凑一下,就可以得到这4个字符为"KCTF"
  2. 这里是v74[0]实际指向的,Arglist[6]的位置,可以通过汇编得出

Part 0x8

  1. 这部分运算直接F5就可以,这里只对最后9位字符串进行判断,要求这4位字符串依次读取,按照此算法,均可被整除
  2. 此时还无法得到正确的字符串序列(图中为了演示此算法已给出),需要放到后面去计算

Part 0x9

  1. 这是一个简单的排序算法,负责将后9位数按照大小进行排序

Part 0xA

  1. 这里会调用sub_4010B7,来操纵一个内置的字符串,然后将排序后的字符串后9位与进行操作后的字符串中的前9位依次进行比较,若相同才可进入之后的判断
  2. 可以发现,这里表明了,后9位字符串的取值正是"123456789"以某一种顺序排列后的结果

Part 0xB

  1. 这里做一个简单的fuzz,找到后9位数的排列顺序,以满足(除了v67的值等于0xf52e0765)外的所有条件
  2. 最终可以找到一个字符串"381654729"

Part 0xC

  1. 此时只差前3位字符串的值未确定了,还是一样,通过一个fuzz,遍历(000~ZZZ)的所有情况,找到符合要求的前3位(3个字符异或结果为7,这个条件也可以加上)
  2. 最终得到字符串"421"
  3. 拼接起来就是"421KCTF381654729"

【公告】看雪招聘大学实习生!看雪20年安全圈的口碑,助你快速成长!

最后于 2022-5-13 11:00 被Ally Switch编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (4)
雪    币: 3969
活跃值: 活跃值 (510)
能力值: ( LV12,RANK:274 )
在线值:
发帖
回帖
粉丝
htg 活跃值 3 2022-5-13 18:14
2
0
Part 0xC:
res = func(s)
这个func是如何关联到程序的?
雪    币: 5242
活跃值: 活跃值 (1105)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Ally Switch 活跃值 2022-5-13 22:22
3
0
htg Part 0xC: res = func(s) 这个func是如何关联到程序的?
import os
import itertools

def func(s):
	
	# Part 0x0
	v70 = 0
	v4 = len(s)
	v5 = 0
	v71 = v4
	v6 = v4
	v72 = 0
	if v4 != 0:
		v7 = s
		for i in range(v6):
			v5 = ord(v7[i]) ^ v5
			v5 = v5 & 0xFF
			v8 = 8
			for j in range(8):
				v9 = 2 * v5
				v9 = v9 & 0xFF
				v10 = v9 ^ 7
				if v9 < 0x80:
					v10 = v9
				v5 = v10
		v4 = v71
		v72 = v10
		print('Part 0x0 Calculate v72 For Part 0x4: ', hex(v72))
		
	
	# Part 0x1
	list_5B20 = sub_40106C()
	
	for x in list_5B20:
		#print(hex(x))
		pass
		
		
	# Part 0x2
	v11 = 0xFFFFFFFF
	for i in range(v4):
		tmp = list_5B20[0xFF & (v11 ^ ord(s[i]))]
		if v11 >= 0x80000000:
			v11 = ((v11 >> 8) | 0xFF000000) ^ tmp
		else:
			v11 = (v11 >> 8) ^ tmp
	v67 = v11 ^ 0xFFFFFFFF	
	print('Part 0x2, This Value Should Be 0xF52E0765: ', hex(v67))
	if v67 == 0xf52e0765:
		return 1

	
	# Part 0x3
	s = sub_4010B7(s, v4)
	print('Part 0x3 Manipulat Original String, Used For Xor: ', s)
	
	
	# Part 0x4
	v13 = v72
	v69 = 1
	v14 = v72 + 1
	v66 = []
	while(v13 < v14):
		if v13 > 0x80:
			v15 = 0xFFFFFF00 | v13
			for j in range(200):
				if (v15 & 1) != 0:
					v15_abs = 0xFFFFFFFF - v15 + 1
					v15_abs = 3 * v15_abs - 1
					v15 = 0xFFFFFFFF - (v15_abs - 1)
					
				else:
					if v15 > 0x80000000:
						v15 = (v15 >> 1) | 0x80000000
					else:
						v15 = v15 >> 1
				v66.append(hex(v15))
				#print(j, ' ', hex(v15))
			
		else:
			v15 = v13 & 0xFF
			for j in range(200):
				if (v15 & 1) != 0:
					v15 = 3 * v15 + 1
				else:
					if v15 > 0x80000000:
						v15 = (v15 >> 1) | 0x80000000
					else:
						v15 = v15 >> 1
				v66.append((v15))
				#print(j, ' ', hex(v15))
		v13 = v13 + 1
		
		
	# Part 0x5
	if v72 < 0x80:
		print('Part 0x5, Calculate With Or: ', (v66[195]), (v66[196]), (v66[197]))
		v17 = (v66[195]) | (v66[196]) | (v66[197])
		v18 = v71
		cmp_a = (s[0])^(s[1])^(s[2])
		print('Part 0x5, Or Result: ', hex(v17))
		print('Part 0x5, Xor Result: ', hex(cmp_a))
	else:
		print('Part 0x5, Calculate With Or: ', hex(int(v66[195], 16)), hex(int(v66[196], 16)), hex(int(v66[197], 16)))
		v17 = int(v66[195], 16) | int(v66[196], 16) | int(v66[197], 16)
		v18 = v71
		cmp_a = (s[0])^(s[1])^(s[2])
		print('Part 0x5, Or Result: ', hex(v17))
		print('Part 0x5, Xor Result: ', hex(cmp_a))
	
		
	# Part 0x6
	v19 = v17 + 2
	v20 = v18 - v19 - 7
	print('Part 0x6, This Value Should Be Less Than Or Equal 0: ' , v20)
	
	
	# Part 0x7 ['007KCTF' can pass here]
	print('Part 0x7, s[3] should be 0x14/20: ', s[3])
	print('Part 0x7, s[4] should be 0xC/12: ', s[4])
	print('Part 0x7, s[5] should be 0x1D/29: ', s[5])
	print('Part 0x7, s[5] should be 0xF/15: ', s[6])
	
	 
	
	# Part 0x8
	# v69 = 1, define in Part 0x4
	# v74[0] == s[6]
	# v74[1] ~ v74[
	# v19, Rest Length Of String
	# v70 = 0, define in Part 0x0
	
	v74 = s[6:]
	print('Part 0x8, Rest Length Of String: ', v19)
	v21 = 0
	v71 = 0
	if v19 > 0:
		v22 = 1
		for i in range(v19):
			v23 = v74[v22] + 10 * v70
			print('Part 0x8, (' ,v23, '=', v74[v22], '+', '10 *', v70, ')%', v69, '==', v23%v69)
			v24 = v23 - 0x37373737
			if v23 <= 0x4B435445:
				v24 = v23
			v70 = v24
			if (v24 % v69):
				print('Failed! ')
				return 0
				#break
			v69 = v69 + 1
			v22 = v69
	
	
	# Part 0x9  Just A Sort
	v25 = v19 - 1
	if (v19 - 1) > 0:
		v26 = v19 - 1
		for i in range(v26):
			v27 = 0
			if v25 > 0:
				for i in range(v25):
					v28 = v74[v27 + 1]
					v29 = v74[v27 + 2]
					if v28 > v29:
						v74[v27 + 1] = v29
						v74[v27 + 2] = v28
					v27 = v27 + 1
			v25 = v25 - 1
	print('Part 0x9, Last 9 Elements After Sort: ', v74[1:])
	
	
	# Part 0xA
	a_str = '1234567890_ABCDEFGHIJKLMNOPQRSTUVWXYZ'
	a_str = sub_4010B7(a_str, v19)
	print('Part 0xA, a_str: ', a_str)
	v30 = 0
	if v19 > 0:
		for i in range(v19):
			print('Part 0xA, Compare 2: ', a_str[v30], v74[v30+1])
			if a_str[v30] != v74[v30+1]:
				print('Failed!')
				return 0
			v30 = v30 + 1
	
	return 0
	
	
def sub_4010B7(s, length):
	
	res = []
	for i in range(length):
		v4 = ord(s[i])
		v5 = 48
		if v4 >= 58:
			v5 = 55
		res.append((v4 - v5) & 0xFF) 
		#res.append(chr((v4 - v5) & 0xFF))
	#print(''.join(res))
	
	return res
	

def sub_40106C():
	
	v1 = []
	
	for i in range(1024):
		v2 = i
		v3 = 8
		for j in range(8):
			if (v2 & 1) != 0:
				tmp = 0xEDB88320
			else:
				tmp = 0
			if v2 >= 0x80000000:
				v2 = ((v2 >> 1) | 0x80000000) ^ tmp
			else:
				v2 = (v2 >> 1) ^ tmp
			#print(hex(v2))
		v1.append(v2)
	v1.append(1)
	
	return v1

	
if __name__ == '__main__':

	#s = 'A7AKCTF381654729'
	#res = func(s)
	'''
	# Fuzz One For Last 9 Bytes
	prefix = 'A7AKCTF'
	maybe_result = []
	
	example = '123456789'
	result = itertools.permutations(example)
	for x in result:
		suffix = ''.join(x)
		s = prefix + suffix
		#print(s)
		res = func(s)
		if res == 1:
			print(res, s)
			break
	
	print(maybe_result)
	'''
	
	'''
	# Fuzz Two For First 3 Bytes
	suffix = 'KCTF381654729'
	text = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVMXYZ'
	result = itertools.permutations(text, 3)
	for x in result:
		prefix = ''.join(x)
		s = prefix + suffix
		#print(s)
		res = func(s)
		if res == 1:
			print(res, s)
			break
	'''


雪    币: 5242
活跃值: 活跃值 (1105)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
Ally Switch 活跃值 2022-5-13 22:28
4
0
htg Part 0xC: res = func(s) 这个func是如何关联到程序的?
定义了一个func,然后在main中去调用,代码写的比较呆,但恰好可以解出答案,两次fuzz都是单独去执行的。我在fuzz后9位的时候将func的返回值设置为1;fuzz前3位的时候,把Part 0x3~0xA都注释掉,然后将返回值设置为0
雪    币: 3969
活跃值: 活跃值 (510)
能力值: ( LV12,RANK:274 )
在线值:
发帖
回帖
粉丝
htg 活跃值 3 2022-5-13 22:49
5
0
游客
登录 | 注册 方可回帖
返回