2021GFCTF RE_WP
wordy
去除花指令
addr =0X556AE377FD56
end = 0x0556AE377FE40
flag = ""
for i in range(addr, end, 13):
c = get_bytes(i+4, 1)[0]
flag+=chr(c)
print(flag)
# GFCTF{u_are2wordy}
BabyReverse
IDA打开,去除所有的花指令
直接看下面对flag如何加密的,进入sub_412E10
, 发现是SM4加密
而传入的第二个参数是key,即byte_4409c0
是key,回到main函数再往上看,发现前面有一个对byte_4409c0
类似RC4加密的操作,
对 byte_4409C0
进行交叉引用
于是下断点调试来获取key,发现不行,猜测前面是反调试,从main函数头部下断点调试
最终定位到这个函数
采用的是self_mapping技术实现反调试,本质是创建secion的时候设置SEC_NO_CHANGE,映射后不能改变节区属性
Self-Remapping-Code
关于这个技术,可以参考下这位大佬的笔记 https://jev0n.com/2021/09/23/Self-Remapping.html
我们直接将call sub_411CE0
的地方nop掉,手动的把byte_4409c0
的地方加1
a = [ 0x07, 0xB8, 0x0D, 0x24, 0xB1, 0x0C, 0x2D, 0xC7, 0x28, 0x2D,
0xC3, 0x61, 0x66, 0x4F, 0x72, 0x13]
addr = 0x04409C0
for i in range(16):
patch_byte(addr+i, a[i]+1)
print("OK")
运行起来
得到key为 GF?->GirlFriend?
提取密文
0D 40 3B 87 A5 66 DA 74 92 7F BB E1 B8 CD EB BC 59 45 1B C0 38 99 AA 22 AA 3F 9D 21 07 4E 81 1F
SM4在线解密
2e69df5961f20aee0897cf1905156344
, 最终得到flag为 GFCTF{2e69df5961f20aee0897cf1905156344}
re_EasyRE_0x00
IDA打开分析,最关键的是sub_100016A0
函数
经过分析,发现sub_10001180
是解密login.key文件,生成的数据放到V13里面
然后下面这个地方是将V13处的数据与生成的一些数据进行对比,猜测是机器码的验证
这是V13处的数据
11 55 66 55 0D 50 51 0C FF 01 80 12 CE A9 08 75 73 65 72 32 33 33 33
最后8个字符是user2333
将对比的数据也提取出来, 然后结合题目,用户名用admin6677登录,长度是9,整理得
11 55 66 55 98 FA 9B 59 6F F6 14 8F E9 DA 09 61 64 6D 69 6E 36 36 37 37
我们写脚本,每次运行到对比数据的时候就把v13的数据给他替换掉
data = [0x11, 0x55, 0x66, 0x55, 0x98, 0xFA, 0x9B, 0x59, 0x6F, 0xF6, 0x14, 0x8F, 0xE9, 0xDA, 0x09, 0x61, 0x64, 0x6D, 0x69, 0x6E, 0x36, 0x36, 0x37, 0x37]
addr = 0x004CB348 # v13的地址
for i in range(len(data)):
patch_byte(addr+i, data[i])
print("OK")
然后绕过机器码验证,往下走,来到sub_10001610
处
可以发现,这个地方肯定是与服务器通信了,我们直接运行,直接Wireshark抓包
提取数据
---> 11 55 66 55 1a 27 00 00 00 00
<--- 11 55 66 55 66 27 00 00 0f 00 f3 46 8a be 81 62 ed 36 d5 df 28 dc 04 8a fd
---> 11 55 66 55 1a 27 01 00 40 00 0e a2 60 19 1f df 39 0d bc 62 48 57 5a 11 87 78 69 11 03 76 4b f9 2c 1f 35 fd ff 4a b8 d8 63 8f b6 b1 f0 cd d3 90 2d 27 05 b7 1e 01 22 74 91 1a a4 53 df 1d f4 69 7d 3e 29 bd d3 30 da 94 a3 03
<--- 11 55 66 55 66 27 01 00 48 00 84 cb 11 ef 71 51 30 0b b3 d8 c1 22 ac c4 ca f1 29 12 cf 79 f5 36 5f 5a 5e a8 f5 fa 62 3c e8 32 69 d6 a1 54 eb 1b 06 06 b0 68 20 5a 62 ea 48 ec 8a 3d 5c 40 d0 a8 03 94 6a 2e b7 f0 e4 33 aa a0 e3 f2 da f8 a9 cf 5d 92
重新调试,接着刚才的位置往下分析,看到了RC4的初始化及加密
猜测是刚开始,服务器端返回RC4的key,然后后面全部使用RC4加密方式进行加密
根据sub_10001350
这个函数可以猜测出数据包的格式, 拿上面服务器返回的key举例子
11 55 66 55 //标志
66 27 //版本
00 00 //命令
0f 00 //后面数据的长度
f3 46 8a be 81 62 ed 36 d5 df 28 dc 04 8a fd //数据,当命令为0的时候,是RC4的key,命令为1和2的时候,是RC4加密的数据
写脚本验证RC4加密
from Crypto.Cipher import ARC4 as rc4cipher
import binascii
def rc4_algorithm(encrypt_or_decrypt, data, key1):
if encrypt_or_decrypt == "enc":
key = key1
enc = rc4cipher.new(key)
res = enc.encrypt(data)
return res
elif encrypt_or_decrypt == "dec":
key = key1
enc = rc4cipher.new(key)
res = enc.decrypt(data)
return res
key = binascii.unhexlify("f3468abe8162ed36d5df28dc048afd")
data1 = binascii.unhexlify(
"0ea260191fdf390dbc6248575a118778691103764bf92c1f35fdff4ab8d8638fb6b1f0cdd3902d2705b71e012274911aa453df1df4697d3e29bdd330da94a303")
m1 = rc4_algorithm("dec", data1, key)
data2 = binascii.unhexlify(
"84cb11ef7151300bb3d8c122acc4caf12912cf79f5365f5a5ea8f5fa623ce83269d6a154eb1b0606b068205a62ea48ec8a3d5c40d0a803946a2eb7f0e433aaa0e3f2daf8a9cf5d92")
m2 = rc4_algorithm("dec", data2, key)
print(m1)
print(m2)
# b'\x8ayqv,\x8eYjj\xdb\xfa\x10\xd6\xa0=\xed!w\xa9/\xdd\xa3\x1a \x05!+\xbd\xd0\xa7\xe7\xd4\xba\t%\xb9N\xeeYR\xdc\xb0Pfq\xae\xe9\xc7\x1eB\xa3\x0eA\xb3\x08\xcf1\xb3\x12\xa5L\xd4`\xcc'
# b'\x00\x10\x00\x80B\x00Please update client!\r\nClient version=10010, Server version=10086\x00'
结合login.key,发现当命令为1的时候,向服务器发送的是login.key的数据,然后服务器返回信息
所以现在需要构造 真正的login.key(11 55 66 55 98 FA 9B 59 6F F6 14 8F E9 DA 09 61 64 6D 69 6E 36 36 37 37)
加密后的数据
sub_10001180
是解密函数,进去分析,发现是RSA的PKCS#1加密
根据这个结构找到e和n
提取出来
e: 65537
n: 0xd928b8efe000f72db5bda67a9aa0740defb555b2603736eecd6d01f38ef2fc79
分解得到p, q
p = 322922590106035145437937724697895880569
q = 304171468404401467258708275665013611777
利用rsatool.py生成private.pem
python rsatool.py -e 65537 -p 322922590106035145437937724697895880569 -q 304171468404401467258708275665013611777 -o private.pem
利用在线解密网站测试 https://the-x.cn/cryptography/Rsa.aspx
发现解密成功,将构造好的数据进行加密,
对于PKCS#1的填充方式可以参考下面2篇文章
https://www.cloudcared.cn/3155.html
https://www.cnblogs.com/feng9exe/p/8075447.html
然后写程序与服务器交互,发现服务器返回命令为2的验证码问题
Question(Send result in uint32_t format, 1 second!): 9540808 * 32 + 509 * 859 = ?
然后利用eval计算数值,构造,返回给服务器,即可得到flag,完整的exp如下
import socket
from Crypto.Cipher import ARC4 as rc4cipher
import re
import struct
login_key = [0x5D, 0x98, 0xEE, 0x8B, 0x68, 0x86, 0x2F, 0x56, 0xBA, 0xA1, 0x27, 0x2A, 0x68, 0x8B, 0x19, 0x31, 0x37, 0xC1, 0x2B, 0x1A, 0x80, 0x5F, 0xAB, 0x8C, 0xE0, 0xE6, 0x81, 0xDF, 0x05, 0xC6, 0xB1,
0x2F, 0x0E, 0x59, 0xC8, 0x45, 0x8A, 0x7D, 0x83, 0x35, 0x5F, 0x02, 0x05, 0x10, 0x8A, 0x35, 0x6D, 0x0C, 0xE8, 0x3C, 0x9C, 0x15, 0xD7, 0xDA, 0xF0, 0x96, 0x6D, 0x2E, 0x77, 0xEC, 0x78, 0x3B, 0x83, 0xB2]
def rc4_algorithm(encrypt_or_decrypt, data, key1):
if encrypt_or_decrypt == "enc":
key = key1
enc = rc4cipher.new(key)
res = enc.encrypt(data)
return res
elif encrypt_or_decrypt == "dec":
key = key1
enc = rc4cipher.new(key)
res = enc.decrypt(data)
return res
def get_data(_cmd, _len, _data, _key):
sig = [0x11, 0x55, 0x66, 0x55] # 签名
banben = [0x66, 0x27] # 版本
cmd_list = [_cmd, 0x00] # 命令
data_len_list = [_len, 0x00] # 数据长度
if _len != 0:
return bytes(sig + banben + cmd_list + data_len_list) + rc4_algorithm('enc', bytes(_data), _key)
return bytes(sig + banben + cmd_list + data_len_list)
def get_captcha(_captcha_str):
m = re.search(
r"Question\(Send result in uint32_t format, 1 second!\): (.*?) = ", _captcha_str)
c = eval(m.group(1))
return struct.pack("I", c)
if __name__ == '__main__':
address = ('119.27.179.145', 10086)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(address)
s.send(get_data(0, 0, [], None))
data = s.recv(1024)
rc4_key = data[10:] # 获取RC4密钥
s.send(get_data(1, 0x40, login_key, rc4_key))
data = s.recv(1024)
captcha_str = rc4_algorithm("dec", data[10:], rc4_key).decode()
captcha = get_captcha(captcha_str) # 计算得到验证码
print(f"Captcha: {captcha}")
# 向服务器返回验证码
send_data = get_data(2, len(captcha), list(captcha), rc4_key)
s.send(send_data)
data = s.recv(1024)
m = rc4_algorithm("dec", data[10:], rc4_key)
print(m)
s.close()
# Captcha: b'\xc84\x06\x03'
# b'\x00\x10\x00\x805\x00flag_0x00 = \x00GFCTF{e8e9071b7a70770bec1f6415c4ed4c1d}\x00'
得到flag为 GFCTF{e8e9071b7a70770bec1f6415c4ed4c1d}
这个Easy_RE在番外篇结束第1天捣鼓出来了,有点可惜,一直卡在找N和那个RSA解密了,一直在用常规的pow(c,d,n)解,后来才发现,RSA加密也得符合一定的标准,填充数据什么的,这里就是用了最普通的PKCS#1,还是学到了很多东西的,继续加油。