CTF 2024 Write up
本萌新刷推的时候无意间看见这个 CTF 2024 (opens in a new tab),虽然期末在即,但还是想玩一下。
第一次参加有关 CTF 的东西,所以写博客记录以下。
Check In
点了 Submit,在 Console 中没有发现任何网络请求,所以答案一定在本地。
在 Sources 中搜索一番,发现了可疑目标。
从正则可以看出,这是一个8位字符的字符串,且仅包含a-z A-Z 0-9
。
所以思路很简单,遍历这些可能字符的字符码进行爆破。
[['a', 'z'], ['A', 'Z'], ['0', '9']].forEach(range => {
for (let i = range[0].charCodeAt(0); i <= range[1].charCodeAt(0); i++) {
const realFlag = (0, _rust_sdk__WEBPACK_IMPORTED_MODULE_1__/* .getFlag1 */.S7)(i);
if (realFlag.length > 0) {
console.log(realFlag);
}
}
});
Hook 入代码后,点击按钮,得到了 Flag 1。
document.querySelector("body > main > div:nth-child(3) > div > button").click();
Bytecode
Bytecode
,字节码。
尝试逆向 WASM 无果后,我决定向第一题一样直接 Hook 入代码。
分析 Hook 入点
首先看 mangleBuffer
是什么,尝试 console.log(mangleBuffer)
,控制台运行:
document.querySelector("body > main > div:nth-child(4) > div > input").value = '1';
document.querySelector("body > main > div:nth-child(4) > div > button").click();
得到结果:
Uint8Array
是 JavaScript 中的一种类型化数组,用于处理二进制数据。
Uint8Array
中的 "Uint" 表示 "无符号整数",而 "8" 表示每个元素占用 8 位(即 1 字节)。因此,
Uint8Array
的每个元素都是一个无符号的 8 位整数,其取值范围是 0 到 255。
接下来看到执行部分,可以看到一共有三次执行,前两次都是针对第一位和第二位的判断,第三次才是真正的执行。
vm.loadCode
表示使用 Uint8Array 用于表示某种虚拟机(注释中标记为:HumbleVM
)可以理解并执行的代码。
这些代码可能是低级的机器码或字节码,它们直接控制虚拟机的操作。
爆破前两次执行
我们可以暴力破解前两次的运行,尝试得出规律:
for (let i = 0; i <= 256; i++) {
if (vm.run(Uint8Array.from([i]))) {
console.log(i);
}
}
控制台直接得到结果162
。
对于第二次执行,我们也使用同样的方法:
for (let i = 0; i <= 256; i++) {
if (vm.run(Uint8Array.from([162, i]))) {
console.log(i);
}
}
控制台得到结果154
。
所以接下来我们要找出输入什么值得到的 mangleBuffer
前两位为 [ 162, 154 ]
.
这里毫无技巧可言,人工暴力随机输入后得到以下条件:
- 输入的字符串为 8 位
- 第一个字符为 0
爆破第三次执行
我们针对第三次执行开始爆破。
思路:使用vm.loadCode
分次载入Uint8Array
,爆破结果。
for (let i = 0; i <= 256; i++) {
if (vm.run(Uint8Array.from([162, 154, i]))) {
console.log(i);
}
}
输入 | 输出 |
---|---|
1, 1 | 所有结果均为 true |
1, 1, 4 | malformed bytecode (expected an operand for instruction 4 ) |
1, 1, 4, 208 | 208 |
以此类推可以得到正确的 mangleBuffer
为 [162, 154, 208, 201, 155, 234, 192, 218,158]
。
爆破 Flag
得到了 mangleBuffer
,我们还需要得到 flag
。
尝试对每一位进行爆破
for (let i = 0; i <= 255; i++) {
const flag = `0${String.fromCharCode(i)}xxxxxx`
const mangledBuffer = (0, _rust_sdk__WEBPACK_IMPORTED_MODULE_1__/* .mangle */.dW)(flag);
if (mangledBuffer[3] === 208) {
console.log(String.fromCharCode(i));
}
}
得到:
Uint8 | Flag | |
---|---|---|
第二位 | 208 | z |
第三位 | 201 | c |
第四位 | 155 | 1 |
第五位 | 234 | @ |
第六位 | 192 | j |
第七位 | 218 | p |
第八位 | 158 | 4 |
所以 Flag 为 0z1@cjp4
。
总结
这是我第一次参加 CTF,感觉过程很暴力,不知道是否有更好的方法。
Copyright © 2018 - 2023 AprilNEA's Blog