最近这一段时间一直被科研和毕设上的事拖着,因此就有段时间没有做Misc题了
趁着写毕设没啥思路和动力的功夫,来稍微复盘下今年西湖论剑的题

|
题目附件下载:https://pan.baidu.com/s/1vXVrxFeokGB4ETv9Bs7TAQ?pwd=nxaj 提取码: nxaj |
题目名称 easydatalog
题面信息如下:
请你对附件中的日志文件进行分析,找出“张三”的身份证号和手机号,譬如其身份证号是119795199308186673,手机号是73628276413,则提交的flag为“119795199308186673_73628276413”。
题目附件给了 access.log 和 error.log 两个日志文件
简单翻看后,我们重点关注error.log,发现传了一个PHP的Webshell

继续翻看日志文件,可以发现出题人传了一个压缩包还有一个JPG图片


由于是分段传输,因此我们手动删除每段间的无效数据,把这两个文件提取出来

发现压缩包是加密的,里面有个data.csv,因此猜测需要从那张JPG图片中得到解压密码
经过尝试,发现JPG图片是盲水印隐写,直接用工具提取即可


使用得到的密码解压压缩包即可得到张三的信息,即flag:DASCTF{30601319731003117X_79159498824}
题目名称 糟糕的磁盘
题目附件给了五个img文件,查看后发现是Linux下RAID格式的磁盘文件


压缩包注释中也给了提示:I only remember the block size is 512KB
方法一:(UFS直接重组)
软件下载链接:UFS Explorer Professional Recovery 9.18
直接用UFS打开五个img文件即可,软件会自动重组,然后点击最下面那个kali

发现里面有一张key.png
和一个secret
文件,其中secret
文件大小为10MB
因此猜测是VC创建的加密镜像,密钥就是key.png
尝试使用key.png
作为密钥挂载,挂载成功后得到flag.txt


打开即可得到flag:DASCTF{R41D_4ND_D15K_M4573R}

方法二:在Ubuntu下用losetup和mdadm进行组装挂载
在WSL2(Ubuntu20.04)中运行一下命令即可成功挂载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
sudo losetup -fP m8X4exzG.img # 绑定第一个镜像文件到一个空闲的 loop 设备,并解析其分区表
sudo losetup -fP Fsiq6lKn.img
sudo losetup -fP gSoNiXLC.img
sudo losetup -fP suPVGqm6.img
sudo losetup -fP uGZ85OzT.img
sudo losetup -a # 查看当前所有已绑定的 loop 设备及其对应的文件
cat /proc/mdstat # 查看当前系统中已组装的 RAID 设备状态
sudo mount /dev/md127 tmp # 挂载 RAID 设备 md127 到 tmp 目录,尝试访问其内容
ls -al tmp
sudo umount tmp # 卸载 tmp 目录,释放 md127 设备的挂载
sudo mdadm --stop /dev/md127 # 停止 RAID 设备 md127,释放相关 loop 设备的占用
sudo losetup -d /dev/loop0 # 解除 loop0 设备的绑定
sudo losetup -d /dev/loop1
sudo losetup -d /dev/loop2
sudo losetup -d /dev/loop3
sudo losetup -d /dev/loop4
# 检查 /dev/loop0 至 /dev/loop4 上的每个设备的 RAID 元数据(查看设备是否是 RAID 阵列的一部分)
sudo mdadm --examine /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
# 创建一个名为 /dev/md127 的 RAID 0 阵列,包含 5 个设备(/dev/loop0 至 /dev/loop4)
sudo mdadm --create /dev/md127 --level=0 --raid-devices=5 /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4
|
Tips:这里我的WSL2可能启用了 mdadm
的 自动检测和组装,因此会在发现 RAID
元数据时自动创建 /dev/md127

但是当我尝试在kali上执行以上操作的时候,它不会自动识别 RAID
阵列并自动挂载

因此还是建议在Ubuntu系统下进行以上操作
题目名称 DSASignatureData
题面信息如下:
请你对附件中的流量文件进行分析,在该流量里有一些个人信息数据。附件中还有一份个人信息的签名数据 data-sign.csv(其中签名算法采用 DSA,哈希算法采用 SHA256)和一组公钥文件(位于 public 文件夹中,文件名格式为 public-XXXX.pem,其中 XXXX 为 userid 左侧补零至四位数,即个人用户对应的公钥文件)。由于数据可能在传输过程中被篡改过,因此需要你进行签名验证,验证数据是否被篡改。找出被篡改过的个人信息数据并保存到新的 csv 文件中(文件编码 utf-8,文件格式和 data.csv 保持一致),并将该文件上传至该题的校验平台(在该校验平台里可以下载该题的示例文件 example.csv,可作为该题的格式参考),校验达标即可拿到 flag。
题面名称 easyrawencode
题目名称 cscs
题目附件给了一个流量包,稍微翻看了一下发现主要是HTTP流量
然后结合题目的名称已经流量包中的特征submit.php
以及心跳包,可以知道是CobalStrike流量分析

