[Pwnable.kr writeups] An attempt to solve Toddler’s bottle

Hello, again. As you probably may know, one of the things that security people enjoy doing in their free time, is play CTFs for fun, or to widen their knowledge and sharpen their skills. I’ve had my share of CTFs too, and in this humble article, I’ll try to publish writeups for the challenges published in Pwnable.kr.

I’ll be updating this article every time i succeed in solving a challenge.

So here it goes.

fd – 1 pt:

In every challenge, there’s a small [somehow funny] hint. In this challenge that hint would be : “Mommy! what is a file descriptor in Linux?”. Evidently, the challenge would be something about a file descriptor: a mistake, vulnerability, a bug, or just something to see if we understand file descriptors.

Side note : A file descriptor in linux, is a handle used to access an input/output resource (file, pipe, network socket, stdin, stdout…) and it’s typed as an integer in C.

Alright, let’s connect to the console and check the source code :

In the line  int fd = atoi( argv[1] ) - 0x1234; , the variable fd  takes the first argument passed to the program, and substracts from it the hexadecimal value 0x1234 . Later on, the program reads data from the file descriptor fd  and puts it in the variable buf , and then it checks: if the value of buf  equals to “LETMEWIN”, the program will read for us the flag file.

So obviously, we need to control the content of the variable buf , and put in it the string “LETMEWIN”; remember that we do control the fd  variable, and we can make the read()  function, read data from our own file descriptor.

The easiest way to control fd , that instinctively comes to mind, is to make it read input from stdin (i.e the keyboard), and we know that the keyboard’s file descriptor is simply 0, hence all we have to do, is pass the hexadecimal value 0x1234 (equals to 4660 in decimal) as an argument, upon launching the program.

Let’s try that :

And we got our flag 🙂

collision – 3 pt

Moving on to the next level, and from the hint “Daddy told me about cool MD5 hash collision today.”, it looks like we should find a collision of some kind of hash.

Let’s connect to ssh and go straight to the source code :

We can see that the program takes an argument, passes it to the function check_password() , and then compares the result to hashcode , before reading us the flag.

Simply put, in order to read the flag, we need to give the program a passcode that, if hashed with the function  check_password(), will be equal to the value  0x21DD09EC .

Let’s try to understand a bit the function   check_password() :

Walter cooking up the answer

First of all, we need to notice that the function takes a pointer to an array of chars, and then casts it or converts it to a pointer of an integers array. Why is that important ? Well, we know that a char’s size is one byte, and an int’s size is 4 bytes; so the variable ip  will hold a pointer to a block of 4 chars. For example, if p  originally points to an array of 20 chars, when casted to int*, the variable ip  will now hold a pointer to an array of 5 blocks of 4 “chars” or bytes, that will be interpreted as one integer.

Then, in the for loop, the program calculates the sum of those 5 integers, and returns the result.

So, if we want to pass this level, we need to provide the program with 20 bytes, which will be then converted to 5 blocks of integers, and their sim should be equal to   0x21DD09EC .

To accomplish that, here’s what I did :

I subtracted 4 from each byte, i.e 0x21DD09EC-0x04040404 = 0x1DD905E8

By doing this, I got 8 bytes that would sum up to our hashcode value, but we need 20 bytes… Well, I’ll just divide the 0x04040404  by 4, which will give me :  0x21DD09EC = (0x01010101 * 4) + 0x1DD905E8 .

Since the system is little endian, I need to inject the code inverted. Let’s try that :

And voila 🙂 we got the flag. That was easy wasn’t it !

bof – 5 pt

The writeup of this level was a bit long, so I wrote it on its own article, check it out here.

flag – 7 pt

Apparently, in this level we have to reverse a binary to get our flag. The hint says “Papa brought me a packed present! let’s open it.”, the important keyword here is packed. So may be the binary is packed, which makes the challenge double layered, we need to unpack, and then try too reverse.

Side note : A packer is usually used to compress and/or encrypt a binary, to make the binary lightweight and more usually hard to reverse. I see packers used mostly on malwares, to make them hard for researchers to understand their internals, and hard for an antivirus to detect.

First of all, let’s check if it indeed packed with any of the known packing tools. We’ll use the strings command line, to extract the strings contained in the binary (duuh!) :

If you scroll up and down through that garbage output, you’ll notice some strings that show that this thing is indeed packed with a UPX packer. Luckily, there’s a tool that can unpack this thing automatically, without us having to do any manual unpacking (which was the same tool used to pack it; Awesome!).

Let’s go ahead and unpack the present daddy gave us :

Alright, after unpacking, we can now reverse the binary without much headaches. When you launch the executable as you can see, it’s nice enough to tell us where it’s putting the flag.

Let’s fire up gdb and hash this thing. Here’s a result of disassembling the main function :

