DockerPwn – A Walkthrough

It’s been a while since my last post!

That said, I have been rather busy with another project – an automated script to attack exposed Docker TCP Sockets. This isn’t a new phenomenon; the vulnerability has existed as long as Docker has. However, it’s somewhat arduous to do via cURL, and I like to build things in Python.

First, a primer – Exposing the Docker TCP socket is akin to having the root password tattooed on your forehead. Basically, any user who has access to the Docker socket has implicit root-level permissions. This is obviously easy to abuse. We can spin up arbitrary containers, and even mount the root file system and alter it as we please, leaving us many methods to shell the host. I’d recommend checking out this article published in April for a good understanding of the threat.

This is where DockerPwn comes in. It’s a fairly simple collection of Python scripts that will automatically perform the requisite actions to obtain a reverse shell in the context of the root user of the host machine.

DockerPwn has three methods it can use to do this, some quieter than others.

  • ShadowPwn (Loudest)
    • Creates container with host’s / mounted to /host
    • Alters any valid Unix SHA512 and root passwords in /etc/shadow.
    • Logs in using Paramiko, and sends a reverse shell using Bash.
    • Catches shell via nclib, escalates using ‘su’, and spawns a PTY.
    • Replaces altered /etc/shadow with /var/backups/shadow.bak
  • UserPwn (Middle Ground)
    • Creates container with host’s / mounted to /host
    • Adds user DockerPwn:DockerPwn to /etc/shadow and /etc/passwd
    • Logs in with Paramiko and sends a reverse shell using bash.
    • Catches shell via nclib, escalates using ‘su’, and spawns a PTY.
  • ChrootPwn (Quietest)
    • Creates container with host’s / mounted to /host
    • Writes out – a bash reverse shell
    • Begins serving HTTP on 80
    • Chroots to /host, downloads to /tmp and executes.
    • Catches shell via nclib, spawns a PTY.

All these methods result in a shell with root-level permissions. Chroot is the only method that does not rely on SSH being presented in some fashion – though if TCP 2375 is exposed, it’s fairly likely that SSH will be as well.

If you’d like to test this tool yourself, it’s fairly simple to set up an environment. Assuming you’re familiar with setting up a virtualized environment, you’ll need a Linux host of some variety.

Once you have your virtualized host up and running, the Docker Socket can be exposed by altering the Docker service file. On Ubuntu or Debian-based distributions, the following is done:

  • sudo nano /etc/systemd/system/docker.service.d/startup_options.conf
  • Insert the following into the file:
# /etc/systemd/system/docker.service.d/override.conf
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://
  • sudo systemctl daemon-reload
  • sudo systemctl restart docker.service && netstat -antp | grep LISTEN

You should now see Docker listening on TCP 2375. You can now utilize DockerPwn from the listening server, or from an outside physical or virtual machine.

Syntactically, the usage is simple, and the only mandatory options are C2, target, and port. Everything else just runs with the defaults listed below if not selected. [-h] [--target TARGET] [--port PORT] [--image IMAGE] [--method METHOD] [--c2 C2]

optional arguments:
  -h, --help       show this help message and exit
  --target TARGET  IP of Docker Host
  --port PORT      Docker API TCP Port
  --image IMAGE    Docker image to use. Default is Alpine Linux.
  --method METHOD  Method to use. Valid methods are shadow, chroot, useradd. Default is useradd.
  --c2 C2          Local IP and port in [IP]:[PORT] format to receive the shell.

See below for a demonstration of DockerPwn in action!

Thanks for reading through – be sure to check out DockerPwn on my GitHub here! – and be on the lookout for future projects 😉

SafeHarbor – Vulnerable Machine

After my last vulnerable machine release, I wanted to do something a bit more difficult. For this machine, I’ve channeled a bit of the OSCP-style machine, though I’d say this is certainly among the harder kind of machine you’d encounter.

Thematically, this machine is focused on financial services, as well as on newer methodologies for IT infrastructure. It’s hard to say much without giving away the box’s secrets, but it is not a standard CTF box!

You will need the following skills:

  • Network Pivot
  • Enumeration
  • Web App Security
  • Metasploit & Meterpreter
  • Patience

Of note as well – this machine has two bonus flags. The flags will appear in the /root directory based on pre-defined extra actions you take during the process of rooting the VM. Good luck!

Download: SafeHarbor at Google Drive (3.2GB)

Mumbai SEO – Vulnerable Machine Release!

In the spirit of VulnHub/HTB, I have created a purposely-vulnerable machine for practice. While I’ve created intentionally vulnerable environments and applications for testing before as part of my daily work, this will be the first one I have released to the InfoSec community at large.

