函数学习
preg_replace:(PHP 4, PHP 5, PHP 7)
功能 : 函数执行一个正则表达式的搜索和替换
定义 :
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索 subject 中匹配 pattern 的部分, 如果匹配成功将其替换成 replacement 。
Demo学习
1 |
|
createToken()
函数中,使用file_put_contents()
函数写文件,但$token
值经过了md5加密,无法利用。在clearToken()
函数中,$file
值经过preg_replace()
函数使用正则过滤后,unlink()
删除对应文件,漏洞点存在此处。正则过滤的范围是a
到z
和.
到_
,也就是ascii46~95范围的字符:
1 | . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ |
可以看到.
和/
未被过滤,那么可以通过../
进行路径穿越删除任意文件。
1 | Payload: |
案例学习
源码
1 | // index.php |
1 | // flag.php |
分析
分析之前先记录一下PHP正则表达式的字符类:
alnum |
字母和数字 |
---|---|
alpha |
字母 |
ascii |
0 - 127的ascii字符 |
blank |
空格和水平制表符 |
cntrl |
控制字符 |
digit |
十进制数(same as \d) |
graph |
打印字符, 不包括空格 |
lower |
小写字母 |
print |
打印字符,包含空格 |
punct |
打印字符, 不包括字母和数字 |
space |
空白字符 (比\s多垂直制表符) |
upper |
大写字母 |
word |
单词字符(和 \w 一样) |
xdigit |
十六进制数字 |
本体存在多处正则绕过和弱类型比较的限制,所以拆分代码依次绕过:
0x01
第一处正则表达式:
1 | if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) |
^
表示以某种字符开头,$
表示以某种字符结尾,[[:graph:]]
表示打印字符。第一处正则要求输入大于等于12位以上可打印字符。
0x02
第二处正则表达式:
1 | $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; |
preg_match_all()
函数返回完整匹配次数,要求匹配次数不小于6次。正则表达式$reg
,表示字符串中,把连续的符号、数字、大写、小写,作为一段进行匹配。
例如:
1 |
|
输出:
1 | Array |
0x03
第三处正则表达式:
1 | $ps = array('punct', 'digit', 'upper', 'lower'); |
要求所输入的字符串至少包含符号、数字、大写字母和小写字母中的三种类型。
0x04
弱类型比较:
1 | if ("42" == $password) echo $flag; |
所输入的字符串与42
进行弱类型比较。
综合得到payload为:
1 | Payload: |
POST发送数据需要对+
进行URL编码。
思考题
题目:网络上还有一种解法是: password=\x34\x32\x2E ,但是这种解法并不可行,大家可以思考一下为什么。
因为POST、GET提交的数据都会被单引号包裹,单引号不会转义ASCII值,所以为false。
附加题
题目要求Getshell
1 | //index.php |
1 | //config.php |
题目中使用addslashes()
函数转义' " \
0x01 利用换行符绕过正则匹配
先输入的Payload为:
1 | http://127.0.0.1/php/day6/shell.php?option=a';%0Aphpinfo();// |
经过addslashes()
函数转义后为a\';%0Aphpinfo();//
,然后写入config.php
文件中,此时文件内代码为:
1 |
|
而此时phpinfo()
虽然已经写入到了文件中,但在第2行中存在\
转义插入的'
,phpinfo仍在单引号的包裹中。然后再输入Payload:
1 | http://127.0.0.1/php/day6/shell.php?option=a |
a
先经过addslashes()
函数中无需转义,在preg_replace
表达式1中将原本的a\
替换为a
,这样转义符就被替换掉,此时config.php
文件中内容为:
1 |
|
成功Getshell
0x02 利用 preg_replace 函数问题
preg_replace()
函数在处理字符串的时候,第二个参数replacement
会自动将\\
反转义为\
。Payload为:
1 | http://127.0.0.1/php/day6/shell.php?option=a\';phpinfo();// |
输入的a\';phpinfo();//
在经过addslashes()
函数后被转义为a\\\';phpinfo();//
,而\\
被函数反转义为\
,此时的就变成了a\\';phpinfo();//
,从而导致单引号逃脱。
0x03 利用 preg_replace() 函数第二个参数的问题
对replacement
的描述
replacement中可以包含后向引用\n 或(php 4.0.4以上可用)$n,语法上首选后者。 每个 这样的引用将被匹配到的第n个捕获子组捕获到的文本替换。 n 可以是0-99,\0和$0代表完整的模式匹配文本。
举例:
1 |
|
这里替换的是第一个表达式中的整个字符串。Payload为:
1 | http://127.0.0.1/php/day6/shell.php?option=;phpinfo(); |
此时config.php
为:
1 |
|
再输入Payload:
1 | http://127.0.0.1/php/day6/shell.php?option=$0 |
config.php
内容为:
1 |
|
前后单引号互相闭合,phpinfo()
逃脱出来。