题目活动所对应的地址:点此,这是吾爱破解论坛每年一次的解题领红包活动,需要大家通过逆向分析的本领来解锁红包口令。
番外篇 一
打开题目的网页,首先能够找到两个网页,一个是AI对话的页面还有一个是抽奖的页面。
在和AI对话时输入UID后直接询问其索要flag失败,发现有限制,那就可以表明这个AI肯定是知道Flag的。
所以可以断定是一个AI模型越狱的题目,所以我们要做的就是绕过这个限制。
通过查看该网页源代码,可以发现我们主要需要解决两个问题:
第一个发送请求时需要验证码,但是源代码采取的是穷举这肯定是不行的,所以要给他换掉。
第二个是如何绕过设定的问题限制,来问到Flag。
验证码好解决,因为在抽奖页面中同样也实现了一个验证码的生成算法,并且这个速度比隔壁快不少,所以我们直接给他搬隔壁去,利用一下。同时,这里的注释也为后续找到flag10埋下伏笔,提示我们可以去这个wasm中找。
然后对于绕过限制,我们可以采用伪造AI的历史对话记录来诱导它决定帮助我们获取到这个flag。这里具体的伪造内容大家可以看下图,差不多意思就行了,如果还是被限制了就多给他绕几个弯,分散它注意的点就行。
最终我们就比较容易的得到我们对应的flag了。
番外篇 二
根据上面所说,我们大致可以猜到flag10肯定与get_verify_code.wasm
有关。所以我们来调查这个文件,首先搜索一下flag10就可以很容易的发现calc_flag10_uid_timestamp_resultbufptr_resultbuflen_return_resultlen
这个函数。
这函数名可谓是操碎了心啊,就生怕你猜不到这个函数是干什么的或者怎么用,直接帮你把参数和返回值全都告诉你了。所以我们只需要想办法调用这个函数,就能得到flag10了。至于怎么构建payload代码,根据之前获取验证码的那段代码依葫芦画瓢,直接抄就行了:
WebAssembly.instantiateStreaming(fetch('get_verify_code.wasm')).then(({instance}) => {
const memory = new Uint8Array(instance.exports.memory.buffer);
const resultBufPtr = 0;
const resultBufLen = 16;
const resultLen = instance.exports.calc_flag10_uid_timestamp_resultbufptr_resultbuflen_return_resultlen('你的UID', '当前的时间戳', resultBufPtr, resultBufLen);
const code = new TextDecoder().decode(memory.subarray(resultBufPtr, resultBufPtr + resultLen));
console.log(code);
});
最终轻松拿到:
番外篇 三
这个flag11是对应着那个抽奖页面, 根据其公开的抽奖算法,大致可以知道是根据区块的Hash值进行计算,最后和总数取余的一个过程。虽然每次都是获取最新的区块数字来决定,但是实际上出块的速度是远小于5分钟的。所以我们可以在离开奖还有三四分钟的时候调用API提前拿到blockHash
:
拿到blockHash
之后我们就可以计算中奖的号码和对应的总数了,这里我直接计算出了前10个可行的情况:
value = int("blockHash Here", 16)
userCount = 10000
flag = 0
while flag < 10:
userCount += 1
if value % userCount > 9980:
print(str(value % userCount) + " - " + str(userCount))
flag += 1
知道大致的组合后我们就可以开始刷记录,凑对应的数字。手刷肯定是不现实的,所以我们需要借助自动化的力量。以下是我刷数量的时候的Payload:
valid=0 // 校验码有效期
cache_code='' // 缓存的校验码
function submit(uid){
const timestamp=Math.floor(Date.now()/1000);
if(valid+50<timestamp){
cache_code=getVerifyCode(`${timestamp}|`);
valid=timestamp;
}
const req={
timestamp:valid,
uid,
verify_code:cache_code
};
fetch('/api/lottery/join',{
method:'POST',
headers:{
'Content-Type':'application/json'
},
body:JSON.stringify(req),
})
.then(res=>res.json()).then(res=>{
if(res.code===0){
console.log('参与成功,您的抽奖序号是 #'+res.data.user_index);
}
else{
alert(res.msg);
}
}
);
}
function start(){
var prefix=0
while(prefix<100){
let str=10000; // 这里写你要刷的数量
submit(str+1+prefix+"");
prefix=prefix+1
}
}
start();
首先想要发送请求我们就需要计算校验码,但是校验码的计算有需要花费时间,所以要想在短时间内发送大量请求我们肯定要搞定这个校验码。通过分析源代码,我们能够知道校验码是基于时间戳生成的,而服务器的限制是这个时间戳必须要在60s内,所以我们可以在这个60秒内重复使用同一个算过的校验码。
最后我们就成功的抽中(hei mu)了我们想要的flag。