Buffer Overflow Attack(SEED Lab)

aayush malla
6 min readSep 20, 2020

--

Before diving into buffer overflow attack let’s first understand what is buffer overflow.Buffer overflow is the condition that occurs when a program attempts to put more data in a buffer than it can hold . In this case buffer denotes a sequential section of memory allocated to contain anything from a character string to an array of integers.Buffer overflow(writing outside the boundary of allocated memory) can corrupt data,crash the program or can cause the execution of malicious code.

Let’s understand the anatomy of stack .

fig1: Anatomy of Stack

Whenever a function is called then a stack is formed where at first parameters of the function are passed then the return address(where to direct the control after the completion of this function), previous frame pointer(frame pointer of calling function) and then local variables of the function. Current frame pointer(ebp) will point to the previous frame pointer.

Now let us suppose buf is of size 24 bytes now if we try to push data of size >24bytes then it will replace the previous frame pointer, return address and so on causing buffer overflow.

We can take advantage of this buffer overflow to run our malicious code. Here if we replace the return address of the function with the address where malicious code resides then the control will flow to the location of malicious code and thus malicious code will be executed.

Demonstrating Buffer Overflow Attack

We will be performing buffer overflow attacks on the SEED Lab . For this you need to download the Ubuntu 16.04(32 bits) VM ,exploit,vulnerable program available in SEED lab.

Link of the lab:

We need to construct an exploit program properly to generate the correct badfile that can cause buffer overflow. Badfile is input for the vulnerable program so when we run the vulnerable program then it will read badfile and thus buffer overflow will occur as we have constructed the badfile accordingly.

Here function bof has buffer overflow program So when main function call bof we can perform buffer overflow in the stack of bof function by replacing the return address in the stack.In bof we have buffer[24] so if we push more data .

Here is the basic exploit program provided to us.Here the buffer is initialized with NOP instructions.NOP instructions does nothing and just pass the control to the next byte. Now to perform buffer overflow we have to first place shell code inside the buffer now as we don’t know the location of shell code inside the buffer we need to replace the return address of the vulnerable program(bof) with the address containing NOP which will eventually pass the control to the location of shell code.

As we have a basic idea of what to do now we will learn how to do it.

Nowadays Operating systems have various countermeasures to prevent buffer overflow. For this session we will be turning off those countermeasures and then perform buffer overflow.Countermeasures we need to turn off are:

Address Space randomization : Ubuntu and several other Linux-based systems use address space randomization to randomize the starting address of heap and stack. This makes guessing the exact addresses difficult; guessing addresses is one of the critical steps of buffer-overflow attacks. In this lab, we disable this feature using the following command:

sudo sysctl -w kernel.randomize_va_space=0

The StackGuard protection scheme:The GCC compiler implements a security mechanism called Stack-Guard to prevent buffer overflows. In the presence of this protection, buffer overflow attacks will not work.We can disable this protection during the compilation using the -fno-stack-protector option.

Non executable stack: Ubuntu used to allow executable stacks, but this has now changed: the binary images of programs (and shared libraries) must declare whether they require executable stacks or not, i.e.,they need to mark a field in the program header. Kernel or dynamic linker uses this marking to decide whether to make the stack of this running program executable or non-executable. This marking is done automatically by the recent versions of gcc, and by default, stacks are set to be non-executable. To change that, use -z noexecstack .

Configuring/bin/sh: In both Ubuntu 12.04 and Ubuntu 16.04 VMs, the/bin/sh symbolic link points to the/bin/dash shell. The dash shell in Ubuntu 16.04 has a countermeasure that prevents itself from being executed in a Set-UID process. Basically, if dash detects that it is executed in a Set-UID process, it immediately changes the effective user ID to the process’s real user ID, essentially dropping the privilege.Since our victim program is aSet-UID program, and our attack relies on running/bin/sh, the countermeasure in/bin/dash makes our attack more difficult. Therefore, we will link/bin/sh to another shell that does not have such a countermeasure (in later tasks, we will show that with a little bit more effort, the countermeasure in/bin/dash can be easily defeated). We have installed a shell program

Called zsh in our Ubuntu 16.04 VM. We use the following commands to link/bin/sh to zsh.

sudo ln -sf /bin/zsh /bin/sh

Finding address: Now at first we need to create a temporary badfile as it is needed by vulnerable program to debug is properly.

touch badfile

Now lets compile the vulnerable program(stack.c)using command

gcc -o stack -g -z execstack -fno-stack-protector stack.c

Now run debugger using command and then set breakpoint at bof (setting breakpoint at bof as is vulnerable) and then run.

gdb stack

b bof

r

Now the program will stop at breakpoint.

Now in order to find the return address we need to find the address of ebp,as from figure we can see that return address is at ebp +4 byte( in 32 bit OS). After finding the address of ebp we need to find the starting address of buffer[24] which is buffer[0] so that we can calculate offset

p $ebp

p &buffer

p $ebp — p &buffer

fig2: address of buffer and ebp

We found that the outputs to be

$ebp = 0xbfffeb08

&buffer = 0xbffeae8

$ebp- &buffer = 0x20 = 32(in decimal)

As we know that the return address is at ebp + 4 byte as the size of frame pointer is 4 bytes so the return address is at 32+4=36 bytes from the buffer.

After determining the return address we replace the return address by something by ebp + offset where offset can be any value that will map it to the address containing NOP instructions that will eventually lead to the address containing shell code.

fig 3: adding code to exploit.c

We can also add the memory location using little Endian format which is

*(buffer+36) = 0x90;

*(buffer+37) = 0xeb;

*(buffer+38) = 0xff;

*(buffer+39) = 0xbf;

Note: 0xbfffeb08+0x88 = 0xbfffeb90

Here we have added 0x88 (128) into the ebp address . Here it is not necessary that we have to add 0x88 only we can add anything other than 4 as it will take it to the original return address then there will be no point replacing the return address with itself. As the exploit have buffer size of 517 among which 36 bytes are used by the stack and 25 bytes are used by shell code so we can add value in between it like 0x80 that will result in replacement of current return address by address containing the NOP codes.

Our final exploit code will look like this.

After doing that we add the shell code to the buffer using the command.

memcpy(buffer+sizeof(buffer)-sizeof(shellcode),shellcode,sizeof(shellcode));

Now after editing the exploit.c file we then compile the exploit.cc file using the command.

gcc -o exploit exploit.c

Now we remove dummy badfile and then we run the file by ./exploit and ./stack

Once we run ./stack we get the root access

fig3: root access

So by doing above mentioned steps we successfully perform buffer overflow attack and gain root access.

References

Download Ubuntu VM:

https://seedsecuritylabs.org/lab_env.html

--

--

aayush malla
aayush malla

Written by aayush malla

Data Engineer, Cybersecurity enthusiast , PLSQL, Data Analyst