Phishing

img

一开始就看到一个奇怪的程序,不知道什么作用,先留着

img

根据题目描述和文件结构,是一个针对游戏用户的钓鱼攻击:

  1. 攻击者诱导用户打开 helpme.chm 帮助文件,然后 CHM文件执行恶意代码,最后 释放DLL文件进行劫持攻击
1
hh.exe -decompile extracted helpme.chm

先解压这个chm文件,然后会得到一个html文件test.html文件包含恶意JavaScript代码:

1
2
3
4
5
6
7
8
9
10
11
12
<head>
<script type="text/javascript">
function command() {
var command = "echo AAAAABogAAAAUgABAAEAAAAABgVLUA...(Base64数据)... > reversed.zip";
}
</script>
</head>
<body onload="command()">
<h1>Example</h1>
<p>This is an example of using CHM file to extract a malicious program.</p>
</body>
</html>

审计发现

- 页面加载时自动执行 command() 函数

- 函数将一段Base64编码的数据通过echo命令写入 reversed.zip 文件

- 文件名 reversed.zip 暗示数据可能被反转

将base64解码后: 000000001a2000000052000100010000,显然不是一个zip的标准文件头,反转后可得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import base64
# 读取Base64数据
with open('base64_data.txt', 'r') as f:
base64_data = f.read().strip()
# 解码
decoded_data = base64.b64decode(base64_data)
# 检查文件头
print(f"文件头: {decoded_data[:16].hex()}")
# 反转数据
reversed_data = decoded_data[::-1]
print(f"反转后文件头: {reversed_data[:16].hex()}")
# 保存为正常ZIP文件
with open('normal.zip', 'wb') as f:
f.write(reversed_data)

解压这个normal.zip文件可以得到一个dll文件:filename.dll

尝试提取可读字符串:

1
2
3
4
5
6
7
8
9
10
import re

with open('extracted_zip/filename.dll', 'rb') as f:
data = f.read()

# 提取Unicode字符串
unicode_strings = re.findall(rb'(?:[\x20-\x7e]\x00){6,}', data)
for s in unicode_strings:
decoded = s.decode('utf-16-le')
print(decoded)

C:\Users\hasay\source\repos\DllHijacking_in_HonkaiStarRail\x64\Release\DllHijacking_in_HonkaiStarRail.pdb

- 项目名称:DllHijacking_in_HonkaiStarRail

- 这是一个针对《崩坏:星穹铁道》的DLL劫持程序

1
2
3
4
1. CHM文件本质是编译后的HTML帮助文件
2. 可以包含HTML、CSS、JavaScript等内容
3. 双击打开CHM文件时,内嵌的JavaScript会自动执行
4. JavaScript可以执行系统命令(通过ActiveX等)

原理就是运行程序会触发dll劫持,显示flag,但是这个程序运行不起来,所以直接分析文件内的字符串获取flag:

flag{the_Security_level_of_HonkaiStarRail_is_???}

DNS分身术

img

使用指令查询dns,根据提示可能存在多个地区多个厂商返回的内容是不一样的

中国香港 地区的查询返回了:”5o_we_gEt_The_wh01e_fl@g}”并且给了提示

1
2
3
4
──(root㉿kali)-[/home/kali/Desktop]
└─# dig @ns3.dnsv2.com flag1.cyberopschallenge.cn TXT +short
"Hint: flag1 is split into three parts across different networks. Maybe edu, unicom, and telecom can see something different?"
"5o_we_gEt_The_wh01e_fl@g}"

查询电信地区:202.96.0.0/16返回中间flag: “1t_depends_0n_ECS

1
2
3
4
┌──(root㉿kali)-[/home/kali/Desktop]
└─dig @ns3.dnsv2.com +subnet=202.96.0.0/16 flag1.cyberopschallenge.cn TXT
略:
flag1.cyberopschallenge.cn. 600 IN TXT "_1t_depends_0n_ECS_"

然后猜测高校域名可能也有东西,接着查询

