2021GFCTF RE部分WP
本帖最后由 zsky 于 2021-11-25 19:29 编辑# 2021GFCTFRE_WP
## wordy
去除花指令
```python
addr =0X556AE377FD56
end = 0x0556AE377FE40
flag = ""
for i in range(addr, end, 13):
c = get_bytes(i+4, 1)
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,映射后不能改变节区属性
(https://github.com/changeofpace/Self-Remapping-Code)
> 关于这个技术,可以参考下这位大佬的笔记 https://jev0n.com/2021/09/23/Self-Remapping.html
我们直接将`call sub_411CE0` 的地方nop掉,手动的把`byte_4409c0`的地方加1
```python
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+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
```
(https://the-x.cn/cryptography/Sm4.aspx)
`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的数据给他替换掉
```python
data =
addr = 0x004CB348# v13的地址
for i in range(len(data)):
patch_byte(addr+i, data)
print("OK")
```
然后绕过机器码验证,往下走,来到`sub_10001610`处
可以发现,这个地方肯定是与服务器通信了,我们直接运行,直接Wireshark抓包
提取数据
```cpp
---> 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举例子
```cpp
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加密
```python
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
提取出来
```cpp
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如下
```python
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 = # 签名
banben = # 版本
cmd_list = # 命令
data_len_list = # 数据长度
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 # 获取RC4密钥
s.send(get_data(1, 0x40, login_key, rc4_key))
data = s.recv(1024)
captcha_str = rc4_algorithm("dec", data, 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, 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,还是学到了很多东西的,继续加油。 chenjingyes 发表于 2021-11-26 16:52
楼主不把题目附件传上?
https://jev0n.lanzoui.com/ibhzswxjbza可以看这里,同时也欢迎师傅们参加明年的GFCTF:lol ZJevon 发表于 2021-11-26 20:09
https://jev0n.lanzoui.com/ibhzswxjbza可以看这里,同时也欢迎师傅们参加明年的GFCTF
好的 哈哈 最后多了两张图没有插入到正文? Hmily 发表于 2021-11-25 18:16
最后多了两张图没有插入到正文?
好的,我修正下 大佬,你真厉害 高手在民间。 不错哦,get到了 谢谢分享 分析的很不错。值得学习。 楼主不把题目附件传上?:lol chenjingyes 发表于 2021-11-26 16:52
楼主不把题目附件传上?
这个的题目附件我应该是没备份,全是我patch后的,之后的比赛我记得备份