在抽奖页面网页源代码中,有几行提示和代码:
// 这个 getVerifyCode 的 wasm 实现比 blueimp-md5 js 实现快 20 倍。
// 猜猜 flag10 藏在什么地方?
WebAssembly.instantiateStreaming(fetch('get_verify_code.wasm')).then(({instance}) => {
window.getVerifyCode = (prefix) => {
console.log('prefix:', prefix);
const startTime = Date.now();
const memory = new Uint8Array(instance.exports.memory.buffer);
const prefixBufPtr = 16;
const prefixBufLen = ((new TextEncoder()).encodeInto(prefix, memory.subarray(prefixBufPtr))).written;
const resultBufPtr = 0;
const resultBufLen = 16;
const resultLen = instance.exports.get_verify_code(prefixBufPtr, prefixBufLen, resultBufPtr, resultBufLen);
const code = (new TextDecoder()).decode(memory.subarray(resultBufPtr, resultBufPtr + resultLen));
console.log(`solved: ${prefix + code} ${(Date.now() - startTime) / 1000}s`);
return code;
};
});
如下图所示:
在代码(不包括注释)的第一行可以看到,网页试图通过绝对路径加载get_verify_code.wasm二进制文件(因此该文件实际URL路径为https://2025challenge.52pojie.cn/get_verify_code.wasm)
在代码(不包括注释)的第二行可以看到,网页试图调用get_verify_code.wasm中的getVerifyCode函数
这不得拉下来逆一逆?
那么,废话不多说,我们把get_verify_code.wasm下载下来看一看到底是何方神圣,一探究竟,如下图所示:
我们使用wabt工具集中的wasm-decompile工具来将get_verify_code.wasm转换为伪代码之后进行分析:
wasm-decompile.exe get_verify_code.wasm -o code.txt
转换为伪代码后,我们搜索其中的所有函数,发现export function只有2个,并且其中一个就是getVerifyCode(a, b, c, d)函数,如下图所示:
查看另一个export function后,发现是calc_flag10_uid_timestamp_resultbufptr_resultbuflen_return_resultlen(a, b, c, d)函数,如下图所示:
由于伪代码非常杂乱,我们借助AI分析一下该函数的输入值和作用,如下图所示:
我们通过导入Python的time库,利用time.time()直接计算时间戳,然后再导入wasmtime库在本地加载get_verify_code.wasm(将Python脚本与get_verify_code.wasm放置在同一目录下),依次传入uid, timestamp, buf_ptr, buf_len,调用calc_flag10_uid_timestamp_resultbufptr_resultbuflen_return_resultlen(uid, timestamp, buf_ptr, buf_len),即可得到flag,代码如下:
import wasmtime
import time
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, "get_verify_code.wasm")
instance = wasmtime.Instance(store, module, [])
# 获取导出函数和内存
calc_flag10 = instance.exports(store)["calc_flag10_uid_timestamp_resultbufptr_resultbuflen_return_resultlen"]
memory = instance.exports(store)["memory"]
# 参数说明:
# a: UID(根据需要填写正确的UID)
# b: 时间戳
# c: 结果缓冲区指针(这里选用heap_base,即1049296)
# d: 结果缓冲区长度(根据实际需要,此处设置为32)
uid = int(input("Please enter your UID: "))
timestamp = int(time.time())
buf_ptr = 1049296 # heap_base
buf_len = 32
# 调用函数,返回值为写入结果的长度
res_len = calc_flag10(store, uid, timestamp, buf_ptr, buf_len)
# 从内存中读取结果
# memory.data_ptr(store) 返回一个可切片的字节数组
raw = memory.data_ptr(store)[buf_ptr:buf_ptr+res_len]
flag = bytes(raw).decode("utf-8")
print(flag)
附件(包含题目get_verify_code.wasm文件 & 转换后的code.txt伪代码文件 & 解题Python脚本文件):
flag10.zip
(13.6 KB, 下载次数: 1)