1

So I am trying to complete a buffer overflow exercise. The code I am trying to exploit is below. What I want to be able to do is to insert my own print statement through the overflow attack. What I would like to do, is to run the attack through $ cat file | code.c

#include <stdio.h>
#include <string.h>
#define INPUT_BUFFER 256 /* maximum name size */
/*
* read input, copy into s
*/
void getl(char *s)
{
    int c;
    while ((c=getchar()) != EOF)
        *s++ = c;
        *s = '\0';
    }
void removenl(char *s)
{
    int l;
    l = strlen(s);
    while (l--)
        if (s[l] == '\n')
            s[l] = '\0';
}

int main()
{
    char target[INPUT_BUFFER];
    getl(target);
    if (strlen(target) < INPUT_BUFFER) {
        removenl(victim);
        printf("%s is the target\n", target);
    }
return 0;
}

I know the attack would happen at the "char target[INPUT_BUFFER];" point. If we put for example 257 'a' it would generate a BOF.

The disassembly at the current moment looks like this.

   0x0000000000400673 <+0>:     push   %rbp
   0x0000000000400674 <+1>:     mov    %rsp,%rbp
   0x0000000000400677 <+4>:     sub    $0x100,%rsp
=> 0x000000000040067e <+11>:    lea    -0x100(%rbp),%rax
   0x0000000000400685 <+18>:    mov    %rax,%rdi
   0x0000000000400688 <+21>:    callq  0x4005ed <getline>
   0x000000000040068d <+26>:    lea    -0x100(%rbp),%rax
   0x0000000000400694 <+33>:    mov    %rax,%rdi
   0x0000000000400697 <+36>:    callq  0x4004b0 <strlen@plt>
   0x000000000040069c <+41>:    cmp    $0xff,%rax
   0x00000000004006a2 <+47>:    ja     0x4006cc <main+89>
   0x00000000004006a4 <+49>:    lea    -0x100(%rbp),%rax
   0x00000000004006ab <+56>:    mov    %rax,%rdi
   0x00000000004006ae <+59>:    callq  0x400623 <removenewlines>
   0x00000000004006b3 <+64>:    lea    -0x100(%rbp),%rax
   0x00000000004006ba <+71>:    mov    %rax,%rsi
   0x00000000004006bd <+74>:    mov    $0x400764,%edi
   0x00000000004006c2 <+79>:    mov    $0x0,%eax
   0x00000000004006c7 <+84>:    callq  0x4004c0 <printf@plt>
   0x00000000004006cc <+89>:    mov    $0x0,%eax
   0x00000000004006d1 <+94>:    leaveq 
   0x00000000004006d2 <+95>:    retq  

The stack without the buffer filled looks like this.

0x7fffffffded0: 0xf7ffe1c8      0x00007fff      0xf7de4961      0x00007fff
0x7fffffffdee0: 0x00000000      0x00000000      0xf7ff7a10      0x00007fff
0x7fffffffdef0: 0x00000001      0x00000000      0x00000000      0x00000000
0x7fffffffdf00: 0x00000001      0x00007fff      0xf7ffe1c8      0x00007fff
0x7fffffffdf10: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdf20: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdf30: 0x00000000      0x00000000      0xf7ffe520      0x00007fff
0x7fffffffdf40: 0xffffdf70      0x00007fff      0xffffdf60      0x00007fff
0x7fffffffdf50: 0xf63d4e2e      0x00000000      0x00400391      0x00000000
0x7fffffffdf60: 0xffffffff      0x00000000      0xffffe0c8      0x00007fff
0x7fffffffdf70: 0xf7a251f8      0x00007fff      0xf7ff74c0      0x00007fff
0x7fffffffdf80: 0xf7ffe1c8      0x00007fff      0x00000000      0x00000000
0x7fffffffdf90: 0x00000001      0x00000000      0x0040072d      0x00000000
0x7fffffffdfa0: 0xffffdfd0      0x00007fff      0x00000000      0x00000000
0x7fffffffdfb0: 0x004006e0      0x00000000      0x00400500      0x00000000
0x7fffffffdfc0: 0xffffe0b0      0x00007fff      0x00000000      0x00000000
0x7fffffffdfd0: 0x00000000      0x00000000      0xf7a36f45      0x00007fff
0x7fffffffdfe0: 0x00000000      0x00000000      0xffffe0b8      0x00007fff
0x7fffffffdff0: 0x00000000      0x00000001      0x00400673      0x00000000
0x7fffffffe000: 0x00000000      0x00000000      0x5f571179      0x81bce2d3
0x7fffffffe010: 0x00400500      0x00000000      0xffffe0b0      0x00007fff

