http://p3.qhimg.com/t0133aa0ef16dfa42a9.jpg



0x00 前言


有时候,通过正则表达式来对字符串进行白名单过滤并不好使。本文通过例子演示正则表达式在对字符串进行白名单过滤的时候可能引发OSCI(Operating System Command Injection)漏洞。



0x01 正文


测试代码如下:

1
2
3
4
5
6
7
8
    <?php
      $file_name = $_GET["path"];
      if(!preg_match("/^[\/a-zA-Z0-9\-\s_]+\.rpt$/m", $file_name)) {
        echo "regex failed";
      } else {
        echo exec("/usr/bin/file -i -b " . $file_name);
      }
    ?>

咋一看这段代码好像没什么问题,就是匹配文件名由字母、数字、下划线、破则号、斜杠、空白字符各种组合的并且后缀名是rpt的文件,如果匹配成功,就执行系统命令file打印文件的类型和编码信息,如果匹配失败就打印'regex failed'.

刚开始的时候,我也是尝试了各种attack payload,但是都没有成功进行命令注入,后来我才发现原来攻击点在\s这正则过滤上,\s 意思是匹配任何空白字符,何为空白字符,就是常见的[\t\r\n\f] (Tab、回车、换行、换页)等特殊字符,这里换行符就很危险,换行符在其他场景可能没有风险,但是在shell环境下,就有可能造成命令注入,看看下面这段payload

1
file%0Aid%0A.rpt

%0A是URL编码后的换行符,显然这段payload 匹配上述正则,当这段payload在shell环境中执行,会发生什么呢?

t01fba45e8667091fbc.jpg

为什么会这样呢。因为在shell环境中多个命令的分隔符除了;之外还有换行符,上述payload 传入shell之后,就变成两条命令执行:

1
2
3
    file -i -b file%0A
    id%0A
   .rpt

所以就出现了打印id 命令执行的内容

那么如何解决呢,很好办,把\s替换成" " (space)就ok了

t019aa04821acc960c2.jpg

是不是以为这样修改就万事大吉了? 年轻人,too young too simple,看看下面的payload

1
    file.rpt%0aid

看看执行后会是什么鬼?

t01af6f3a3b5b8684db.jpg

很意外吧,命令注入成功,我们来看看什么原因导致了

注意到正则表达式结尾的/m 了,在php中,/m表示开启多行匹配模式,开启多行匹配模式之后^和$的含义就发生了变化,没开启多行模式之前(即单行匹配模式), ^ 和$ 是匹配字符串的开始和结尾,开启多行模式之后,多行模式^,$可以匹配每行的开头和结尾,所以上述payload里面含有换行符,被当做两行处理,一行匹配OK即可,所以进入了exec执行分支,进而导致命令执行。


 0x02 总结


所以,当我们在使用正则表达式构建白名单的时候,一定要小心一些,尤其涉及到命令执行的时候,有时候,白名单越明确越好,比如遇到类似上述场景,我们可以定义一些完整的被允许的文件名,而不是用正则表达式。



本文由 安全客 翻译,作者:ForrestX386

原文链接:https://nvisium.com/blog/2015/06/11/regex-regularly-exploitable/