01
题目简介
02
解题步骤
启动并访问靶机

根据题目猜测,应该是需要我们打到一百万分。但是手动操作基本是不可能的,所以我们只能查看JS文件了。我们在Player.js里面发现了金币更新的方法updateCoin()

阅读代码,这段代码是更新金币数的,但是里面有两个加密了的中奖弹窗,这两个弹窗的解密逻辑是一样的!
先看到两个base64加密的数组
var _0x3f85=['MxXCgw4=','w53DhVRWw6X...ZMKF'];之后有一个解码的function,这个方法有两个输入,_0x3f85fc要看输入的是哪个数组,_0x57fdeb就是密钥了。
var _0x57fd=function(_0x3f85fc,_0x57fdeb){ ... }然后解密的逻辑是base64→UTF-8→RC4,明白逻辑之后,所以就能构建脚本了。使用AI辅助构建脚本很方便
# -*- coding: utf-8 -*-
"""
复刻浏览器解密管线:atob(base64) -> %XX 编码 -> decodeURIComponent(UTF-8) -> RC4(key) 异或
等价于:base64 解码得到字节 -> 按 UTF-8 解码成字符串 -> 标准 RC4 解密
"""
import base64
import re
_HEX = set('0123456789abcdefABCDEF')
def extract_flag(f1: str, f2: str) -> str:
"""
两段弹窗文本各含半截 flag,拼出完整 DASCTF{...}:
f1 = ...DASCTF{<前半 hex> (无闭合 })
f2 = <后半 hex> }... (无开头 DASCTF{)
做法:左半从 'DASCTF{' 起只保留 hex;右半从 '}' 往前回溯 hex;再拼回。
"""
# 左半:去掉字面前缀 'DASCTF{',再去掉其余非 hex 字符
left = f1[f1.index('DASCTF{') + len('DASCTF{'):]
hex_left = re.sub(r'[^0-9a-fA-F]', '', left)
# 右半:从最后一个 '}' 向前回溯连续的 hex 字符
end = f2.rindex('}')
start = end
while start > 0 and f2[start - 1] in _HEX:
start -= 1
hex_right = f2[start:end]
return 'DASCTF{' + hex_left + hex_right + '}'
def rc4(data: str, key: str) -> str:
"""
标准 RC4 解密(对称,加密解密同一套逻辑)。
- data: 待解密字符串(已按 UTF-8 解码后的字符串)
- key : RC4 密钥(字符串,如 'dhCd')
返回解密后的明文字符串。
"""
# 1) KSA:初始化并打乱 S 盒
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + ord(key[i % len(key)])) % 256
S[i], S[j] = S[j], S[i]
# 2) PRGA:生成密钥流并与密文逐字符异或
out = []
i = j = 0
for k in range(len(data)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
# ord(data[k]) 对应 JS 的 data.charCodeAt(k)
out.append(chr(ord(data[k]) ^ S[(S[i] + S[j]) % 256]))
return ''.join(out)
def decrypt(b64: str, key: str) -> str:
"""
解密单段密文:
b64 -> base64 密文(题目 JS 数组里的字符串)
key -> RC4 密钥(题目函数调用里的第二个参数)
"""
raw = base64.b64decode(b64) # 1) atob:base64 -> 原始字节
text = raw.decode('utf-8') # 2)+3) %XX 编码 + decodeURIComponent == 按 UTF-8 解码
return rc4(text, key) # 4) RC4 解密
# ===== 题目原始数据(来自 Player.js 的 _0x3f85 / _0x3dd2 数组)=====
# 第一段:金鲨条件
A = [
'MxXCgw4=',
'w53DhVRWw6XDjcOJCMOOw7TDq8OfwrbDlzh5TCXDpsOzw5o0wpzComjDv2zDucO/w4nDsMODwpLDk8Kpw60eK07ChgzDmGpTJcOPECZowp0Gwq0awrp9wodQw5RuwoEPw74FJ3nDg2x8IkvCuMO4w5PCjMKJZMKF'
]
# 第二段:百万富翁条件
B = [
'ZUXDiiA=',
'e8KafsKXw4XCgw3CiG/CgcKCw74rwqIow6w/UE4JwoHDi8KNw7o6wovDscOAw7nCq2nCiMKcwpMWw50QwrNLwqrCk8KJbsKuwrIPwrADLcOWZ8OCw4QKwqhpw5MgwrjDqRJqOsO2wpnCslLCoWhPw5Jkf3s='
]
if __name__ == '__main__':
print('[段1] 属性名 =', decrypt(A[0], 'HhP[')) # 期望 'coin'
print('[段1] flag前半 =', decrypt(A[1], 'dhCd'))
print('[段2] 属性名 =', decrypt(B[0], 'fhy)')) # 期望 'coin'
print('[段2] flag后半 =', decrypt(B[1], 'GK)i'))
# 两段各含半截 DASCTF{...},拼出完整 flag
flag = extract_flag(decrypt(A[1], 'dhCd'), decrypt(B[1], 'GK)i'))
print('\n==== 完整 flag ====')
print(flag)