Flare-on12

1

看到加密逻辑:

爆破一下:

1
2
3
4
5
6
encoded = "\xd0\xc7\xdf\xdb\xd4\xd0\xd4\xdc\xe3\xdb\xd1\xcd\x9f\xb5\xa7\xa7\xa0\xac\xa3\xb4\x88\xaf\xa6\xaa\xbe\xa8\xe3\xa0\xbe\xff\xb1\xbc\xb9"

for i in range(1000):
for j in range(len(encoded)):
print(chr(ord(encoded[j]) ^ (i + j)),end="")
print()

2

使用python3.12,因为python的这种marshal库不同版本的差异还是挺大的。
序列化的字节码,首先zlib解压看到base85、zlib等等字符串,中间的是base85编码

对中间的解码,得到marshal数据,然后直接解码、解压、反汇编

1
2
3
4
5
6
7
cipher = ....

code_ = (base64.b85decode(cipher))

data = zlib.decompress(code_)

dis.dis(marshal.loads(data))

得到的python字节码,交给AI反编译出代码,异或得到rc4的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
import os

LEAD_RESEARCHER_SIGNATURE = b"m\x1b@I\x1dAoe@\x07ZF[BL\rN\n\x0cS"

def rc4_init(key: bytes):
"""RC4 Key Scheduling Algorithm (KSA)"""
S = list(range(256))
j = 0
key_length = len(key)

for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i] # 交换
return S


def rc4_generate_keystream(S, data_length: int):
"""RC4 Pseudo-Random Generation Algorithm (PRGA)"""
i = 0
j = 0
keystream = []
for _ in range(data_length):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i] # 交换
K = S[(S[i] + S[j]) % 256]
keystream.append(K)
return keystream


def rc4_decrypt(key: bytes, ciphertext: bytes) -> bytes:
S = rc4_init(key)
keystream = rc4_generate_keystream(S, len(ciphertext))
return bytes([c ^ k for c, k in zip(ciphertext, keystream)])


if __name__ == "__main__":
key = b"YourUserName"
ciphertext = (
b"r2b-\r\x9e\xf2\x1fp\x185\x82\xcf\xfc\x90\x14\xf1O\xad#]\xf3\xe2"
b"\xc0L\xd0\xc1e\x0c\xea\xec\xae\x11b\xa7\x8c\xaa!\xa1\x9d\xc2\x90"
)

key = bytes((c ^ (i + 42)) for i, c in enumerate(LEAD_RESEARCHER_SIGNATURE))
plaintext = rc4_decrypt(key, ciphertext)
print(plaintext.decode(errors="ignore"))
1
Th3_Alch3m1sts_S3cr3t_F0rmul4@flare-on.com

3

首先是pdf中含有的流对象4,前两个字节并不是zlib流的标准头

从对象7看到是有加密的,但是在打开文件时,没要求输入密码,而PDF是允许空密码的,这里用到pikepdf

1
2
3
import pikepdf
with pikepdf.open('encrypted.pdf', password='') as pdf:
pdf.save('decrypted.pdf')

在解密出来的pdf中看到:

接下来zliib解压

1
2
3
4
5
6
7
8
9
import zlib
from PIL import Image
import numpy as np

data_hex = "789CC5523B4F03310CDEFD2BC2040C559CC48E9391BE682524A87A120362C8E332015299F8F9E47A6542CCD8B22DBFE2CF564ECA1BABB0B399D4A2EBF20ECBBDD2CFCA89DE29A357477DAF974F2B15F483220E7AFB02FA6EF7057ABD1AE075BF86D66A686D44448394C853247F760CE2D9CE82BD2E2392BBE4FE915B2B13A08C61C666798A1A33612CD44372C1ECF06FF2482818E60E4BF3B26831225F36FF4DD2F3DC3B836327E2B3CFD26790F342C222FD42A957859FDBB9865898AA2334B5E4DC722C2ED41C92D46A3D276B388EB146E284C1773C2EB89472653B323A2A5668643112C7E672A9AD24C686A9852EFDED31065F3CB10F659A1D61B35707057082E5005A1942353430E7DF3154B8D9BEA5CF71F1F871757B0D9B010EF00DE01C803D"
data_bytes = bytes.fromhex(data_hex)

decompressed = zlib.decompress(data_bytes)
print(decompressed)

可以看到结果就是pdf中的操作,有两个图片,一个是37x1像素的,一个是显示出来的Flare-On!,第一个很可疑。

1
2
3
4
5
6
7
8
9
10
with open('hidden.jpg', 'wb') as f:
f.write(bytes.fromhex('ffd8ffe000104a46494600010100000100010000ffdb00430001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101ffc0000b080001002501011100ffc40017000100030000000000000000000000000006040708ffc400241000000209050100000000000000000000000702050608353776b6b7030436747577ffda0008010100003f00c54d3401dcbbfb9c38db8a7dd265a2159e9d945a086407383aabd52e5034c274e57179ef3bcdfca50f0af80aff00e986c64568c7ffd9'))

from PIL import Image
import numpy as np

img = Image.open('hidden.jpg')
pixels = np.array(img).flatten()
text = ''.join(chr(p) for p in pixels)
print(text)

这里是大模型想出来的,提取灰度值(

1
Puzzl1ng-D3vilish-F0rmat@flare-on.com

真是有misc味了

4

虚拟机上直接运行,就出了:

这道题用了COM组件,也是借此机会学习了一下,COM逆向的方法:

1
https://www.cnblogs.com/QKSword/p/10927987.html

是可以拿到调用的COM的CLSIDIID,我这里发现貌似只调用了两个:stdPictureIPictureDisp

本身的行为是复制自己,在里面还发现了gif数据:

5