教育网(清华大学)59.66.0.0/16:”flag{DNS_V1eW_1s_P0w3rfu1”

1
2
3
dig @ns3.dnsv2.com +subnet=59.66.0.0/16 flag1.cyberopschallenge.cn TXT

flag1.cyberopschallenge.cn. 600 IN TXT "flag{DNS_V1eW_1s_P0w3rfu1"

完整:flag{DNS_V1eW_1s_P0w3rfu1_1t_depends_0n_ECS_5o_we_gEt_The_wh01e_fl@g}

flag2:

查询返回flag2.cyberopschallenge.cn TXT

1
"Hint: Query flag2.cyberopschallenge.cn for the second flag, but it requires authorized network access (Authorized Networks: 172.32.255.0/24 and 172.33.255.255)" 

根据提示可以找到第二部分flag和提示

1
2
3
dig @ns3.dnsv2.com +subnet=172.32.255.1/24 flag2.cyberopschallenge.cn TXT

"flag{Auth0r1z3d_N3tw0rk_"

“There are two levels of trust… Only the ‘chosen one’ at 172.33.255.255 can see the complete secret. you must ask who is in charge: the highest authority”

1
dig @ns3.dnsv2.com +subnet=172.33.255.255/32 flag2.cyberopschallenge.cn TXT

从得到的记录中可以筛选出:W1th_TCP_Supp0rt}

完整flag:flag{Auth0r1z3d_N3tw0rk_W1th_TCP_Supp0rt}

网络运维小助手

img

img

img

1
flag{S@y-the-w0rd}

校园网络保卫战

img

Flag 1 (动态指令):

题目描述:

该程序启动后会尝试连接一个远程的C2(命令与控制)服务器来获取一个动态的“行动指令”,这个指令就是 Flag 1。你需要弄清楚程序是如何构建通信URL、如何进行身份验证的,并最终获得这个指令。攻击者似乎把它藏在了某个公开的代码托管平台上。

已知flag是一个指令,且藏在公开代码托管平台,第一反应就是github

进到主函数后查看1eb0函数果然发现了github的相关字样

img

审计一下这一串代码作用和函数可能的作用,分析发现这个函数是一个 从远程 URL(很可能是 GitHub)下载数据、解码并返回解析结果 的网络数据获取与处理函数

img

注意一下开头的两个函数,一个是用来获取url,一个可能就是来获取验证的token了

1880函数里,可以凑出完整的url

img

这里存着数据

img

完整url:https://api.github.com/repos/fwhong/srclog/contents/Ameroyt2dstg.txt

1A00函数里存着token,并且使用了0x42异或,解密出完整的token

img

img

1
github_pat_11BM4SPEA0******************  # Token已隐藏(三段拼接)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

并且拿到数据:

脚本拿数据