The machine is in OVA format, and may be imported into VirtualBox using the ‘Import Appliance’ feature.

If you have any feedback, leave a comment here or hit me up on Twitter/LinkedIn, etc.


Download Link: Mumbai SEO at Google Drive (2GB)

root@CaffSec~# – My OSCP Experience

Please Note:

This is an edited, redacted version of the original article in an effort to comply with a request from Offensive-Security. Though I believe little value could be gleaned from the original article from someone taking the course, they are very protective of their process and IP – rightfully so.

Original Article:

When I registered for my PWK course in May of this year, I promised myself I wouldn’t write one of these posts. I felt like there was little to gain from writing another blog post with the same resources that have been posted repeatedly. And in large part, I still feel that way, but feel strangely compelled to write this, a month after clearing the exam.

My Background

I pre-gamed the OSCP quite a bit. I have been in Information Security in some definition for 6 years. Around March of 2017, I joined HackTheBox, after struggling immensely with the join challenge. I wasn’t really sharp on web app security, and barely knew any coding languages of any type at the time, save C#. It was around this time that one of my friends made me aware of the OSCP exam and what it entailed. I had passed the only other two certifications I had attempted and obtained other certifications (CCNA, Network+) at that point fairly easily, and it sounded like a challenge – albeit one I was not prepared for.

Throughout 2017, I continued to do HTB, with some moderate success, though it was fairly difficult to do in the position I was in, which demanded a lot of my attention, and left me unable to study.

In 2018, I changed jobs to become a full-time Vulnerability Analyst, which was probably the biggest help in my preparation for the OSCP, as the exposure to breadth and depth of vulnerabilities and exploits that can affect a large enterprise environment is incredibly helpful. During this time, I also obtained my Security+ and my CISSP.

The Course

After registering in April of 2019 for the OSCP with 60 days worth of labs, I patiently waited for the course to begin on May 11. On May 11 at 7 PM, my lab access came in. On my first night in the labs, I worked until 2 AM, and managed to own four of the boxes.

After owning the first ten or so boxes, I felt I would almost certainly be prepared to take the exam 30 days in. If I failed – so what? I had 30 more days of lab time. So, I scheduled my first exam attempt 30 days out from the course start date. During this time, I made it my goal to at least get a box a day down. This wasn’t always the case, but by the end of the thirty day cycle to my exam, I had taken down 32 boxes.

There is a lot of speculation around the ‘Big Four’ boxes in the lab online, but I will say that I believe they are not the be-all, end-all they are made out to be, and almost all boxes presented by OffSec present some value in the learning process.

Each of these boxes taught interesting lessons that I hadn’t considered before. Several of them taught me to never assume that a vector was not attackable based on previous experience, and to ensure you enumerate everything.

Once I felt I had really owned a significant amount of the lab, including 1-2 machines in adjacent networks requiring pivot, I began to work on my lab report , and completed it in the span of about a week – though it requires a lot of work, and I do not recommend underestimating this.

It was now time for my first exam attempt.

Hubris and Downfall – Exam Attempt #1

My first exam attempt began 30 days after venturing into the labs, at 1:00 PM. I was able to quickly set up the proctoring session, and the VPN email came through exactly on time, and was established without a hitch.

I immediately began working on the buffer overflow, as it was worth 25 points, and was easily the concept I was most comfortable with. I will say this about the buffer overflow – take your time, and don’t miss anything stupid. Make sure you step through each thing you were taught in the course material. That said, the BOF machine fell quickly, in about 30 minutes, and I had 25 points. Awesome.

From there, I made very little progress. Initially, I began to enumerate what I thought was the second 25 point box, but had gotten turned around somewhat, and was actually enumerating the BOF box again, looking for an entry point that did not exist. I wasted at least two hours before realizing my mistake.

After pivoting and returning to face the actual targets, I managed to enumerate one 20 pointer enough to get a limited shell. However, I could not find a method to escalate in any way, and spent several hours attempting to do so. Facing a dead-end, I moved to the 25 point box. However, I was unable to make heads-or-tails of it, the 20 point box, or the low-effort 10 pointer – which seemed the most confusing of all.

By midnight, it was apparent that I was not going to make any more progress, never mind pass. I let my proctor know I was stepping away, and slept. I did not bother to finish or turn in the report the next day.

Regaining Composure

I was pretty unhappy with my performance in the first exam attempt. I had assumed that the exam would be an extension of what was experienced in the labs, and that could not be further from the truth.