但是这题和之前遇见的常规的CS流量直接给java序列化后的密钥对文件不同,这题它给了我们 Beacon
在 Cobalt Strike 中,Beacon 是一种后门 payload,旨在提供持续的访问控制和通信通道。它被植入目标系统后,能够通过多种隐蔽的通信方式(如 HTTP/HTTPS)与攻击者的控制服务器进行定期的交互,执行命令、获取敏感信息并保持持久化。这使得 Beacon 成为渗透测试和红队操作中用于维持长期控制、获取系统信息和横向移动的重要工具。

然后去网上找相关的文章,可以找到下面这些文章:
https://www.freebuf.com/articles/system/327060.html
https://www.freebuf.com/articles/network/407982.html

从文章中我们可以知道 mB9u
其实是上图第三步中Beacon随机生成的URL地址
并且结合文章中中的计算方法 (ord('m')+ord('B')+ord('9')+ord('u')) % 256 = 93
可以找到受害者的主机是64位的(上面结果如果是92对应32位,93则对应64位)
然后我们跟着参考文章中的,使用 1768.py 去解析题目中给我们的 Beacon 文件

我们可以在解析结果中得到公钥,用CyberChef转换一下,并补上PEM的开头和结尾

1
2
3
|
-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgFJeF4Hy8C0TKngYptJput2/OTUsjSApDsIpT75Nd+ZUnvR2bYsOFiAACt+9ev+ZzXLwViPrDe8gImXPYx3YlazV6YHahCTAOilYlcgZSjFkHy7s1ahxXKic2/lDPF1DdTh2dmbDvbD4YpVVN1tXT+QIqUroL5KWAIXUFjdPFlSzAgMBAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
-----END PUBLIC KEY-----
|
然后我们手动解析一下公钥,并尝试分解一下n

发现yafu
可以分解 n 得到 p 和 q

