题目活动所对应的地址:点此,这是吾爱破解论坛每年一次的解题领红包活动,需要大家通过逆向分析的本领来解锁红包口令。

番外篇 一

打开题目的网页,首先能够找到两个网页,一个是AI对话的页面还有一个是抽奖的页面。

在和AI对话时输入UID后直接询问其索要flag失败,发现有限制,那就可以表明这个AI肯定是知道Flag的。

所以可以断定是一个AI模型越狱的题目,所以我们要做的就是绕过这个限制。

通过查看该网页源代码,可以发现我们主要需要解决两个问题:

  • 第一个发送请求时需要验证码,但是源代码采取的是穷举这肯定是不行的,所以要给他换掉。

  • 第二个是如何绕过设定的问题限制,来问到Flag。

验证码好解决,因为在抽奖页面中同样也实现了一个验证码的生成算法,并且这个速度比隔壁快不少,所以我们直接给他搬隔壁去,利用一下。同时,这里的注释也为后续找到flag10埋下伏笔,提示我们可以去这个wasm中找。

flag9-1.png

然后对于绕过限制,我们可以采用伪造AI的历史对话记录来诱导它决定帮助我们获取到这个flag。这里具体的伪造内容大家可以看下图,差不多意思就行了,如果还是被限制了就多给他绕几个弯,分散它注意的点就行。 flag9-2.png

最终我们就比较容易的得到我们对应的flag了。 flag9-3.png

番外篇 二

根据上面所说,我们大致可以猜到flag10肯定与get_verify_code.wasm有关。所以我们来调查这个文件,首先搜索一下flag10就可以很容易的发现calc_flag10_uid_timestamp_resultbufptr_resultbuflen_return_resultlen这个函数。 flag10-2.png

这函数名可谓是操碎了心啊,就生怕你猜不到这个函数是干什么的或者怎么用,直接帮你把参数和返回值全都告诉你了。所以我们只需要想办法调用这个函数,就能得到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);
});

最终轻松拿到: flag10-3.png

番外篇 三

这个flag11是对应着那个抽奖页面, 根据其公开的抽奖算法,大致可以知道是根据区块的Hash值进行计算,最后和总数取余的一个过程。虽然每次都是获取最新的区块数字来决定,但是实际上出块的速度是远小于5分钟的。所以我们可以在离开奖还有三四分钟的时候调用API提前拿到blockHashflag11-1.png

拿到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
​

flag11-2.png

知道大致的组合后我们就可以开始刷记录,凑对应的数字。手刷肯定是不现实的,所以我们需要借助自动化的力量。以下是我刷数量的时候的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。

flag11-3.png