1、function
首先根据题目意思,传入两个参数 action和arg ,接着是三元判断符,即 action和arg如果没有赋值的话,就会被赋值为空字符。
接着是正则匹配式,action参数的开头不能是字母、数字、下划线。且 /i 表示不区分大小写 /s匹配任何不可见字符,包括空格、制表符、换页符等 /D不允许结尾有换行符。
所以如果action前面是一个特殊字符,就可以绕过正则了。
根据wp,发现%5c可以绕过,而%5c url解码后发现是一个反斜杠'\' 为什么这个字符可以绕过呢。
因为php里默认命名空间是'\',所有原生函数和类都在这个命名空间中。所以使用\function_name()时相当于写了这个函数的绝对路径。
任何发现后面会执行 $action('',$arg); 所以需要输入一个函数含有两个参数,且第二个参数可控。这里学习到了create_function()函数。
例子:
create_function('$fname','echo $fname."Zhang"')
function fT($fname) {
echo $fname."Zhang";
}
那如果使第二个参数改为 echo $fname."Zhang";}phpinfo();//
那就会变为
create_function('$fname','echo $fname."Zhang"':}phpinfo();})
function fT($fname) {
echo $fname."Zhang";}phpinfo();// 后面被注释掉
}
就可以执行一个phpinfo了。
所以可以使 action=%5ccreate_funciton&arg=2;}print_r(scandir('../'));// 查看上级目录下的文件
再读取文件内容 action=%5ccreate_funciton&arg=2;}print_r(file_get_contents('../flag_h0w2execute_arb1trary_c0de'));//
就可以得到flag了。
2、easy - pcrewaf
阅读代码后可以知道,这是一个和文件上传有关的题目,我们上传的文件内容会被读取到$data中,然后对$data进行正则匹配,
preg_match('/<\?.*[(`;?>].*/is', $data)
所以如果我们上传php文件的话,就会被正则给匹配到,会返回bad request。所以我们要想办法绕过这个正则表达式。
假如我们的文件内容为 //aaaaa的话,那么首先会匹配到 < 然后是 ? 接着匹配所有字符,匹配式直接到最后一位a,但是正则表达式还没有结束,[(`;?)]还没有匹配,所以此时会进行回溯,就是往前匹配,回溯过程如下。
但是回溯是有次数限制的,如果超过了这个限制的话,正则匹配就会失败,返回false,那这个次数限制是多少呢,据wp了解到是100万次,所以只要在// 后加100万个a 就可以绕过正则匹配了。
所以构造payload为 //'a'*1000000
然后再将文件传到服务器上,要记住设置allow_redirects=false,因为源代码中设置了303跳转,如果直接读取res.headers的话是无法读到url的
得到地址后再去操作
3、phpmagic
首先分析源码
需要我们post两个参数 domain 和 log
log会与SERVER_NAME拼接成log_name
而domain会进行html实体编码,也就是尖括号会被转成html实体
然后会判断log_name的后缀名是否存在数组中的值,若不存在则向log_name中写入domain的内容
首先我们要写入的文件肯定是php文件,那我们要怎么绕过检测后缀名这一步呢?根据wp提示,php/.可以绕过,因为php在处理路径的时候会递归的删除掉路径中存在的/. ,其次是log_name前面拼接的SERVER_NAME怎么处理。其实SERVER_NAME是可控的
所以只要修改headers里的host即可控制。
搞定了文件名之后,那我们要写入的内容要怎么绕过html实体编码这一关呢?这里可以想到题目作者的一篇文章 php://filter的妙用。通过 php://filter/write/=convert.base64-decode/resource=xxx.php,这样我们将要写入文件的内容先进行base64编码,然后在传入服务器之后被解码,这样就可以完成绕过了。
所以我们要传入的内容是
base64编码一下 PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==
路径为/data/ + md5($_SERVER('REMOT_ADDR'))
4、phplimit
代码很简洁,就是一个正则匹配式后,执行命令。
那么首先弄懂这个正则表达式的匹配规则就行了。
^\W表示匹配以数字或字母开头的数据,而\((?R)?\)可以分开来理解,\(.......\)是括号匹配,而(?R)?表示重复这个模式,例如 a(b(c()))可以被匹配到。
[^\w]匹配到b,\( 匹配到( ,(?R)匹配到 c() ,\)匹配到)。 所以也可以认为(?R)是匹配上一轮匹配到的数据,它会一直重复。
也就是说我们可以输入的数据可以是这种类型:function(function(function())),但是我们的函数中不能带有参数,那我们要怎么操控可控的数据呢?可以想到http头重的一些数据会被记录在一些函数中,或许可以利用。
根据wp提示有三种方式
第一种、session_id()
session_id 用来获取当前会话id,但是会话id的组成仅允许字母数字,这样可以想到用十六进制来进行操作。
所以构造payload:eval(hex2bin(session_id(session_start())));
第二种、get_defined_vars()
get_defined_vars()会以数组方式回显当前环境下的所有已定义变量,包括环境变量,服务器变量和用户自定义变量。
可以看到,返回了我们所传入的数据a的值,这说明我们有了可控数据,那我们要怎么样提取出这个数据呢?
这里有提取位置的函数。
使用current就获取到了我们数组中第一个变量 code的值,那我们再在这个数组中取它的最后一项。
所以最后构造payload
第三种、getcwd()
getcwd()可以获取当前目录
然后用scandir进行目录遍历
那么怎么进入上级目录呢,可以使用dirname(),它会返回文件路径的目录。
想到了之前的读取位置的函数,但是好像没有读取第三个位置的函数,所以可以把数组顺序倒过来一下。
但是读取文件的时候我们发现报错了
显示文件不存在,我们突然想到,还没有变换目录,所以我们还停留在www目录下,此时可以用chdir()来跳转目录。
或者用readfile()
之前犯了一个错误,就是想直接用file_get_contents()读取文件内容,但是这样是不会有回显的,需要用var_dump(file_get_contents())才能读取到文件内容,且返回的是数据的结构信息。
而使用readfile读取文件内容的时候是直接返回文件内容,这一点要注意。
参考博客:https://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&mid=2652850262&idx=1&sn=9676e4febf850dd9e4b36ea2538974dd&chksm=bd59349b8a2ebd8d24e9ce40a6397098ef91018d28f087b3518f8287e7a8a351536ffa1cc06e&mpshare=1&scene=23&srcid=#rd
https://www.jianshu.com/p/748749d38fb8