我们使用rsatool.py
结合得到的 p 和 q 生成一下PEM格式的私钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgFJeF4Hy8C0TKngYptJput2/OTUsjSApDsIpT75Nd+ZUnvR2bYsO
FiAACt+9ev+ZzXLwViPrDe8gImXPYx3YlazV6YHahCTAOilYlcgZSjFkHy7s1ahx
XKic2/lDPF1DdTh2dmbDvbD4YpVVN1tXT+QIqUroL5KWAIXUFjdPFlSzAgMBAAEC
gYApWVrrvY2c0zZKu/VjQ/ivQUPy0b63GmVyS1Lg8frzAiAaESnE2Pl6bwsGbxTE
I+3jeYuE1IdWOAeMnKPhY80fOSgws6vSri7CcxnMUEEn3AMw4YSwBIaBGkdLnfxf
pbS/kUUb/z7/A1SRtNq1n4hZYinnG2NpUuiO1WqwHqOGoQJBAJE14+VVt8ONGIZ1
qIf4cqAnAmtonPhyDNdYZQC0IlxNzyixo/lnlTc80b3jYUA4w8GGQQZea70op4RS
fIJV5J8CQQCRNePlVbfDjRiGdaiH+HKgJwJraJz4cgzXWGUAtCJcTc8osaP5Z5U3
PNG942FAOMPBhkEGXmu9KKeEUnyCVeNtAkAhlDeuCcNj6hXYyg592tsO49ZwZhGe
dik4Bw3cOsuTUr7r5yBHBUgBLQRHh/QuOLIz50rUITOC24rZU4XNUfV7AkB6vJQu
Ke+zaDVMoXKbyxIH8DEJXFkhXjUgZ+SnXZqVbmclPFEe48Cp+cxGtkRjJhfAIZwg
p/pk3lIJdDctay9ZAkBukZv1vD/LR3Y64R5xkoLIliyCTtHgUCY44xkJvQfCGchn
xSu0tBnGgSI3El1K1eOyT6NKSZGeQUGlLGcsBtcT
-----END RSA PRIVATE KEY-----
|
然后后续的步骤就和常规的CobalStrike流量分析一样了
首先使用私钥去解密心跳包中的cookie信息得到AES_KEY
和HMAC_KEY
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
|
import base64
import hashlib
import hexdump
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
privateKey = '''-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgFJeF4Hy8C0TKngYptJput2/OTUsjSApDsIpT75Nd+ZUnvR2bYsO
FiAACt+9ev+ZzXLwViPrDe8gImXPYx3YlazV6YHahCTAOilYlcgZSjFkHy7s1ahx
XKic2/lDPF1DdTh2dmbDvbD4YpVVN1tXT+QIqUroL5KWAIXUFjdPFlSzAgMBAAEC
gYApWVrrvY2c0zZKu/VjQ/ivQUPy0b63GmVyS1Lg8frzAiAaESnE2Pl6bwsGbxTE
I+3jeYuE1IdWOAeMnKPhY80fOSgws6vSri7CcxnMUEEn3AMw4YSwBIaBGkdLnfxf
pbS/kUUb/z7/A1SRtNq1n4hZYinnG2NpUuiO1WqwHqOGoQJBAJE14+VVt8ONGIZ1
qIf4cqAnAmtonPhyDNdYZQC0IlxNzyixo/lnlTc80b3jYUA4w8GGQQZea70op4RS
fIJV5J8CQQCRNePlVbfDjRiGdaiH+HKgJwJraJz4cgzXWGUAtCJcTc8osaP5Z5U3
PNG942FAOMPBhkEGXmu9KKeEUnyCVeNtAkAhlDeuCcNj6hXYyg592tsO49ZwZhGe
dik4Bw3cOsuTUr7r5yBHBUgBLQRHh/QuOLIz50rUITOC24rZU4XNUfV7AkB6vJQu
Ke+zaDVMoXKbyxIH8DEJXFkhXjUgZ+SnXZqVbmclPFEe48Cp+cxGtkRjJhfAIZwg
p/pk3lIJdDctay9ZAkBukZv1vD/LR3Y64R5xkoLIliyCTtHgUCY44xkJvQfCGchn
xSu0tBnGgSI3El1K1eOyT6NKSZGeQUGlLGcsBtcT
-----END RSA PRIVATE KEY-----'''
publicKey = '''-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgFJeF4Hy8C0TKngYptJput2/OTUsjSApDsIpT75N
d+ZUnvR2bYsOFiAACt+9ev+ZzXLwViPrDe8gImXPYx3YlazV6YHahCTAOilYlcgZSjFkHy7s1ahx
XKic2/lDPF1DdTh2dmbDvbD4YpVVN1tXT+QIqUroL5KWAIXUFjdPFlSzAgMBAAE=
-----END PUBLIC KEY-----'''
def get_AES_HMAC_key(PRIVATE_KEY,encode_data):
# 提取协商密钥和主机信息
private_key = RSA.import_key(PRIVATE_KEY.encode())
cipher = PKCS1_v1_5.new(private_key)
ciphertext = cipher.decrypt(base64.b64decode(encode_data), 0)
if ciphertext[0:4] == b'\x00\x00\xBE\xEF':
raw_aes_keys = ciphertext[8:24]
raw_aes_hash256 = hashlib.sha256(raw_aes_keys).digest()
aes_key = raw_aes_hash256[0:16]
hmac_key = raw_aes_hash256[16:]
hexdump.hexdump(ciphertext) # 主机信息
return aes_key.hex(),hmac_key.hex()
if __name__ == "__main__":
# 协商密钥和主机信息用RSA公钥加密之后放在了心跳包的cookie中
cookie_data = "SLHAIOj8/1icVtP6fImtJz6B6wR0t/XwLg1G0Y3AxoxnseBfPONxoyjAWCCOH84IJULnCZZrO7cIRxJPS2PtmDD4MvD8/PIpoW8Gj8536vhwd+tyXjNKyLNyNYcj+JgO4N5FTnKtkONgv7KnsMjJC3E0eI0ctqmZll8SrXLUS9k="
SHARED_KEY,HMAC_KEY = get_AES_HMAC_key(privateKey,cookie_data)
print(f"AES key: {SHARED_KEY}")
print(f"HMAC key: {HMAC_KEY}")
# 00000000: 00 00 BE EF 00 00 00 5D 28 AB 95 1F C9 6B CB 93 .......](....k..
# 00000010: EC 13 CF 9D D5 F2 13 73 A8 03 A8 03 43 50 DF EC .......s....CP..
# 00000020: 00 00 0B 50 00 00 0E 06 01 1D B0 00 00 00 00 77 ...P...........w
# 00000030: 02 04 D0 77 02 34 70 8C B8 A8 C0 57 49 4E 2D 52 ...w.4p....WIN-R
# 00000040: 52 49 39 54 39 53 4E 38 35 44 09 41 64 6D 69 6E RI9T9SN85D.Admin
# 00000050: 69 73 74 72 61 74 6F 72 09 61 72 74 69 66 61 63 istrator.artifac
# 00000060: 74 2E 65 78 65 t.exe
# AES key: 9fe14473479a283821241e2af78017e8
# HMAC key: 1e3d54f1b9f0e106773a59b7c379a89d
|
然后使用得到到AES_KEY和HMAC_KEY去解密CS传输的数据
Tips:
/cm:心跳包主要用于C2服务端要求客户端执行命令
/submit.php:主要用于传输客户端回传的执行结果
解密心跳包中传输的数据
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
|
import hmac
import binascii
import base64
import hexdump
from Crypto.Cipher import AES
def decrypt(encrypted_data, iv_bytes, signature, shared_key, hmac_key):
if hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[:16] != signature:
print("message authentication failed")
return
cipher = AES.new(shared_key, AES.MODE_CBC, iv_bytes)
return cipher.decrypt(encrypted_data)
if __name__ == "__main__":
SHARED_KEY = binascii.unhexlify("9fe14473479a283821241e2af78017e8")
HMAC_KEY = binascii.unhexlify("1e3d54f1b9f0e106773a59b7c379a89d")
with open('data.txt','r') as f:
data = f.read()
encrypt_data = bytes.fromhex(data)
encrypt_data_length = len(encrypt_data)
print(f"[+] 数据总长度为:{encrypt_data_length}")
# encrypt_data_length = int.from_bytes(encrypt_data[:4], byteorder='big', signed=False)
data = encrypt_data[:encrypt_data_length-16]
signature = encrypt_data[encrypt_data_length-16:]
iv_bytes = b"abcdefghijklmnop"
dec = decrypt(data, iv_bytes, signature, SHARED_KEY, HMAC_KEY)
print(f"{'='*80}")
print("[+] counter: {}".format(int.from_bytes(dec[:4], byteorder='big', signed=False)))
print("[+] 任务返回长度: {}".format(int.from_bytes(dec[4:8], byteorder='big', signed=False)))
print("[+] 任务输出类型: {}".format(int.from_bytes(dec[8:12], byteorder='big', signed=False)))
pcapng_data = dec[64:-60]
with open("out.pcapng",'wb') as f:
f.write(pcapng_data)
print(hexdump.hexdump(pcapng_data))
# output = dec[12:int.from_bytes(dec[4:8], byteorder='big', signed=False)].decode('gbk',errors='ignore')
# print(output)
|
解密submit.php中回传的数据
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
|
import hmac
import binascii
import base64
import hexdump
from Crypto.Cipher import AES
def decrypt(encrypted_data, iv_bytes, signature, shared_key, hmac_key):
if hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[:16] != signature:
print("message authentication failed")
return
cipher = AES.new(shared_key, AES.MODE_CBC, iv_bytes)
return cipher.decrypt(encrypted_data)
if __name__ == "__main__":
SHARED_KEY = binascii.unhexlify("9fe14473479a283821241e2af78017e8")
HMAC_KEY = binascii.unhexlify("1e3d54f1b9f0e106773a59b7c379a89d")
encrypt_datas = ["00000040efeda3e57f7d7fd589d11640ea0f9a4fe6bc91332723ffc5f43f78b37c21cc7485c44d6c8eb6af74fc7044046059c76519e493e351c9f631d6785d5c07eae9e3","000001602a99f7cc51face35199e8b1a4a5616e0301591b6f1f48b1d000149cb83d6a81e9659849a52c4f50a8629b0dfb7c036df406b44d449e40fe18df3594721e1f5849662271c1ea18b18c8eb58af5ee2c3a784852dd1c4a5c699f9518d2e2fc70d756cd68361ac794eed4eae6b062be6c31651caf93954f2a89b10e25b1fd9757ec17ee8b97038c4babb73c4f21688f5d235797844c2c9c288fac3fd2bd9cf5373956389b7e5232e35b6f268f9d67ba54f3e7e1606d4cb4020d5f480c6e5f4409b8d87e0443ae0bcfe93d286291ba6bfd0c7f37593581d90bb4ab7cfb065b4421a727f120fb491c2dc01797e38996dfc123fb120c5ed312577cc917d8a435b73c25b6d29ef0bad595100256c9aa5571e5c0ce0a8ea2c173ca1fae577fa924506b75b86522052f019d6843d74dc6fbdf2219b77e020a049c4e77df3658c80bcb703f8f878ff2f70c5c69d0cf6f4efb5a755ba854dfa5777a23989286770da6e0444d0","000000d0c72ef8b74a7d8acc332695b62448280f9a4eaa12457de4adcad279b0563f2d4cb0707f7e2853c45acf28a365d905cf8ca421d557bd7655cbd50aafbdbe5f3f570c9c3d876d0c21b661ca5c46e09f987f7e1263f6d33c34db28a2fd342fe48e5801d1a97fb88e00f0c648ec889f6b72d71edd2eed5affd32bc8d51e27fcc148d16823c1bc235b0e16d9d477bd0b4582941db373e171cce78b10c869eb987baf3fd9f879b236be6f3af43b7742f6241dfe02ab696c96f1779d0003d6b2720d1c93890e75fcce939f1c8e0922ce5044bc3a","00000100f24f15cd6f33c36e70ca228d10babfac1cf6bfbb9b6923a7828c9ed30b76d3ce1cb3d8f97c358bf90004e771ac646b1b996fd248ac8f0b460e0a36950dffcde04f3bae831982b528393f3a3c771310ba0c0bb7418ba5e8734a6bd37bc8a51cc0683c0904e0f404180e4c4c34720a3e5d6767c435f1746e6b93a13a2ecdc8074089e684b90748fc1a7e24e66bd637673437d9e24a37ce6f584b478e2f0485f3c05414dd4c35eb9ecfed8d4fbdab54db4233258f4fea6ed515a1030feeb184db94a4841236b491d2f7379e10f52d50ae573cd6f4504aa9750da273fa65c2a9eaf9b9bb014cafc53a9e9f0042bfcd5d24fc1b29173fd3308ff08d30b2a7d42132d4"]
for encrypt_data in encrypt_datas:
# encrypt_data = base64.b64decode(encrypt_data)
encrypt_data = bytes.fromhex(encrypt_data)
encrypt_data_length = int.from_bytes(encrypt_data[:4], byteorder='big', signed=False)
encrypt_data_l = encrypt_data[4:]
data1 = encrypt_data_l[:encrypt_data_length-16]
signature = encrypt_data_l[encrypt_data_length-16:]
iv_bytes = b"abcdefghijklmnop"
dec = decrypt(data1, iv_bytes, signature, SHARED_KEY, HMAC_KEY)
print(f"{'='*80}")
print("[+] counter: {}".format(int.from_bytes(dec[:4], byteorder='big', signed=False)))
print("[+] 任务返回长度: {}".format(int.from_bytes(dec[4:8], byteorder='big', signed=False)))
print("[+] 任务输出类型: {}".format(int.from_bytes(dec[8:12], byteorder='big', signed=False)))
output = dec[12:int.from_bytes(dec[4:8], byteorder='big', signed=False)].decode('gbk',errors='ignore')
print(hexdump.hexdump(dec))
print(output)
|
上面两个代码的主要区别就在于:第二个代码中的加密数据是从源数据的第四字节开始的
我们用第二个代码解密submit.php
的数据,可以得到如下内容,并且发现里面有个secret.pcapng
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
|
================================================================================
[+] counter: 2
[+] 任务返回长度: 35
[+] 任务输出类型: 30
00000000: 00 00 00 02 00 00 00 23 00 00 00 1E 77 69 6E 2D .......#....win-
00000010: 72 72 69 39 74 39 73 6E 38 35 64 5C 61 64 6D 69 rri9t9sn85d\admi
00000020: 6E 69 73 74 72 61 74 6F 72 0D 0A 00 5C 00 44 00 nistrator...\.D.
None
win-rri9t9sn85d\adminis
================================================================================
[+] counter: 3
[+] 任务返回长度: 324
[+] 任务输出类型: 30
00000000: 00 00 00 03 00 00 01 44 00 00 00 1E 20 C7 FD B6 .......D.... ...
00000010: AF C6 F7 20 43 20 D6 D0 B5 C4 BE ED C3 BB D3 D0 ... C ..........
00000020: B1 EA C7 A9 A1 A3 0D 0A 20 BE ED B5 C4 D0 F2 C1 ........ .......
00000030: D0 BA C5 CA C7 20 31 38 37 30 2D 33 35 35 33 0D ..... 1870-3553.
00000040: 0A 0D 0A 20 43 3A 5C 55 73 65 72 73 5C 41 64 6D ... C:\Users\Adm
00000050: 69 6E 69 73 74 72 61 74 6F 72 5C 44 65 73 6B 74 inistrator\Deskt
00000060: 6F 70 20 B5 C4 C4 BF C2 BC 0D 0A 0D 0A 32 30 32 op ..........202
00000070: 34 2F 31 32 2F 31 36 20 20 31 35 3A 30 35 20 20 4/12/16 15:05
00000080: 20 20 3C 44 49 52 3E 20 20 20 20 20 20 20 20 20 <DIR>
00000090: 20 2E 0D 0A 32 30 32 34 2F 31 32 2F 31 36 20 20 ...2024/12/16
000000A0: 31 35 3A 30 35 20 20 20 20 3C 44 49 52 3E 20 20 15:05 <DIR>
000000B0: 20 20 20 20 20 20 20 20 2E 2E 0D 0A 32 30 32 34 ....2024
000000C0: 2F 31 32 2F 31 36 20 20 31 35 3A 30 35 20 20 20 /12/16 15:05
000000D0: 20 20 20 20 20 20 20 20 20 31 37 2C 39 32 30 20 17,920
000000E0: 61 72 74 69 66 61 63 74 2E 65 78 65 0D 0A 20 20 artifact.exe..
000000F0: 20 20 20 20 20 20 20 20 20 20 20 20 20 31 20 B8 1 .
00000100: F6 CE C4 BC FE 20 20 20 20 20 20 20 20 20 31 37 ..... 17
00000110: 2C 39 32 30 20 D7 D6 BD DA 0D 0A 20 20 20 20 20 ,920 ......
00000120: 20 20 20 20 20 20 20 20 20 20 32 20 B8 F6 C4 BF 2 ....
00000130: C2 BC 20 35 31 2C 31 36 36 2C 36 30 31 2C 32 31 .. 51,166,601,21
00000140: 36 20 BF C9 D3 C3 D7 D6 BD DA 0D 0A 00 00 00 00 6 ..............
None
驱动器 C 中的卷没有标签。
卷的序列号是 1870-3553
C:\Users\Administrator\Desktop 的目录
2024/12/16 15:05 <DIR> .
2024/12/16 15:05 <DIR> ..
2024/12/16 15:05 17,920 artifact.exe
1 个文件 17,920 字节
2 个目录 51,166,601,216 可
================================================================================
[+] counter: 4
[+] 任务返回长度: 173
[+] 任务输出类型: 22
00000000: 00 00 00 04 00 00 00 AD 00 00 00 16 00 00 00 01 ................
00000010: 43 3A 5C 55 73 65 72 73 5C 41 64 6D 69 6E 69 73 C:\Users\Adminis
00000020: 74 72 61 74 6F 72 5C 44 65 73 6B 74 6F 70 5C 2A trator\Desktop\*
00000030: 0A 44 09 30 09 31 32 2F 31 36 2F 32 30 32 34 20 .D.0.12/16/2024
00000040: 31 35 3A 30 35 3A 34 32 09 2E 0A 44 09 30 09 31 15:05:42...D.0.1
00000050: 32 2F 31 36 2F 32 30 32 34 20 31 35 3A 30 35 3A 2/16/2024 15:05:
00000060: 34 32 09 2E 2E 0A 46 09 31 37 39 32 30 09 31 32 42....F.17920.12
00000070: 2F 31 36 2F 32 30 32 34 20 31 35 3A 30 35 3A 33 /16/2024 15:05:3
00000080: 30 09 61 72 74 69 66 61 63 74 2E 65 78 65 0A 46 0.artifact.exe.F
00000090: 09 32 38 32 09 30 38 2F 32 33 2F 32 30 31 37 20 .282.08/23/2017
000000A0: 31 34 3A 31 38 3A 33 35 09 64 65 73 6B 74 6F 70 14:18:35.desktop
000000B0: 2E 69 6E 69 0A 00 00 00 00 00 00 00 00 00 00 00 .ini............
None
C:\Users\Administrator\Desktop\*
D 0 12/16/2024 15:05:42 .
D 0 12/16/2024 15:05:42 ..
F 17920 12/16/2024 15:05:30 artifact.exe
F 282 08/23/2017 14:18:35 desk
================================================================================
[+] counter: 5
[+] 任务返回长度: 216
[+] 任务输出类型: 22
00000000: 00 00 00 05 00 00 00 D8 00 00 00 16 00 00 00 02 ................
00000010: 43 3A 5C 55 73 65 72 73 5C 41 64 6D 69 6E 69 73 C:\Users\Adminis
00000020: 74 72 61 74 6F 72 5C 44 65 73 6B 74 6F 70 5C 2A trator\Desktop\*
00000030: 0A 44 09 30 09 31 32 2F 31 36 2F 32 30 32 34 20 .D.0.12/16/2024
00000040: 31 35 3A 30 38 3A 33 31 09 2E 0A 44 09 30 09 31 15:08:31...D.0.1
00000050: 32 2F 31 36 2F 32 30 32 34 20 31 35 3A 30 38 3A 2/16/2024 15:08:
00000060: 33 31 09 2E 2E 0A 46 09 31 37 39 32 30 09 31 32 31....F.17920.12
00000070: 2F 31 36 2F 32 30 32 34 20 31 35 3A 30 35 3A 33 /16/2024 15:05:3
00000080: 30 09 61 72 74 69 66 61 63 74 2E 65 78 65 0A 46 0.artifact.exe.F
00000090: 09 32 38 32 09 30 38 2F 32 33 2F 32 30 31 37 20 .282.08/23/2017
000000A0: 31 34 3A 31 38 3A 33 35 09 64 65 73 6B 74 6F 70 14:18:35.desktop
000000B0: 2E 69 6E 69 0A 46 09 33 34 39 35 38 38 09 31 32 .ini.F.349588.12
000000C0: 2F 31 36 2F 32 30 32 34 20 31 35 3A 30 38 3A 33 /16/2024 15:08:3
000000D0: 31 09 73 65 63 72 65 74 2E 70 63 61 70 6E 67 0A 1.secret.pcapng.
000000E0: 00 00 00 00 00 00 00 00 40 6F CC FE FE 07 00 00 ........@o......
None
C:\Users\Administrator\Desktop\*
D 0 12/16/2024 15:08:31 .
D 0 12/16/2024 15:08:31 ..
F 17920 12/16/2024 15:05:30 artifact.exe
F 282 08/23/2017 14:18:35 desktop.ini
F 349588 12/16/2024 15:08:31 secret
|
我们用第一个代码解密C2服务端(10.11.4.3)
要求客户端执行命令的最大的那个数据包,可以得到一个pacpng
流量包文件
结合第二个代码解密出来的submit.php
的数据,应该就是那个secret.pcapng
了