While there are certainly similarities, it’s far more important to understand the process itself, not the technical details of what the lab is teaching you. I had heard this before, but I believe it’s really difficult to take this to heart and understand what is really meant by this without experiencing it yourself – even if that initially leads to frustration and failure.

To better prepare for my second attempt, I did the following:

  • Completed @TJNull’s HTB List w/ HTB VIP
  • Watched IppSec on Youtube – specifically, areas I was weak in (Windows & PrivEsc)
  • Found a set of enumeration checklists to follow
  • Completed OSCP Pre-Exam VulnHub machines
  • Spent a week feeling sorry for myself

After about three weeks of the above, I felt ready to challenge the exam again – though I really hoped I would have a different set of boxes and experience this time, so the challenge would remain. At this point, I also bought a second ultrawide monitor for use during the exam – I’d highly recommend having at least two monitors, as it did feel rather crippling to only have one.

Finally Free – Exam Attempt #2

My second exam attempt started a bit later in the day, at about 3:00 PM. I was more laid back during this exam attempt than I had been in the previous one. No butterflies, no sweaty palms, just ready to give it a second shot. Again, the proctoring session and exam started seamlessly, and I began.

The BOF fell again in approximately 30 minutes. 25 points, plus lab report left me with 30 out of a necessary 70. The plan going in was to immediately attack the second 25 point box to attempt to obtain as many points as possible quickly.

I began enumerating the 25 point box following the checklist I had previously obtained during my two-week pity party, and quickly found an entry point, though it was more complex than I had imagined I would see. With a limited shell on this box, I ostensibly now had 42.5/70. My initial attempts at privilege escalation were fruitless, so I then moved on to a 20 pointer to avoid tunnel-vision and missing something obvious.

The 20-point box was incredibly easy. I found an entry point to it within less than an hour, and the privilege escalation less than half an hour after that. It was a platform I was very familiar and comfortable with, and had no issues exploiting, as I had seen it before several times. I now had 62.5/70, and it was time for dinner.

I stepped away from my desk entirely at this point, at about 6:00 PM, and had a normal dinner with my family. I ranted and raved for a few minutes about how I was unsure if I could get the remaining 7.5 points, or if I would fail again, and returned to my test shortly thereafter.

The 10 point and remaining 20 point box seemed entirely hopeless, as I couldn’t find any entry point to either of those – though the 10 point box is supposed to be the easiest. In light of this, I focused all of my efforts on privilege escalation of the 25 point box.

There seemed to be very little to go on, which some might think would make the escalation rather obvious. In my case, it made it rather difficult, and I ended up spinning my tires until almost 1:00 AM. At about 12:45 AM, however, I managed to find the simple things I had missed, and was able to return a root shell. I now had 75 points – and with that, went to bed.

I got up far before my exam was scheduled to end the next day, and attempted to get some insurance points, but came up entirely flat. My brain was fried at this point, and I was not able to proceed. In light of this, I began writing my report, and submitted it shortly thereafter, about 24 hours ahead of schedule.

Waiting, Inconsolably

I’m a bit of a worry-wart by nature, so waiting for me is often the worst part of any process. It certainly didn’t help that I realized in a cold sweat at midnight three days after report submission that I had missed a piece of information I thought was a critically failing mistake.

My report took about nine days to grade with the lab report, when I was finally greeted with the email that I had passed. My stomach sank when I saw the email from Offensive Security, but I was pleasantly surprised to read that I had passed. About a month later, my certification package came in the mail.

Takeaways and Future Plans

I think most other folks have offered summations of this cert that are probably more than sufficient, so I’ll keep mine a concise, bulleted list.

  • Have a Plan of Attack
  • Follow your Plan of Attack
  • Don’t Panic
  • Take Your Time

I can’t really stress any of these enough. It isn’t enough to have a plan, you must also execute it. When things don’t go to plan, you cannot panic. You must simply adapt your plan. Remember – no plan survives contact with the enemy.

Take your time, be sure you do things right the first time. Good life advice and good OSCP advice. 24 hours is plenty of time to do it, and do it well, and be thorough. And don’t forget that you have another 24 hours post-exam to get that report in. Don’t rush it like I did, and there won’t be any doubt as to whether you passed.

As for the future, right now, I’m not sure what that holds. Shortly after taking the OSCP, I also took the Pentest+ with CompTIA and passed that, solidifying another credential. Really, at this point, I’m taking a bit of a break from the stress of it all.

That said, I’d really like to get some professional, real-world pentesting engagement experience at some point, and will probably tackle the OSCE at some point next year, as I’m already half-way through the registration challenge there.

