ECE9609 - assignment 2, PicoCTF buffer overflow challenges
Assignmnet 2
Starting from assignment 2, I’ll use a Kali Linux VM to do the homework.
Question 1 - File Permissions
Following the steps:
Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!ssh col@pwnable.kr -p2222 (pw:guest)
Login pwnable’s server through port 2222:
Checking the file permissions:
The answers to the questions:
-
Which user owns the file
col
?col_pwn
owns the file col. -
Which files in this directory can the users in the group
col
read from?col
andcol.c
can be read by the users in groupcol
. Current user is in the groupcol
, so the current user canr-x
, which are reading and executing to the file.col.c
has been set that everyone is readable(r--
). -
What does the SUID flag do?
SUID
is a type of special permissions, files withSUID
permission could be excuted as if the owner is excuting it.s
indicates the file hasSUID
permission.In my opinion,
SUID
is normally used by the command, for example: while a none root user change its password, he need to use commandpasswd
, which hasSUID
permission to modify the password file(/etc/passwd
).
-
What exactly does
-r-sr-x---
tell us about the filecol
? Be sure to explain who is allowed to do what.in
-r-sr-x---
, the first-
tells us it is a file.For the owner of the file:
r-s
is the permissions the owner has, meansread
andSUID
, allow other user execute it with the owner’s permission.For the users in group
col
: they haver-x
permission, which isread
andexcute
.For current logged in user col: The current user is in the group
col
, so he canread
andexcute
the file.For other users(root not included): no permission allowed
The following pictures I found at http://www.csit.parkland.edu/~smauney/csc128/permissions_and_links.html and https://pamirwebhost.com/check-linux-file-permissions-with-ls/ are very helpful with leaning the linux file permissions:
Question 2 - Basics of C
Steps:
Create the source file by using vi flag.c
and paste the code into the file:
Then save the file (:wq
):
Compile the source code by gcc flag.c -O ex_flag
, compile and save the binary to ex_flag
and finally, execute it:
So for the questions:
-
What is the flag?
The program outputs
Here's your HINT
, and theflag = HINT
. -
What command(s) did you use to compile and run this program?
I used
gcc
to compile the source code. After compiling it, the output file will automatically has the execute permission, so I execute using./theFileName
.
Question 3 - Basics of Computer Memory
What I understand about memory:
-
in a X86 32-bit system, every process has a 32-bit long virtual address and it has an offset compared to the physical address.
-
in a X86 32-bit system, virtual address space is allocated to two part, kernel space and user space. Kernel space normally takes up to 1G space at high address, the rest is user space which is 3G.
-
Each process has the following memory space:
-
Source: https://www.cnblogs.com/chaozhu/p/6377485.html
On linux system, we can take a look at the memory map:
And at the bottum of the memory is stack:
What I understand about endianness:
Endianness defines how a operating system read memory, from high address to low address or the oppsite order.
Answers to the questions:
-
What is the number 3735928559 in hexadecimal form?
0xDEADBEEF (I used an online converter)
-
Suppose this number was stored as an integer (i.e.,
int
type) in little-endian format at memory address 0x12345678. Fill in the following memory map showing where each byte is stored. If the value is unknown/not relevant, leave it as0x??
.First of all, little-endian means the last byte of the data is stored at the lowest address. In terms of
0xDEADBEEF
, the least significant isEF
(the last). Because the number is stored at memory address0x12345678
, the start of the memory is0x12345678
(lowest address). So,EF
should be placed at address0x12345678
.
Address | Value ------------------------- ... | 0x12345674 | 0x?? 0x12345675 | 0x?? 0x12345676 | 0x?? 0x12345677 | 0x?? 0x12345678 | 0xEF 0x12345679 | 0xBE 0x1234567a | 0xAD 0x1234567b | 0xDE
Question 4 - Collision Challenge
First login the server:
ssh col@pwnable.kr -p2222
After login, I checked the directory using ls
, I find that there’s a file called flag, it’s possible where the flag is. My mission is to access the file. Then I checked flag file’s permission using ls -l
.
So, flag is owned by col_pwn. Before I tried other stuff, I checked if my current user is on the sudoer list using sudo cat flag
.
But I’m not a sudoer. I have a new idea while dealing with sudo, on January 26, 2021, someone found sudo has a buffer overflow bug, which is exploitable by any user on the machine. So, I look this up on Google and find this article https://www.sudo.ws/alerts/unescape_overflow.html. Here’s a test to check if the machine has the bug:
To test whether your version of sudo is vulnerable, the following command can be used:
sudoedit -s /
A vulnerable version of sudo will either prompt for a password or display an error similar to:
sudoedit: /: not a regular file
A patched version of sudo will simply display a usage statement, for example:
usage: sudoedit [-AknS] [-a type] [-C num] [-c class] [-D directory] [-g group] [-h host] [-p prompt] [-R directory] [-T timeout] [-u user] file ...
If the sudoers plugin has been patched but the sudo front-end has not, the following error will be displayed:
sudoedit: invalid mode flags from sudo front end: 0x20002
Here’s what i find:
And the OS has been patched.
Next, I opened col.c to see the code using vi col.c
:
So, the question became how to make 0x21DD09EC
equal to check_password(INPUT)
.
After reviewing the check_password()
function, it takes the char input, force convert to int, copy it to the location of pointer ip
, then add them together like numbers. From the notice, I know the input is 20-bytes long. And one int takes up 4 bytes, so it’s 20/4=5 numbers I need know.
Now I convert 0x21DD09EC
to decimal(568134124
) to figure out the 5 numbers. However, 568134124 cannot be divided by 5.
So, I just take the integer part 113626824
and mutiply it with 4 (I also could set the first 4 number as 113626825, the rest is the same) , 113626824 * 4 = 454507296
. Now I have 4 numbers, the rest value would be the fifth number. 568134124 - 454507296 = 113626828
. Check if this is correct, 113626824 * 4 + 113626828 = 568134124
.
Now I have the 5 numbers: 113626824 - 113626824 - 113626824 - 113626824 - 113626828
In hex format: 0x06C5CEC8 0x06C5CEC8 0x06C5CEC8 0x06C5CEC8 0x06C5CECC
Finally, we input the numbers like the course website(https://whisperlab.org/introduction-to-hacking/lectures/collision):
./col $’\x06\xc5\xce\xc8\x06\xc5\xce\xc8\x06\xc5\xce\xc8\x06\xc5\xce\xc8\x06\xc5\xce\xcc’
Surprisingly, the answer is wrong:
I stucked here, so I googled. In this article https://0xrick.github.io/pwn/collision/#Exploitation, it says that the system is little-endian, I’m not sure where this is come from. I did not find anything related to this. So, I use a python program to check if the system is little-endian.
python -c "import sys;print(sys.byteorder=='little')"
And it is little-endian:
So, I need to reverse the input and try again:
./col $’\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06’
Eventually, I have the flag:
Flag: daddy! I just managed to create a hash collision :)
Question 5 - bof Challenge
The challenge asks me to download 2 files, so I did. And it seems bof
is a executable file, so I give it excute permission and run it:
Ok, so nc pwnable.kr 9000
is running the same program:
The next step is to analysis the source code, I use vim to see the code, vi bof.c
.
so, main()
calls func()
then exit, the logic is in function func()
. It has an integer input key
, then compare the key with 0xcafebabe
. Before comparison, the program requires a std input from keyboard and save it to a 32 bytes char variable overflowme
, so it’s possible to overflow variable overflowme
and write the data (0xcafebabe
) to the address where variable key
is. This will make the varibale key
equal to 0xcafebabe
and calls system("/bin/sh")
then enter a new bash. (gets()
function in C language does not validate the memory, it just wirte everything it gets to the memory address)
I’ll try to get the exploit on my local kali linux and then use it on pawnable.kr’s server. This time, I’ll only use gdb to debug the program, but I could also use a gun program on kali linux to debug it. In order to debug c program with GDB, I need to re-compile bof.c
with flag gcc -g3 -m32 bof.c -o new_bof
. -g3
means compile full debug information to the binary(It’s ok to use g3, because it’s a small program, while debug complex program, -g is much preferred). -m32
means compile it to 32-bit binary.
But it did not work:
It seems that I don’t have stdio.h
header, and this is impossible on a linux machine with gcc. So, I googled the error:
ok, I need to install 32-bit library manully. Kali linux use apt to manage the packages, so I only need to apt-get install gcc-multilib
to install mulilib.
Then run gcc to compile it again, and it worked just fine:
Great! GDB is not installed too:
After install GDB, I can finally debug the program:
Then I use run
to start the program and try something random just to play with it.
I take a look at the source code, I understand I need to set a breakpoint at the comparison to check the value in key
. (it’s just partial code in the picture)
Now run the program, I put a long string to overflow it:
So, the key’s value is replaced with 0xffffd180
, I tried again without overflow:
Now I know the buffer overflow could work on this program. So, I use an online tool generate a string(https://wiremask.eu/tools/buffer-overflow-pattern-generator/) which is very easy to use on buffer overflow. What I used here is Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
I could use the string to find where is target place.
I know it overflows the varibable at the start of Ab
. Next is to find the content of variable key
, then I know the place to overflow key’s data.
I learned to print the key as a sequence of 4 characters on the class tutorial notes.
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6A|KEY|b7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
The bold words are where key is. Now I can set any value I want to key.
So, the expoit string is Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6A + \xbe\xba\xfe\xca
.
Since it doesn’t matter what content is in the overflow variable, so I replace the long string with one char q
.
Now I have the overflow string, so I’ll use a python script to help me input it:
(python -c "print 'q'*52 +'\xbe\xba\xfe\xca'"; cat) | nc pwnable.kr 9000
This python script will input the data to pwnable.kr’s 9000 port. It’s like running a local program ./bof
. The difference is piped the data to a remote server.
Finally, I have the user col
‘s shell and use it to get the flag.
And the flag is daddy, I just pwned a buFFer :)