用以下代码解其余心跳包的数据可以得到攻击者要求受害客户端执行的其他命令
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
|
import hmac
import binascii
import base64
import hexdump
from Crypto.Cipher import AES
def decrypt(encrypted_data, iv_bytes, signature, shared_key, hmac_key):
if hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[:16] != signature:
print("message authentication failed")
return
cipher = AES.new(shared_key, AES.MODE_CBC, iv_bytes)
return cipher.decrypt(encrypted_data)
if __name__ == "__main__":
SHARED_KEY = binascii.unhexlify("9fe14473479a283821241e2af78017e8")
HMAC_KEY = binascii.unhexlify("1e3d54f1b9f0e106773a59b7c379a89d")
data = ["75c02fc5b995a17bc6aa35cb218d77673c2779496c263e60ad2e72308f04c7fc45945bc005c0581f68cde2d86f427559","2861dcee3abe0beb543a42a83f765f8e8447799da043820c2fa534f47ef86e84bcb75dbab2b13772c66626d9461b64dffbd222ab8cb6098675785d68a054580a","01f629f493a28c3f2c4fe5aa660bba59d58aa1f464c1d359d6682f6f166e8c2c0a8b8639a577108a7ba285fa2314ee0b3cf150f69ad4311680e15b55994a0260","7277c5f144e04d2eaefa418ad6003a2350e8dfabb3e295b4d427679198881b6a12207b0b9d99831218db551b8469f577"]
for item in data:
encrypt_data = bytes.fromhex(item)
encrypt_data_length = len(encrypt_data)
print(f"[+] 数据总长度为:{encrypt_data_length}")
# encrypt_data_length = int.from_bytes(encrypt_data[:4], byteorder='big', signed=False)
data = encrypt_data[:encrypt_data_length-16]
signature = encrypt_data[encrypt_data_length-16:]
iv_bytes = b"abcdefghijklmnop"
dec = decrypt(data, iv_bytes, signature, SHARED_KEY, HMAC_KEY)
print(f"{'='*80}")
print("[+] counter: {}".format(int.from_bytes(dec[:4], byteorder='big', signed=False)))
print("[+] 任务返回长度: {}".format(int.from_bytes(dec[4:8], byteorder='big', signed=False)))
print("[+] 任务输出类型: {}".format(int.from_bytes(dec[8:12], byteorder='big', signed=False)))
print(hexdump.hexdump(dec))
|
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
|
[+] 数据总长度为:48
================================================================================
[+] counter: 1734332826
[+] 任务返回长度: 16
[+] 任务输出类型: 4
00000000: 67 5F D1 9A 00 00 00 10 00 00 00 04 00 00 00 08 g_..............
00000010: 00 00 00 64 00 00 00 5A 41 41 41 41 41 41 41 41 ...d...ZAAAAAAAA
None
[+] 数据总长度为:64
================================================================================
[+] counter: 1734332874
[+] 任务返回长度: 37
[+] 任务输出类型: 78
00000000: 67 5F D1 CA 00 00 00 25 00 00 00 4E 00 00 00 1D g_.....%...N....
00000010: 00 00 00 09 25 43 4F 4D 53 50 45 43 25 00 00 00 ....%COMSPEC%...
00000020: 0A 20 2F 43 20 77 68 6F 61 6D 69 00 00 41 41 41 . /C whoami..AAA
None
[+] 数据总长度为:64
================================================================================
[+] counter: 1734332876
[+] 任务返回长度: 34
[+] 任务输出类型: 78
00000000: 67 5F D1 CC 00 00 00 22 00 00 00 4E 00 00 00 1A g_....."...N....
00000010: 00 00 00 09 25 43 4F 4D 53 50 45 43 25 00 00 00 ....%COMSPEC%...
00000020: 07 20 2F 43 20 64 69 72 00 00 41 41 41 41 41 41 . /C dir..AAAAAA
None
[+] 数据总长度为:48
================================================================================
[+] counter: 1734332896
[+] 任务返回长度: 19
[+] 任务输出类型: 53
00000000: 67 5F D1 E0 00 00 00 13 00 00 00 35 00 00 00 0B g_.........5....
00000010: 00 00 00 01 00 00 00 03 2E 5C 2A 41 41 41 41 41 .........\*AAAAA
None
|
仔细观察以上数据其实可以发现攻击者一共就要求执行了whoami
和dir
这两个命令
然后我们将解密得到的流量包文件保存为secret.pcapng
,打开翻看,发现主要是UDP流量