With the buffer filled, it looks like this.

0x7fffffffded0: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdee0: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdef0: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf00: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf10: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf20: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf30: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf40: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf50: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf60: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf70: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf80: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdf90: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdfa0: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdfb0: 0x61616161      0x61616161      0x61616161      0x61616161
0x7fffffffdfc0: 0x61616161      0x61616161      0x61616161      0x37636161
0x7fffffffdfd0: 0x30343630      0x00003030      0xf7a36f45      0x00007fff
0x7fffffffdfe0: 0x00000000      0x00000000      0xffffe0b8      0x00007fff
0x7fffffffdff0: 0x00000000      0x00000001      0x00400673      0x00000000
0x7fffffffe000: 0x00000000      0x00000000      0x5f571179      0x81bce2d3
0x7fffffffe010: 0x00400500      0x00000000      0xffffe0b0      0x00007fff

I know to make this attack work, I need to work to overwrite a return address, and then put my own shell code in there.

I have put what I think would be the shellcode payload in to GDB and have gotten a shellcode representation of it.

#include <stdio.h>
void main() {
    printf("Now i've pwn your computer");
    return 0;
}



\xff\x25\x12\x0c\x20\x00\x68\x00\x00\x00\x00\xe9\xe0\xff\xff\xff\x55\x48\x89\xe5\xbf\xa4\x05\x40\x00\xb8\x00\x00\x00\x00\xe8\xe6\xfe\xff\xff\x90\x5d\xc3\x0f\x1f\x00

How do I actually take this shellcode and make it an exploit? I guess I am confused on where to input it, and how to make the program naturally run it. Let me know if you see anything, or if you can give me some helpful hints in the right direction.

Thanks.

  • Did you disable ASLR, stack canaries and other mitigations for buffer overflow attacks that modern operating systems usually come with? – Tobi Nary Apr 28 '18 at 19:30
  • Yes, this is run on a linux environment. ASLR is disabled, well atleast I think so. The address in the stack printout is always exactly the same. , I also have -fno-stack-protector set to turn off stack-canaries. – Joe Hopper Apr 28 '18 at 19:35
0

While this is generally a rather broad question, I‘ll try to answer in a general approach:

If you disabled the usual suspects (stack canaries, ASLR, no-exec-bit,…), the general approach is as follows:

You check how much space you have to fill before the buffer overruns and fill that space with arbitrary input (I personally prefer NOPs to create a NOP slide, just in case).

Be sure to have your payload at the end of that NOP slide and before you are overwriting the return address.

For example, if the return address is the last 4 bytes of 1000 bytes and your payload is 100 bytes, you put 896 NOPs followed by your payload, followed by an address within the NOP slide.

You can look up the hex value for the architecture you‘re using online.

I personally like to either:

  • use python to generate the required input or
  • write the hex values manually into a text file and generate a binary that can be fed as input like you described using cat and a pipe.

To use the second option as you asked, you can generate the necessary binary file by using

xxd -r -p textfile > file

Where textfile is your file containing a string of hex values like

 aaaaaaaaaaaaaaaff25…
  • Just curious, for the shellcode portion, should it be just the printf and return statement, or should it be the entire main function? – Joe Hopper Apr 28 '18 at 20:36
  • Just the opcodes you want to execute. So in this case, a few pushes and a call. – Tobi Nary Apr 28 '18 at 21:09
  • Just got it working! Thanks for the hints and tips! The return address was crucial to getting it! – Joe Hopper Apr 30 '18 at 2:44
0

Whenever you thinking about executing a buffer overflow, you need to think about how to control the execution pointer (EIP/RIP) first.

The execution pointer is saved in the stack frame whenever a function is executed. When the function returns, it retrieves the execution pointer from stack and resumes execution (there are more to it, but I`m oversimplifying so you can get the general idea)

You cannot look into the stack alone. You must look into the registers: stack pointer, base pointer and instruction pointer. This will tell you whether you successfully overrode EIP and with the expected values.

Also, you need to override EIP with the address of your payload, but in order to do that you need to know where your payload is. In this case, since it is a simple buffer overflow, it is in the stack. However, you need to either know the address of the stack beforehand OR search for a JMP ESP instruction in the code and point EIP there.

So in summary:

  • Control EIP
  • Make EIP point to stack
  • Add remaining payload to the stack

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.