But for now, I feel all certed up, and am going to see where this leads me. All in due time!


[Personal GitHub links removed at request of Offensive-Security]

@TJNull’s OSCP List for HTB:

IppSec’s YouTube Channel:



Attacking Simple ASCII Shift Ciphers

There are many simple ciphers in semi-common use across the Internet as a whole, particularly in ‘roll-your-own’ encryption implementations.

Unfortunately, many custom encryption or cipher algorithms fall rather flat during implementation. This is largely due to either too much information being disclosed, or poor logic in the algorithm. Ideally, you don’t want to expose your key – but frequently these algorithms do, specifically when implemented in WebApps – otherwise the client could not use the algorithm.

These ciphers are, for all intents and purposes, an ASCII implementation of a Caesar Cipher.

This article contains a Python recreation of a JS ‘encryption’ scheme I’ve seen in use across a few web applications. It implements what amounts to a shift cipher using ASCII ordinals and basic mathematics operations.

The Cipher

Just a quick look at this algorithm’s Python implementation indicates it’s likely very reversible, and as a result will not be difficult to attack. It seems to use whole numbers instead of float values – which would make this difficult, if not impossible – and exposes the key entirely within the script itself.

I find when attacking an unknown cipher or algorithm such as this one, it’s helpful to go through the operations performed line-by-line and comment my understanding of the operations being performed.

Starting before the main() method, we’ll comment out our understanding. Firstly, examining this we know the following:

  • Password length handled cannot be > 30 characters due to key length.
  • Python’s random library is in use to generate a random number
  • Two lists are defined to store the password input, and another list to store the ‘encrypted’ results.

The main function is fairly boring and innocuous. It only reveals to us that it accepts ‘–password‘ to know what string to encrypt, and ‘–encrypt’ as a bool value to trigger the encryptPass() function – where the real magic happens.

This function is where the actual shift takes place, and so has a lot to unpack, though it’s simple in practice.

First, a random number between 0 and 5 is generated using Python’s random library. The resultant number is then multiplied by 5, and one is added to the result, creating a final random number. Obviously, regardless of which random number is generated – there are only six possibilities.

Second, the password passed into the script as an argument is reversed and each character is loaded separately into a Python list object called passArray. As shown above, this would result in something like [d, r, o, w, s, s, a, p] for a password of ‘password’.

Here’s where things get interesting. Each ASCII character on the keyboard has a numeric value assigned to it. There are 256 possible ASCII character values in the extended table. However, when limited to current keyboard models, this can be pared down to 127. If limited further to printable characters, the possibilities are reduced again, to ASCII codes 32-126, for a total of 94 possible printable ASCII password characters.

Once loaded into the array, the ordinal, or numeric value of the ASCII character is obtained, and loaded into a variable entitled ‘CharASCII’, resulting in a Python list object of numeric values. For our example ‘password’, it would look like [100,114,111,119,115,115,097,112].

The numeric value of the character then has the corresponding keyArray value subtracted from it – so the value of keyArray[0] is subtracted from CharASCII[0], and so on – then the previously generated random number is added to it.

This is repeated for each character up to 30 characters in the passArray list variable. The resultant ordinals are loaded into another list object, resulting in a list of numeric values of transformed password characters in reverse order. The resultant data from these operations is then loaded into a third list object entitled encryptedArray.

With the mathematic operations complete, the ordinal is then re-converted to ASCII characters, and spit out onto the screen. Let’s run the script and see what the output looks like for ‘password’.

Interestingly, we have a seemingly random string returned from this. It’s important to note on subsequent runs, the output value may change depending on the random number that is generated between 0-5.

However, because we’re able to analyze the script and have access to the key, we should be able to build a brute-force script that reverses every mathematic operation, and tries all five possible random numbers to get the right combination and reverse the password.

Reversing the Script

Above is the final product of taking the first code and reversing every mathematic operation performed on the ASCII ordinals obtained during encoding. In addition, this code contains a for loop which tries every possible random number – that is, 0 through 5 – and performs the requisite operations to get the ‘magic number’ with which to shift the ASCII ordinals.

Let’s repeat our process from earlier and step through this script a chunk at a time in order to better understand what it is doing.