```c
#!/usr/bin/env python3
"""
解密URL和Token以获取Flag 1
"""

# 从IDA读取的加密数据
url_part1 = bytes([0x2a, 0x36, 0x36, 0x32, 0x31, 0x78, 0x6d, 0x6d, 0x23, 0x32, 0x2b, 0x6c, 0x25, 0x2b, 0x36, 0x2a, 0x37, 0x20, 0x6c, 0x21, 0x2d, 0x2f])
url_part2 = bytes([0x30, 0x27, 0x32, 0x2d, 0x31])
url_part3 = bytes([0x24, 0x35, 0x2a, 0x2d, 0x2c, 0x25, 0x6d, 0x31, 0x30, 0x21, 0x2e, 0x2d, 0x25, 0x6d, 0x21, 0x2d, 0x2c, 0x36, 0x27, 0x2c, 0x36, 0x31])
url_part4 = bytes([0x6d, 0x3, 0x2f, 0x27, 0x30, 0x2d, 0x3b, 0x36, 0x70, 0x26, 0x31, 0x36, 0x25, 0x6c, 0x36, 0x3a, 0x36])

# Token数据
token_data = bytes([0x25, 0x2b, 0x36, 0x2a, 0x37, 0x20, 0x1d, 0x32, 0x23, 0x36, 0x1d, 0x73, 0x73, 0x0, 0xf, 0x76,
0x11, 0x12, 0x7, 0x3, 0x72, 0x9, 0x2c, 0x15, 0xe, 0x3b, 0x7b, 0x2e, 0x20, 0x5, 0xc, 0x70,
0x14, 0x1d, 0x7a, 0x25, 0x3b, 0x16, 0x29, 0x28, 0x3b, 0x6, 0x29, 0x2a, 0xa, 0x11, 0x10, 0x13,
0x77, 0x32, 0x29, 0x4, 0x2b, 0xe, 0x32, 0x74, 0x32, 0x26, 0x14, 0x33, 0x70, 0x1b, 0x13, 0x12,
0x75, 0x20, 0x11, 0x9, 0x2c, 0x72, 0x37, 0x26, 0x15, 0x30, 0x10, 0x2c, 0x5, 0x7, 0x4, 0x7,
0x8, 0x0, 0x17, 0x6, 0x4, 0x2c, 0x33, 0x29, 0x4, 0x73, 0xb, 0xc, 0xd])

# XOR密钥
XOR_KEY = 0x42

def decrypt_xor(data, stop_at_null=True):
"""使用0x42异或解密数据"""
result = bytearray()
for byte in data:
decrypted = byte ^ XOR_KEY
if stop_at_null and decrypted == 0: # 解密后遇到0停止
break
result.append(decrypted)
return result.decode('ascii', errors='ignore')

# 解密URL各部分
part1 = decrypt_xor(url_part1)
part2 = decrypt_xor(url_part2)
part3 = decrypt_xor(url_part3)
part4 = decrypt_xor(url_part4)

# 构建完整URL
full_url = f"{part1}/{part2}/{part3}{part4}"

print("=" * 70)
print("URL解密结果:")
print("=" * 70)
print(f"Part 1: {part1}")
print(f"Part 2: {part2}")
print(f"Part 3: {part3}")
print(f"Part 4: {part4}")
print(f"\n完整URL: {full_url}")

# 解密Token
token = decrypt_xor(token_data)
print("\n" + "=" * 70)
print("Token解密结果:")
print("=" * 70)
print(f"Token: {token}")

print("\n" + "=" * 70)
print("获取Flag 1:")
print("=" * 70)
print(f"访问URL: {full_url}")
print(f"使用Authorization: Bearer {token}")
print(f"Accept: application/vnd.github.v3.raw")

# 尝试访问URL获取Flag 1
import requests

headers = {
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github.v3.raw',
'User-Agent': 'CTF-Client'
}

print("\n尝试访问GitHub获取Flag 1...")
try:
response = requests.get(full_url, headers=headers, timeout=10)
if response.status_code == 200:
print(f"\n成功获取数据!")
# 将响应内容保存为二进制
flag1_data = response.content
print(f"响应长度: {len(flag1_data)} 字节")
print(f"响应内容 (hex): {flag1_data.hex()}")

# 尝试作为文本显示
try:
print(f"响应内容 (text): {response.text}")
except:
print("无法作为文本显示")

# 保存到文件
with open('flag1_data.bin', 'wb') as f:
f.write(flag1_data)
print("\n数据已保存到 flag1_data.bin")

else:
print(f"请求失败,状态码: {response.status_code}")
print(f"响应: {response.text}")
except Exception as e:
print(f"请求出错: {e}")
import traceback
traceback.print_exc()

img

1
245919c3bdc2b6c29d27431dc3a8c2bec5a01d581dc385c3bac28d7b5649c2a9c2acc39d265019c3bec2afc5a0275305

往下翻函数到sub_401D30可以发现,吧这个数据进行了两次编码使用MultiByteToWideChar(CP_UTF8)和WideCharToMultiByte(CP1252)

img

编码完后会进入check函数,这里用key来循环异或

1
0x42,0x35,0x78,0x9A,0xCD,0xEF

img

解密就是解两次码加异或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env python3

# 从GitHub获取的数据(hex格式)
flag1_data_hex = "245919c3bdc2b6c29d27431dc3a8c2bec5a01d581dc385c3bac28d7b5649c2a9c2acc39d265019c3bec2afc5a0275305"

# XOR密钥(从IDA读取的 byte_40A14C)
xor_key = bytes([0x42, 0x35, 0x78, 0x9a, 0xcd, 0xef])

print("=" * 70)
print("Flag 1 解密分析")
print("=" * 70)

# 将hex转换为bytes
flag1_data = bytes.fromhex(flag1_data_hex)
print(f"从GitHub获取的数据长度: {len(flag1_data)} 字节")
print(f"原始数据 (hex): {flag1_data.hex()}")
print(f"原始数据 (bytes): {list(flag1_data)}")
print(f"XOR密钥: {xor_key.hex()}")

# 尝试使用ctypes在Windows上进行正确的转换
import sys
if sys.platform == 'win32':
print("检测到Windows系统,使用ctypes进行编码转换...")
import ctypes

# MultiByteToWideChar: UTF-8 to Unicode
utf8_bytes = flag1_data
wide_size = ctypes.windll.kernel32.MultiByteToWideChar(
65001, # CP_UTF8
0,
utf8_bytes,
len(utf8_bytes),
None,
0
)
print(f"Unicode字符数: {wide_size}")

wide_buffer = ctypes.create_unicode_buffer(wide_size)
ctypes.windll.kernel32.MultiByteToWideChar(
65001, # CP_UTF8
0,
utf8_bytes,
len(utf8_bytes),
wide_buffer,
wide_size
)

# WideCharToMultiByte: Unicode to CP1252
binary_size = ctypes.windll.kernel32.WideCharToMultiByte(
1252, # CP1252
0,
wide_buffer,
wide_size,
None,
0,
None,
None
)
print(f"转换后的字节数: {binary_size}")

binary_buffer = ctypes.create_string_buffer(binary_size)
ctypes.windll.kernel32.WideCharToMultiByte(
1252, # CP1252
0,
wide_buffer,
wide_size,
binary_buffer,
binary_size,
None,
None
)

binary_data = binary_buffer.raw[:binary_size]
print(f"二进制数据 (hex): {binary_data.hex()}")
print(f"二进制数据 (bytes): {list(binary_data)}")

# 与密钥循环异或
flag1 = bytearray()
for i, byte in enumerate(binary_data):
flag1.append(byte ^ xor_key[i % len(xor_key)])

print(f"\n解密后的Flag 1 (hex): {flag1.hex()}")
print(f"解密后的Flag 1: {bytes(flag1).decode('ascii', errors='replace')}")

else:
print("非Windows系统,尝试手动解析UTF-8...")
# 手动解析UTF-8,提取原始字节值
# UTF-8规则:
# - 0x00-0x7F: 单字节,直接对应
# - 0xC2-0xDF + 0x80-0xBF: 双字节,表示 U+0080 到 U+07FF

binary_data = bytearray()
i = 0
while i < len(flag1_data):
b = flag1_data[i]
if b < 0x80:
# 单字节
binary_data.append(b)
i += 1
elif b >= 0xC2 and b <= 0xDF and i + 1 < len(flag1_data):
# 双字节UTF-8
b2 = flag1_data[i + 1]
codepoint = ((b & 0x1F) << 6) | (b2 & 0x3F)
if codepoint <= 0xFF:
binary_data.append(codepoint)
i += 2
elif b >= 0xE0 and b <= 0xEF and i + 2 < len(flag1_data):
# 三字节UTF-8
b2 = flag1_data[i + 1]
b3 = flag1_data[i + 2]
codepoint = ((b & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)
if codepoint <= 0xFF:
binary_data.append(codepoint)
i += 3
else:
i += 1

print(f"手动解析的二进制数据长度: {len(binary_data)}")
print(f"二进制数据 (hex): {bytes(binary_data).hex()}")
print(f"二进制数据 (bytes): {list(binary_data)}")

# 与密钥循环异或
flag1 = bytearray()
for i, byte in enumerate(binary_data):
flag1.append(byte ^ xor_key[i % len(xor_key)])

print(f"\n解密后的Flag 1 (hex): {flag1.hex()}")
print(f"解密后的Flag 1: {bytes(flag1).decode('ascii', errors='replace')}")

print("\n" + "=" * 70)

img

flag2: (静态后门密码): 除了远程指令,程序内部还硬编码了一个用于紧急情况下激活所有后门权限的“主控密码”,它就是 Flag 2。攻击者使用了一套加密算法(包括字节替换、位旋转和多层异或)来保护它。你需要剥茧抽丝,逆向解密算法,还原出原始的密码。

题目已经提示了加密算法的具体内容,只需要找到密文和具体加密算法就可以拿到flag2

这是flag的部分,一眼输入后就一个2270函数,然后直接check,确定是加密函数了 ,提取buf2的密文

img

1
2
3
4
0x94,0x58,0xB2,0x65,0xE6,0xF2,0x42,0xAF,0x40,0xBA,0xE7,0x7C,0xA8,0x9E,0xA6,0x4A,
0xA9,0xE6,0xB5,0xE0,0x77,0x81,0x32,0x13,0xB,0xD8,0x57,0x40,0x2E,0x7D,0x9B,0x33,
0xD4,0xBB,0x16,0x9E,0xD0,0xF1,0x43,0x79,
0xCC,0x7B,0x47,0x5D

加密函数扔分析一正向加密步骤:

  1. 异或0x33: input[i] = input[i] ^ 0x33
  2. S-box替换: input[i] = sbox[input[i]]
  3. 循环右移3位: input[i] = ROR(input[i], 3)
  4. 索引异或: input[i] = input[i] ^ (i - 86)

注意这里的算法的sbox是靠算法生成的,使用SIMD指令生成,这下图就是在生成sbox的算法

img

下面就是右移,带索引异或

img

写解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env python3

encrypted_data = bytes([
0x94, 0x58, 0xb2, 0x65, 0xe6, 0xf2, 0x42, 0xaf, 0x40, 0xba, 0xe7, 0x7c,
0xa8, 0x9e, 0xa6, 0x4a, 0xa9, 0xe6, 0xb5, 0xe0, 0x77, 0x81, 0x32, 0x13,
0x0b, 0xd8, 0x57, 0x40, 0x2e, 0x7d, 0x9b, 0x33, 0xd4, 0xbb, 0x16, 0x9e,
0xd0, 0xf1, 0x43, 0x79, 0xcc, 0x7b, 0x47, 0x5d
])

print("=" * 70)
print("Flag 2 最终解密")
print("=" * 70)
print(f"加密数据长度: {len(encrypted_data)} 字节")
print(f"加密数据 (hex): {encrypted_data.hex()}")

# 读取正确的S-box和逆S-box
with open('sbox_correct.bin', 'rb') as f:
sbox = f.read()

with open('inv_sbox_correct.bin', 'rb') as f:
inv_sbox = f.read()

print(f"\nS-box前16字节: {sbox[:16].hex()}")
print(f"逆S-box前16字节: {inv_sbox[:16].hex()}")

# 逆向解密
def decrypt_flag2(encrypted):
"""
逆向加密算法解密Flag 2
加密步骤:
1. 输入 ^ 0x33
2. S-box替换
3. 循环右移3位
4. 异或 (index - 86)

逆向步骤(倒序):
4. 异或 (index - 86) [自逆]
3. 循环左移3位 [逆向右移]
2. 逆S-box替换
1. 异或 0x33 [自逆]
"""
decrypted = bytearray(len(encrypted))

# 步骤1: 逆向最后的异或操作 (index - 86)
for i in range(len(encrypted)):
decrypted[i] = encrypted[i] ^ ((i - 86) & 0xFF)

print(f"\n步骤1(逆向异或index-86): {decrypted.hex()}")

# 步骤2: 逆向循环右移3位(即循环左移3位)
for i in range(len(decrypted)):
byte = decrypted[i]
decrypted[i] = ((byte << 3) | (byte >> 5)) & 0xFF

print(f"步骤2(循环左移3位): {decrypted.hex()}")

# 步骤3: 逆向S-box替换
for i in range(len(decrypted)):
decrypted[i] = inv_sbox[decrypted[i]]

print(f"步骤3(逆S-box替换): {decrypted.hex()}")

# 步骤4: 逆向第一次异或0x33
for i in range(len(decrypted)):
decrypted[i] ^= 0x33

print(f"步骤4(异或0x33): {decrypted.hex()}")

return bytes(decrypted)

# 解密
flag2 = decrypt_flag2(encrypted_data)

print("\n" + "=" * 70)
try:
flag2_str = flag2.decode('ascii')
print(f"解密后的Flag 2: {flag2_str}")
except:
print(f"解密后的Flag 2 (hex): {flag2.hex()}")
print(f"解密后的Flag 2 (尝试解码): {flag2.decode('ascii', errors='replace')}")
print("=" * 70)

# flag{static_analysis_ftw_9e5d2c4a87cafebabe}