Flag in your Hand - Writeup by AI
·
Flag in your Hand - Writeup by AI
1. 题目描述
- 题目名称: Flag in your Hand
- 题目来源: 攻防世界 (Adworld)
- 题目类型: JavaScript 逆向/密码学
题目提供了一个网页界面,用户需要输入正确的 token 来获取 flag。
2. 考点分析
| 考点 | 说明 |
|---|---|
| JavaScript 代码逆向分析 | 理解压缩混淆的 JS 代码逻辑 |
| MD5 哈希算法实现 | 识别自定义实现的 MD5 算法 |
| Base64 编码原理 | 理解 Base64 编码实现细节 |
| 字符编码与 ASCII 转换 | 逐字符验证逻辑分析 |
| 函数调用链追踪 | 从 HTML 到 JS 核心函数的调用关系 |
3. 解题思路
3.1 代码结构分析
通过阅读 index.html 和 script-min.js 两个文件,可以发现:
-
index.html 提供了输入界面和调用逻辑
function getFlag() { var token = document.getElementById("secToken").value; ic = checkToken(token); // 验证 token fg = bm(token); // 生成 flag showFlag() } -
script-min.js 包含核心函数:
checkToken(s): 表面验证函数(幌子)bm(s): 生成 flag 的函数ck(s): 在 MD5 计算过程中调用的真正验证函数rstr(s): MD5 哈希实现rb(ip): Base64 编码实现
3.2 关键函数分析
checkToken() 函数(幌子)
function checkToken(s) {
return s === "FAKE-TOKEN";
}
这个函数返回固定值,是明显的幌子,不是真正的验证逻辑。
ck() 函数(真正的验证)
function ck(s) {
try {
ic
} catch (e) {
return;
}
var a = [118, 104, 102, 120, 117, 108, 119, 124, 48,123,101,120];
if (s.length == a.length) {
for (i = 0; i < s.length; i++) {
if (a[i] - s.charCodeAt(i) != 3)
return ic = false;
}
return ic = true;
}
return ic = false;
}
这个函数在 MD5 的 binl() 函数中被调用,传入了原始输入字符串。这才是真正的验证逻辑!
验证条件:a[i] - s.charCodeAt(i) == 3
即:s.charCodeAt(i) = a[i] - 3
bm() 函数(Flag 生成)
function bm(s) {
return rb(rstr(str2rstr_utf8(s)));
}
通过跟踪函数调用链:
str2rstr_utf8(s): UTF-8 编码转换rstr(s): 实际调用binl()实现 MD5 哈希rb(ip): 使用 Base64 字符表进行 Base64 编码
结论:bm() = Base64(MD5(token))
其他辅助函数
| 函数名 | 功能 | 用途 |
|---|---|---|
rh(ip) |
十六进制编码 | 将字节转换为 hex 字符串(未使用) |
hm(s) |
MD5+Hex | 生成 MD5 的十六进制表示(未使用) |
binl(x, len) |
MD5 核心算法 | 实现完整的 MD5 压缩函数 |
rstr2binl(input) |
字符串转字节数组 | MD5 预处理 |
binl2rstr(i) |
字节数组转字符串 | MD5 后处理 |
4. 详细步骤
步骤 1: 恢复正确的 token
根据 ck() 函数中的验证逻辑:
a = [118, 104, 102, 120, 117, 108, 119, 124, 48, 123, 101, 120]
token = ''.join(chr(code - 3) for code in a)
逐字符计算:
| 索引 | a[i] | 计算 | ASCII | 字符 |
|---|---|---|---|---|
| 0 | 118 | 118-3=115 | 115 | ‘s’ |
| 1 | 104 | 104-3=101 | 101 | ‘e’ |
| 2 | 102 | 102-3=99 | 99 | ‘c’ |
| 3 | 120 | 120-3=117 | 117 | ‘u’ |
| 4 | 117 | 117-3=114 | 114 | ‘r’ |
| 5 | 108 | 108-3=105 | 105 | ‘i’ |
| 6 | 119 | 119-3=116 | 116 | ‘t’ |
| 7 | 124 | 124-3=121 | 121 | ‘y’ |
| 8 | 48 | 48-3=45 | 45 | ‘-’ |
| 9 | 123 | 123-3=120 | 120 | ‘x’ |
| 10 | 101 | 101-3=98 | 98 | ‘b’ |
| 11 | 120 | 120-3=117 | 117 | ‘u’ |
得到 token: security-xbu
步骤 2: 生成 flag
根据 bm() 函数的逻辑,需要对 token 先进行 MD5 哈希,再进行 Base64 编码:
import hashlib
import base64
md5_hash = hashlib.md5(b'security-xbu').digest()
flag = base64.b64encode(md5_hash).decode('utf-8').rstrip('=')
计算过程:
- Token:
security-xbu - Token 字节表示:
b'security-xbu' - MD5(
security-xbu) =45e9c86f277c16083985ac2f426ed30d - MD5 字节数组:
[69, 233, 200, 111, 39, 124, 22, 8, 57, 133, 172, 47, 66, 110, 211, 13] - Base64(MD5) =
RenIbyd8Fgg5hawvQm7TDQ== - 去除填充 =
RenIbyd8Fgg5hawvQm7TDQ
得到 flag: RenIbyd8Fgg5hawvQm7TDQ
步骤 3: 验证结果
通过在浏览器中打开 index.html,输入 token security-xbu,点击"Get flag!"按钮,页面显示:
- You got the flag below!!
- RenIbyd8Fgg5hawvQm7TDQ
✅ 与 Python 脚本输出完全一致!
5. 完整代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CTF 题目:Flag in your Hand - 解题脚本
来源:攻防世界 (Adworld) - Crypto
类型:JavaScript 逆向/密码学
"""
import hashlib
import base64
def recover_token(a, offset=3):
"""根据 ck() 函数的验证逻辑恢复 token"""
return ''.join(chr(code - offset) for code in a)
def generate_flag(token):
"""模拟 bm() 函数生成 flag: Base64(MD5(token))"""
md5_hash = hashlib.md5(token.encode('utf-8')).digest()
return base64.b64encode(md5_hash).decode('utf-8').rstrip('=')
def verify_token(token, a, offset=3):
"""验证 token 是否满足 ck() 函数的条件"""
if len(token) != len(a):
return False
return all(a[i] - ord(token[i]) == offset for i in range(len(a)))
def solve():
# ck() 函数中的验证数组
a = [118, 104, 102, 120, 117, 108, 119, 124, 48, 123, 101, 120]
# 恢复 token
token = recover_token(a)
# 验证 token 正确性
assert verify_token(token, a), "Token verification failed!"
print("=" * 60)
print("步骤 1: 恢复 Token")
print("=" * 60)
print(f"验证数组 a: {a}")
print(f"偏移量:3")
print(f"恢复的 token: {token}")
print(f"Token 长度:{len(token)}")
# 详细验证过程
print("\n验证过程:")
for i in range(len(a)):
char_code = ord(token[i])
diff = a[i] - char_code
print(f" a[{i:2d}]={a[i]:3d}, token[{i:2d}]='{token[i]}'(ASCII:{char_code:3d}), 差值={diff}")
print("\n" + "=" * 60)
print("步骤 2: 计算 MD5 哈希")
print("=" * 60)
md5_hash = hashlib.md5(token.encode('utf-8')).digest()
print(f"Token 字节表示:{token.encode('utf-8')}")
print(f"MD5 哈希 (hex): {md5_hash.hex()}")
print(f"MD5 哈希 (bytes): {list(md5_hash)}")
print("\n" + "=" * 60)
print("步骤 3: Base64 编码")
print("=" * 60)
flag = base64.b64encode(md5_hash).decode('utf-8').rstrip('=')
print(f"MD5 哈希字节数:{len(md5_hash)}")
print(f"Base64 编码 (带=): {base64.b64encode(md5_hash).decode('utf-8')}")
print(f"Base64 编码 (去=): {flag}")
print("\n" + "=" * 60)
print("最终结果")
print("=" * 60)
print(f"Token: {token}")
print(f"Flag: {flag}")
print("=" * 60)
return token, flag
if __name__ == "__main__":
token, flag = solve()
6. 总结
关键点
- 识别真正的验证函数:
checkToken()是幌子,隐藏在 MD5 实现中的ck()才是真正的验证逻辑 - 理解验证条件:
a[i] - s.charCodeAt(i) == 3是核心公式,可逆向推导出 token - 识别复合编码方式:
bm()函数实现的是Base64(MD5(token))的复合变换 - 函数调用链分析:
bm()→str2rstr_utf8()→rstr()→binl()→rb()
解题技巧
- 对于压缩/混淆的 JavaScript 代码,要仔细追踪函数调用链
- 注意在哈希算法中嵌入的验证逻辑这种隐蔽手法
- 识别常见的编码算法特征(如 Base64 的字符表、MD5 的初始值和运算)
- 使用 Python 快速批量计算字符转换,避免手动逐个推算
- 重要: 通过浏览器实际验证结果,确保脚本输出正确
易错点
- ❌ 误以为
checkToken()是真正的验证函数 - ❌ 误以为
bm()只是简单的 Base64 编码(实际是 MD5+Base64) - ❌ 忽略
ck()函数在binl()中被调用的隐蔽位置 - ✅ 正确识别
ck()函数的验证逻辑 - ✅ 正确分析
bm()的复合变换过程 - ✅ 通过实际运行验证结果一致性
最终答案
- 正确 Token:
security-xbu - Flag:
RenIbyd8Fgg5hawvQm7TDQ
补充说明
JavaScript 函数映射关系
getFlag() (HTML)
├─ checkToken(token) → 返回 false (幌子)
└─ bm(token)
├─ str2rstr_utf8(s) → UTF-8 编码
├─ rstr(s) → MD5 哈希
│ └─ binl(s, len) → 核心 MD5 算法
│ └─ ck(s) → 真正的验证函数 ✓
└─ rb(ip) → Base64 编码
验证数组的快速计算
# 方法 1: 列表推导式
a = [118, 104, 102, 120, 117, 108, 119, 124, 48, 123, 101, 120]
token = ''.join(chr(x - 3) for x in a) # security-xbu
# 方法 2: 手动计算每个字符
# 118-3=115→'s', 104-3=101→'e', 102-3=99→'c', ...
MD5 + Base64 组合
import hashlib
import base64
token = "security-xbu"
md5_hash = hashlib.md5(token.encode('utf-8')).digest() # 16 字节
flag = base64.b64encode(md5_hash).decode('utf-8').rstrip('=')
# 结果:RenIbyd8Fgg5hawvQm7TDQ
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)