PHP代码审计Day学习笔记5-8
文章目录
来自先知社区-红日安全-代码审计小组的PHP代码审计的项目本阶段的内容题目均来自 PHP SECURITY CALENDAR 2017
Day5 - escapeshellarg与escapeshellcmd使用不当
前言
mail()
bool mail (
string $to ,
string $subject ,
string $message [,
string $additional_headers [,
string $additional_parameters ]]
)
php内置函数
- to,指定邮件接收者,即接收人
- subject,邮件的标题
- message,邮件的正文内容
- additional_headers,指定邮件发送时其他的额外头部,如发送者From,抄送CC,隐藏抄送BCC
- additional_parameters,指定传递给发送程序sendmail的额外参数。
eg:
使用了-X
指定了日志文件,导致木马文件的写入
FILTER_VALIDATE_EMAIL
filter_var()
函数的选项,确保是否是有效的电子邮箱地址
escapeshellcmd()
escapeshellcmd — shell 元字符转义
**功能:**对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。
escapeshellarg()
escapeshellarg — 把字符串转码为可以在 shell 命令里使用的参数
**功能:**将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号
题目
漏洞解析
- 第3行
filter_var()
对邮箱检查,时由于底层正则表达式的原因,我们通过重叠单引号和双引号,欺骗 filter_val() 使其认为我们仍然在双引号中,这样我们就可以绕过检测 - PHP的
mail()
函数在底层实现中,调用了escapeshellcmd()
函数,但第7行又调用了escapeshellarg()
.escapeshellcmd()
和escapeshellarg()
一起使用,会造成特殊字符逃逸
eg:
详细分析一下这个过程:
1.传入的参数是
127.0.0.1' -v -d a=1
2.由于escapeshellarg先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。所以处理之后的效果如下:
'127.0.0.1 -v -d a=1'
3.接着escapeshellcmd函数对第二步处理后字符串中的 以及 a=1’ 中的单引号进行转义处理,结果如下所示:
'127.0.0.1''' -v -d a=1'
4.由于第三步处理之后的payload中的 被解释成了 而不再是转义字符,所以单引号配对连接之后将payload分割为三个部分,具体如下所示:
所以这个payload可以简化为 curl 127.0.0.1/ -v -d a=1'
,即向 127.0.0.1
发起请求,POST 数据为 a=1’ 。
总结
考察绕过 filter_var() 函数的邮件名检测,通过 mail 函数底层实现中调用的 escapeshellcmd() 函数处理字符串,再结合 escapeshellarg() 函数,最终实现参数逃逸,导致 远程代码执行 。
Day6 - 正则使用不当导致的路径穿越问题
前言
preg_relace()
preg_replace — 执行一个正则表达式的搜索和替换
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int KaTeX parse error: Expected 'EOF', got '&' at position 19: …it = -1 [, int &̲count ]] )
搜索subject
中匹配pattern
的部分, 以replacement
进行替换。
题目
题目叫福斯特模式,代码如下
漏洞解析
- 第21行的
preg_replace()
调用中使用的正则表达式中的非转义连字符-
。如果连字符未转义,则将其用作范围指示符,从而导致 替换在点(46)和下划线(95)之间的范围内,并且不是a到z的任何字符。 因此,点和斜杠没有过滤,可以用于目录遍历,例如使用查询参数action = delete&data = … / … / config.php。
Day7 - parse_str函数缺陷
前言
pares_str()
parse_str — 将字符串解析成多个变量
void parse_str ( string KaTeX parse error: Expected 'EOF', got '&' at position 25: …tring [, array &̲result ] )
参数 | 说明 |
---|---|
encoded_string | 输入的字符串 |
result | 设置了第二个变量 result, 变量将会存入到这个数组。 |
题目
题目叫做钟,代码如下:
漏洞解析
- 变量覆盖漏洞,不安全的使用了
parse_str()
.第21行的钓鱼,非常类似于注册全局变量,因此我们可以控制5到8行的$config
变量
payload
http://host/?config[dbhost]=10.0.0.5&config[dbuser]=root&config[dbpass]=root&config[dbname]=malicious&id=1.
Day8 - preg_replace函数之命令执行
前言
prep_replace()
preg_replace — 执行一个正则表达式的搜索和替换
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int KaTeX parse error: Expected 'EOF', got '&' at position 19: …it = -1 [, int &̲count ]] )
搜索subject
中匹配pattern
的部分, 以replacement
进行替换。
- $pattern 存在 /e 模式修正符,允许代码执行,将 $replacement 当做php代码来执行
反向引用
对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
题目
题目叫蜡烛,代码如下
漏洞解析
- 这道题目考察的是
preg_replace()
函数使用/e
模式,导致代码执行的问题。我们发现在上图代码第11行
处,将 GET 请求方式传来的参数用在了complexStrtolower
函数中,而变量 $regex 和 $value 又用在了存在代码执行模式的 preg_replace 函数中。所以,我们可以通过控制 preg_replace 函数第1个、第3个参数,来执行代码
payload
官方的
/?.*={${phpinfo()}}
不能用,因为如果GET请求的参数名存在非法字符,PHP会将其替换成下划线,.*
变成_*
可用的
S*=${phpinfo()}
详细分析见: 深入研究preg_replace与代码执行