2

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

Luigi
  • 23
  • 4
  • Nothing you've shown tells me that the crash happens after strcpy rather than at return; can you show why you think this?. It's not strange that you don't see the printf output; stdout is buffered, and you may not see all the output if the program doesn't get a chance to flush the buffer. If you want to guarantee output, write to stderr. Also, as your compiler nicely points out, you have a format string vulnerability as well as a buffer overflow. – multithr3at3d May 24 '20 at 17:44
  • 1
    Also, what is the question? You are able to control the return address, so what's wrong? – multithr3at3d May 24 '20 at 17:50
  • I'm not able to control the ret-address because segentation fault happens as soon as strcpy finishes, if you see the dmesg output you can see that eip has not been overwritten by the input string, whereas esp has been and this gives the segmentation fault. If I would have control of the eip I should have eip overwritten by the input string at the return from the main. Also I have added the output from valgrid that shows that segmentation fault happens because of the esp being changed to point out of the addressable space...I debugged the binary and I saw the problem happens within the strcpy. – Luigi May 24 '20 at 18:52

1 Answers1

1

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.

multithr3at3d
  • 12,355
  • 3
  • 29
  • 42
  • 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.. – Luigi May 24 '20 at 21:13
  • @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` – multithr3at3d May 24 '20 at 21:52
  • I think I figured it out; see edits. – multithr3at3d May 25 '20 at 00:52
  • 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. – Luigi May 25 '20 at 08:37