Information Security
buffer-overflow exploit-development rop
Updated Mon, 22 Aug 2022 11:57:43 GMT

segmentation fault at strcpy while perforforming a buffer overflow


I have this code that I need to use to perform a ret2libc

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    char buf[256];
    printf("buff is at:%p\n",buf);
  printf("%s",argv[1]);
  strcpy(buf, argv[1]);
  printf(buf);
}

I compile it as gcc -m32 -fno-stack-protector ./rt2.c -ort2 and than start it with a cyclic patter (generated with pwntools) as follows:

./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac

I was expecting a segmentation fault at the end of the main...it instead happens at the end of the strcpy...actually what happens (by looking with gdb) is that at the end of the strcpy the ESP points to 0x6361616e which is part of the input string.

Another strange thing is that printf before the strcpy does not print out anything

this is the result of the execution:

$ ./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
buff is at:0xffffcf20
Segmentation fault (core dumped)
$ dmesg | tail -1
[ 4432.704356] rt2[3280]: segfault at 6361616e ip 00000000565555e0 sp 000000006361616e error 4 in rt2[56555000+1000]

Even more strange, to me, if I comment the strcpy the printf before of it does instead print out...

I tried to execute it with with valgrind and it confirmed that at some point ESP got overwritten.

valgrind ./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
==2735== Memcheck, a memory error detector
==2735== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2735== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2735== Command: ./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
==2735== 
buff is at:0xfeffce80
==2735== Warning: client switching stacks?  SP change: 0xfeffcf98 --> 0x6361616e
==2735==          to suppress, use: --max-stackframe=1684115926 or greater
==2735== Invalid read of size 4
==2735==    at 0x1085E0: main (rt2.c:20)
==2735==  Address 0x6361616e is on thread 1's stack
==2735== 
==2735== 
==2735== Process terminating with default action of signal 11 (SIGSEGV)
==2735==  Access not within mapped region at address 0x6361616E
==2735==    at 0x1085E0: main (rt2.c:20)
==2735==  If you believe this happened as a result of a stack
==2735==  overflow in your program's main thread (unlikely but
==2735==  possible), you can try to increase the size of the
==2735==  main thread stack using the --main-stacksize= flag.
==2735==  The main thread stack size used in this run was 8388608.
--2735-- VALGRIND INTERNAL ERROR: Valgrind received a signal 11 (SIGSEGV) - exiting
--2735-- si_code=1;  Faulting address: 0x6361616E;  sp: 0x82d8cf20
valgrind: the 'impossible' happened:
   Killed by fatal signal
host stacktrace:
==2735==    at 0x5803F9F6: ??? (in /usr/lib/valgrind/memcheck-x86-linux)
sched status:
  running_tid=1
Thread 1: status = VgTs_Runnable (lwpid 2735)
==2735==    at 0x482A4D0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-x86-linux.so)

I also tried to compile with as: gcc -g -m32 -fno-stack-protector -z stack-size=4194304 ./rt2.c -ort2 but nothing changed




Solution

Edit: I was able to replicate what you saw in a debugger at first. I set a breakpoint at strcpy, and trying to step over it would result in a segfault. However, if I set a breakpoint on the instruction after strcpy, I do not get the segfault until the end of main:

  0x56556239 <main+144>       lea    esp, [ecx-0x4]
   0x5655623c <main+147>       ret

So I suspect there is some oddity going on with the debugger in this case that gave a false assumption about where the crash was happening. It's clear now that it was overwriting the address that is eventually referenced here. I tried different sizes for the overflow, and eventually found one that got me a crash that bypassed this and gave control over EIP; a size of 268 seems to work.

Original Answer: Considering that the segfault occurs during strcpy and you are thus unable to overwrite the return address, I think the problem may require a different approach. The presence of printf(buf); seems like this is supposed to be solved by exploiting a format string vulnerability. It may allow you to overwrite nearly any arbitrary address. If the stack address pointing to the saved return pointer is on the stack, you may be able gain control by writing to it.

I'm not exactly sure why the segfault is happening where it is, but I suspect the stack layout may simply be unfavorable for hijacking execution flow with a buffer overflow. It is "undefined behavior", after all.





Comments (4)

  • +0 – Let's say that the code was meant to demonstrate a BoF and later, after enabling stack canary, how to bypass by using the format string. I made different BoFs before, but I was especially surprised by this behaviour that I cannot understand. Of course behaviour is "undefined" but not random, thus there must be a motivation and I'm not able to get it. The strcpy should have its own stack frame above (actually below) the one of the main and the overflowed buffer is in the main stack frame, thus overflowing it should not affect the strcpy frame. That's why I cannot figure out how ESP can change.. — May 24, 2020 at 21:13  
  • +0 – @Luigi well, I didn't bother to debug it that far, but even though they are separate stack frames, strcpy is responsible for restoring the previous state of the esp and ebp — May 24, 2020 at 21:52  
  • +0 – I think I figured it out; see edits. — May 25, 2020 at 00:52  
  • +0 – I also had a similar experience: if you breakpoint at strcpy and step over, it gives you the segmentation fault, nevertheless if you breakpoint at strcpy and step into and go ahead step-by-step it goes ahead without errors until the end... so you think it could be an error of the debugger? ...possible...thanks a lot!!! At the moment this is the only possible answer. Cheers. — May 25, 2020 at 08:37