We can see that there’s indeed a call to malloc() , and we know that malloc()  returns the address of the allocated memory and puts it in the register EAX, since we’re on a 64bit, it’ll be in the register RAX.

But the thing is, I don’t see where the call to strcpy()  is, so I will try and guess that it’s in the line  callq 0x400320 . Let’s try that, and put a break point at the address   0x0000000000401195 :

Let’s run the program, and check what we’ll find in the register RAX:

So it does contain an address 0x6c96b0 , and the address points to a void part of the memory, that was initialized by malloc.

Let’s step to the next instruction, and check the contents of that address again :

Tada, we got our flag 🙂 It does actually sound like a delivery service hah !

passcode – 10 pt

The writeup of this level was a bit long, so I wrote it on its own article, check it out here.

random – 1 pt

I know, this is only one point, but bare with me. The writeup of this level was a bit long, so I wrote it on its own article, check it out here.

input – 4 pt

The writeup of this level was a bit long, so I wrote it on its own article, check it out here.

leg – 2 pt

In this level, we have two files to analyze so we can clear the level’s executable : a C file, and a complementary ASM file, containing the disassembly of the program in the ARM architecture.

Let’s start with the C file. We have 3 functions from key1()  to key3() that return values calculated in the inline assembly code. We’ll clear this level, if we provide a key that equals the sum of the values returned by the 3 functions.

Let’s start with the first function key1()  :

It seems like the register we’re gonna work with is r3 . In this function, the instruction executed moves the value of the  pc  register to r3 . Let’s see in the docs what value pc  holds normally in an ARM architecture :

In ARM state, the value of the PC is the address of the current instruction plus 8 bytes.
In Thumb state:
  • For B, BL, CBNZ, and CBZ instructions, the value of the PC is the address of the current instruction plus 4 bytes.
  • For all other instructions that use labels, the value of the PC is the address of the current instruction plus 4 bytes, with bit[1] of the result cleared to 0 to make it word-aligned.

Let’s assume that we are in ARM state, the PC register in this case holds the address of the current instruction plus 8 bytes, which means the address of the instruction after the next instruction. We’re going to use the ASM file now to see where’s that, specifically the key1()  disassembly :

From this, we can conclude that pc  holds the value  0x00008ce4 , which means r3  contains now  0x00008ce4 . Hence key1()  returns  0x00008ce4 .

Let’s see for key2() :


We can see that again, the value of pc  is moved to r3 , and then we add 0x4 to r3 . Let’s look at the disassembly :

We can conclude that pc  will hold the value  0x00008d08. In the end r3  will hold the value  0x00008d08+0x4 , which means key2()  will return the value  0x00008d0c .

Let’s move on to the final function key3()  :

Let’s see what lr  hold normally :

LR, the Link Register
Register R14 is used to store the return address from a subroutine. At other times, LR can be used for other purposes.

As you can see from the definition, lr  holds the return address; since key3()  was called from the main()  function, let’s look at main()’s disasembly, and see from where was key3() called :

We can conclude that lr  contains the value  0x00008d80 , so key3()  will return the same value.

Let’s calculate the some of these 3 values :

Seems like the key expected from us is 108400 , let’s try it :

It worked 🙂 !

mistake – 1 pt

Well the hint says something about operator priority or precedence. It’s as easy as that, you just have to spot where that flow is, and it turns out it’s in line 17 :

As you can see in the precedence table (http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm) the relational operator < has more priority than than the assignment operator =

By that logic, we can conclude that in this line if(fd=open("password",O_RDONLY,0400) < 0) , comparison will be executed first, and then fd will be assigned the value 0, which is in fact the file descriptor for the standard input  stdin .

Since fd now is the stdin file descriptor, later on, the read function will actually wait for an input from the keyboard before it even asks for the actual passwor. So all you have to do is enter 0000000000 before the program asks for the password. When the prompt asks you for the password, you just give it 1111111111. Tada you got your flag

2 thoughts on “[Pwnable.kr writeups] An attempt to solve Toddler’s bottle

  1. Hi, thanks for sharing, i’m try to understand collision puzzle, why did you substract 4 form hascode?

    Why if I do hascode/5 = 06c5cec8 and pass this 5 times to ./col got a wrong answer?

    Could please give me some advice?


    1. Hello friend ! Thanks for reading this 🙂

      As for your question, 4 is the remainder of the division, that why I subtracted 4.

      If you multiply 5 times 06c5cec8 you’ll get 06c5cec8*5 = 21dd09e8, notice that 21dd09e8 is 4 away from the hashcode i.e :
      (hashcode – 21dd09e8) = 4
      And that’s why your proposed answer didn’t quiet work 🙂

      I hope my explanation was clear, feel free to ask me again if you still feel confused or stuck 🙂

      Have a great one !

Leave a Reply

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