In the first chunk of code for our brute-forcing module, we have a variable where we store a resultant ciphertext produced by the previous script. In this case, the hash variable is set to Oyafcb\{.

The code then iterates over each character in the hash, and appends it to a Python list object called encryptedArray. For the current value, this would lead to a value of [O,y,a,f,c,b,\,{].

Once the characters are loaded into the list object, the script begins the brute force attempt. It iterates over each of the possible random numbers, performing the requisite operations that existed in the previous script to get all possible combinations. In the case of this cipher, the combinations are 1, 6, 11, 16, 21, and 26. Time could be saved here by hard-coding these values, but for demonstrative purposes, the exact operations were followed from the first cipher script.

In the second chunk of code for the brute force module, a nested for loop inside the random number loop reverses all the operations performed in the initial cipher. That is to say the following occurs for each character:

  • ASCII values of cipher text are re-converted into their numeric ordinal values.
  • Because the random number value was added in the initial cipher, it is subtracted from the resultant ordinal in this module.
  • Because the corresponding key value was subtracted in the initial cipher, it is added to the resultant ordinal value from the last step.

The result of this operation is another Python list object with individual characters, converted from ASCII ordinal values. Each value in this list is then joined together into a string, reversed, and stored in a variable called decryptedPass which is then printed to the screen. These actions take place six times – using each possible random number.

Let’s take a look at the script in action:

The script runs in a few seconds – as simple math like this is no challenge for any modern computer. It quickly iterates through all the characters and all 5 possible random numbers, dumping each result out to the screen.

It’s obvious in this case what the random number was, and what the resultant password should be. In other cases, it may not be, if a complex password was utilized, but it’s no problem to try each possibility either, considering there are only five.

Mitigating the Attack

Mitigation of attacks on classical ciphers or variations on them is simple – Don’t use them in any way, as they are inherently insecure. Every programming language, including JS and Python, have AES implementations or libraries, making it entirely unnecessary to roll-your-own encryption.

There are ways that this cipher could become more difficult to break, though not impossible. If the key were not included within the source, it would be much more difficult. With only 92 possibilities of ASCII ordinals in the final output, this may become easier, however.

Another way to make this infinitely more difficult to attack would be to use a random float value instead of a whole number as the random seed. Given that there are an infinite amount of floating points between any two whole numbers, this would make an attack infinitely less likely and more complex. Unfortunately, because ASCII ordinals are a whole number value, this isn’t really possible in practice.

Thank you for reading! If you found this article interesting, please feel free to share it with your friends in the Information Security community!

Steam Escalation of Privilege Exploit

Earlier this week, a zero day for Steam Client resulting in escalation of privilege to NT AUTHORTY\SYSTEM was published. While this particular vulnerability has received quite a bit of press attention, in most threat models, it’s likely a rather small portion of the whole attack surface.

The Steam Client vulnerability entirely relies on two things:

  • Poor service and registry key permissions configuration
  • The ability to create symbolic links to registry keys.

As shown in the image below, the permissions for the “Steam Client Service” allow any user to start or stop the service.

Additionally, the output of Get-ACL for the corresponding registry key the service accesses seems similarly open, allowing BUILTIN\Users full control.

Many previous articles have been written regarding this weakness, so I will not dive into technical depth, but the gist is that due to our ability to fully control this key, we may create a symbolic link using external tools which will allow us full access to another key, allowing us to execute arbitrary code.

To this end, I created the following PowerShell code which automatically steps through the requisite steps to exploit the vulnerability.

The script first automatically checks for previous exploit attempts. I learned the hard way that not checking for this was a really good way to delete the Windows Installer Service entirely, since the Steam Client Service had previously been symlinked to it. It additionally checks for the presence of the RegLN and Netcat binaries, and downloads them from the Github Repo if they do not exist in the expected directory.

Once this is completed, the requisite service operations are performed – clearing the Steam key, and symlinking the Windows Installer key to it. The ImagePath parameter is then changed to automatically run a Netcat reverse shell, which is executed the next time the Windows Installer service is launched. See the GIF below for a demonstration of the exploit.

The exploit code can be downloaded and altered as needed from my GitHub repository at this link. Hack responsibly!

CaffeineSec Open!

It’s well known in the InfoSec community that in order to be successful and contribute to the knowledge of others, a blog is (nearly) a requirement!

After putting it off for some time (In the past year I’ve been busy with starting a new position, obtaining my CISSP, OSCP, Security+, and Pentest+!) I decided that now would be a good time to begin pursuing this particular goal, and contributing more back to the InfoSec community, as so many have done for me on my journey.

I’ve owned this domain for quite some time, and finally got around to configuring a nice place for it to live in AWS this past evening. I am going to aim to try and post some sort of informative article, current event, walkthrough, or general security news every week.

So, without further ado, here we are! I hope you enjoy your stay on my InfoSec blog, and feel free to share it and make me scale up from the free tier in AWS!

– Dylan