赛后和师傅们交流后知道了是CS(反恐精英)1.6
的流量,这里的出题思路参考自 2021L3HCTF Lambda
这也呼应了题目名(CSCS),CS流量里套了一个CS(反恐精英)1.6
流量
ReHLDS 是一个 C++ 开源项目,它是 HLDS(Half-Life Dedicated Server)的一个优化版本
专门用来搭建 反恐精英1.6(CS 1.6)等基于 Half-Life 引擎的游戏服务器。
但是这道题没有2021L3HCTF那题那么复杂,因为这里的数据包带的数据都比较短
因此这里直接一个个包解码过去就行,也不用根据解密后数据的长度进行分块处理
首先把解密的CPP代码在Linux下编译成.so文件
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
|
extern "C"
{
int _LongSwap(int l)
{
unsigned int res = __builtin_bswap32(*(unsigned int *)&l);
return *(int *)&(res);
}
const unsigned char mungify_table2[] =
{
0x05, 0x61, 0x7A, 0xED,
0x1B, 0xCA, 0x0D, 0x9B,
0x4A, 0xF1, 0x64, 0xC7,
0xB5, 0x8E, 0xDF, 0xA0};
void COM_UnMunge2(unsigned char *data, int len, int seq)
{
int i;
int mungelen;
int c;
int *pc;
unsigned char *p;
int j;
mungelen = len & ~3;
mungelen /= 4;
for (i = 0; i < mungelen; i++)
{
pc = (int *)&data[i * 4];
c = *pc;
c ^= seq;
p = (unsigned char *)&c;
for (j = 0; j < 4; j++)
{
*p++ ^= (0xa5 | (j << j) | j | mungify_table2[(i + j) & 0x0f]);
}
c = _LongSwap(c);
c ^= ~seq;
*pc = c;
}
}
}
|
1
2
3
4
5
6
7
|
g++ -shared -fPIC -o a.so a.cpp
# g++:使用 C++ 编译器进行编译。
# -shared:指示编译器生成共享库(.so 文件)。
# -fPIC:生成位置无关代码(Position Independent Code),这是生成共享库所必需的。
# -o libcom_unmunge.so:指定输出文件名为 libcom_unmunge.so。
# com_unmunge.cpp:源代码文件名。
|
Linux下输入以上命令即可编译得到 a.so
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
|
import json
from ctypes import *
import subprocess
tshark_path = r"tshark"
lib=CDLL('./a.so')
COM_UnMunge=lib.COM_UnMunge2
def solve():
command = [
tshark_path,
'-r', "secret.pcapng",
'-Y', 'udp',
'-T', 'json',
'-e', 'ip.src',
'-e', 'ip.dst',
'-e', 'data.data',
]
res = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
json_out = json.loads(res.stdout)
for packet in json_out:
try:
layers = packet.get('_source', {}).get('layers', {})
ip_src = layers.get('ip.src', [None])[0]
ip_dst = layers.get('ip.dst', [None])[0]
data = layers.get('data.data', [None])[0]
if not all([ip_src, ip_dst, data]):
continue
bytes_data = bytes.fromhex(data)
enc_data = bytes_data[8:]
seq = bytes_data[:4]
buf=create_string_buffer(enc_data)
COM_UnMunge(buf,len(enc_data),seq[0])
buf = bytes(buf) # 这里要先把 C-String 类型转为 bytes 类型
print(f"[+] {ip_src} ==> {ip_dst}:\n{bytes(buf)}")
if b'DASCTF' in buf:
break
except Exception as e:
print(f"[x] {e}")
if __name__ == "__main__":
solve()
|
最后Linux下运行以上代码即可得到最后的flag:DASCTF{C0UnT3R_S7R1K3_4nD_C0BaLt_57RIK3_4LL_FUN}
