A study in green

Here I’m back, my first attempt to publish a write up.

About two years ago, I participated in a Moroccan CTF competition. And there was a particular mind bending networking level (looks easy though after we’ve solved it). In this post, I would like to share a write up explaining how we’ve solved it.

Since it’s been two years, and I just decided to write about it now, obviously I dont have any of the material saved from that competition to recreate the experience. But luckily enough, I found online a very very similar exercise, that follows the exact same scenario. This one is called the “greenmonster”, it’s a firewall module, and the goal is very simple : Satisfy some conditions, clear the levels to bypass the firewall, which lets us connect to the port where we can get the shell.

Before we start solving the problem, I hosted for you here : the source code, the Makefile, and the firewall module precompiled for a “4.4.0-kali1-686” kernel. You can either compile the files for your environment, or just download the fw.ko and run the command:

Alright, let’s dive into it. After a quick scan of the entire code, we can guess what every function does, so let’s just go straight to the hook function that executes the module logic :

We skip the initialisation part and prep code, and scroll down straight to this part of the code :

We can see that there are two main branches in this code, that represent the two levels of this challenge. The first level  if (ip->protocol == IPPROTO_TCP) concerns the processing made to the TCP packets and the conditions that need to be fulfilled to pass this level.

The second branche  if (ip->protocol == IPPROTO_UDP) processes the UDP packets and checks the conditions needed to clear the second level.

  • Tackling the first level

Obviously we should start by clearing the first level, so we’re going to focus on the first branch.

Sidenote : NF_ACCEPT is a netfilter.h constant that tells the kernel to let the packet traversal. NF_DROP tells the kernel to block the packet.

Trying to read the first branche’s block of code, this part caught my eye :

It seems that the first level will be cleared when j == N_KNOCKS . By looking at the constants declarations made above the code, we can see that N_KNOCKS  holds the number 5 : #define N_KNOCKS 5 .

Ergo, to clear the level, j  must be equal to 5, and the variable j in itself gets the value from the node currkr->knock_port_idx . So how are we going to make the node currkr->knock_port_idx reach the value 5 ?

Well, if we dig just a little bit deeper, we’ll notice this block of code :

From this block of code, and previous assumption, we can understand the following :

  1. The node  currkr->knock_port_idx will get incremented every time the condition currport == ports[j]  is fulfilled.
  2. The variable ports[j] is an array that holds these values {4511, 4527, 4539, 4552, 4538};
  3. The variable currport  holds the port number receiving the TCP packet (or received the metaphoric knock as portrayed by the source code author)
  4. This level processes TCP packets.
  5. The node currkr->knock_port_idx  should reach the value 5 to clear the level.

Alright, let’s collect the pieces together : every time, the next port in queue receives a TCP knock, the node currkr->knock_port_idx  will be incremented, by the time we’ve knocked all the 5 ports, the node currkr->knock_port_idx  will hold the value 5, which will enable us to clear the first level.

Simply put, we need to send 5 TCP knocks, in the right order, to every port in the array {4511, 4527, 4539, 4552, 4538} .

Knock Knock

The following python code, does just that :

Now we can move on to level 2.

  • Tackling the second level

Now, the second level in itself contains an if branching, that divides it into two consecutive stages.

The first stage is contained in the following block of code :

This code starts by verifying if we actually got past the first level in the first three line.

But first, let’s review quickly a piece of code way up in the source code :

By reading this, we can only guess that the module is expecting an UDP packet from the port 4777.

So let’s focus more on the following block of code :

 

This code gets data from the received UDP packet, stores it in the variable key1 , and then computes key1 ^ XORK1 . To clear this first stage of the level 2, we must send an UDP packet containing a value key1  that fulfils this condition MAGIC1 == key1 ^ XORK1 .

Let’s replace the constants by their values to make it simple : 0x12F9BC11 == key1 ^ 0xdeadb00b .

Obviously, we need to guess  key1 so we can send it and clear this stage. How can we do that ? Well, the first thing that pops to my mind is brute force… but I hate brute force, it’s usually slow and dull.

Is there a better way ? Well yes of course ! Let’s just recall the simplest rules of boolean algebra :

A B A ^ B
1 1 0
1 0 1
0 1 1
0 0 0

From this XOR table, we can recall these two simple rules :

  1. A ^ A will always equal 0
  2. A ^ 0 will always equal A

Hence, using these two rules, let’s solve this simple equation:

        key1 ^ XORK1 = MAGIC1

=>    key1 ^ XORK1 ^ XORK1 = MAGIC1 ^ XORK1

=>    key1 ^ 0 = MAGIC1 ^ XORK1   ( because XORK1 ^ XORK1 = 0 )

=>    key1 = MAGIC1 ^ XORK1   ( because key1 ^ 0 = key1 )

=>    key1 = 0xdeadb00b ^ 0x12F9BC11

And we got it.

Now, all we need to do is send the value 0xdeadb00b ^ 0x12F9BC11 to the UDP port 4777.

The following python lines will do just that :

Moving on to the second stage of the second level, and that is the following block of code :

Let’s focus again on this particular part of the code :

Before we start analysing this part of the code, let’s scroll just a bit upward, to this part :

We can see here that vals , is also a variable, or more likely an array that holds data received for the UDP port 4777.

Now that we cleared that up, let’s go back to analysing our second stage block of code. In this part, the module is waiting for 11 values, stores in the array vals, that need to verify some conditions.

The challenge here, is that these values depend on one another. But to make it easier for us, let’s try to sum up these conditions, and let’s suppose that these values are called b1,b2,b3,b4,b5,b6,b7,b8,b9,b10 and b11.

After reading the code, we can summarise the conditions that it expects in the following formulas or equations :

  1. b3 = b1 ^ b2
  2. b6 = b4 & b5
  3. b11 = b6 ^ b3 ^ b7 ^ b8 ^ b9 ^ b10 

After fulfilling these conditions, our IP address will be whitelisted i = whitelist(currip);  and then we can connect to the port where can get the shell.

As the lazy guy I am, the first thought that came to me, is try 0 for all these values.

So let’s see *fingers crossed*

  1. b3 = 0 ^ 0 = 0
  2. b6 = 0 & 0 = 0
  3. b11 = 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 = 0

aaand there we go, so next up, we need to send 11 zeros, to the UDP port number 4777. That can be done using this python line of code :

and then add these lines to connect to the TCP port 9999 that will enable us to interface with the shell :

Finally, here are all the pieces of the python exploit, brought together :

Let’s test it quickly :

Screen Shot 2016-07-07 at 12.45.40 AM

And we got the shell 😀 and we beat the green monster 😀

Thanks for reading, I hope I was clear and my logic isn’t flawed.

Any remarks, suggestions, corrections, critiques, or anything, are welcomed in the comment section.

Later !

4 thoughts on “A study in green

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy This Password *

* Type Or Paste Password Here *