2

I have a simple C program that asks for input and echoes it back, so essentially a gets and then a printf. I want to call this program through Python subprocess and capture the output - even when the C program crashes (trying to overflow the buffer from Python).

Everything works when I respect the buffer size, but as soon as I overflow I cannot capture stdout anymore. Running the program in a shell works fine, i.e. shows the output before crashing.

The C program (echo.exe):

#include <stdio.h>

void echo()
{
    char buffer[20];

    printf("Enter some text:\n");
    gets(buffer);
    printf("You entered: %s\n", buffer);
    printf("Some other output...\n");
}

int main()
{
    echo();
    return 0;
}

Compile via gcc echo.c -o echo.exe -m32.

The python program (exploit.py):

from subprocess import Popen, PIPE

payload = 32*"."
payload += "AAAA"

command = "echo.exe"

p = Popen([command], shell=True, stdout=PIPE, stdin=PIPE, stderr=PIPE)
stdout, stderr = p.communicate(input=payload)

print stdout, stderr

When I run python exploit.py the program crashes and does not print anything. If you give it a smaller payload to not overflow the buffer, it captures/prints stdout OK and does not crash.

What I want: Call my python script, crash echo.exe AND get the stdout printed.

I could get what I want if I just pipe the input to echo.exe in Powershell. This will print the output, then crash:

python -c "print '.'*32 + 'AAAA'" | .\echo.exe

Output:

Enter some text:
You entered: ................................AAAA
Some other output...

Question: How can I call echo.exe within a python script, crash it AND get the output? And why does it not work in the above script (I could assume the buffer is not flushed by the crashing C program)?

This is on a Win 8.1, 32-bit, and Python 2.7. It is purely an exercise of curiosity, so if I need to modify the C program and/or the python script I'm fine with that (rather the python script though). I just want to understand the why and maybe get ideas about possible ways to do it.

Thanks!

user1683766
  • 189
  • 1
  • 11
  • Do you need the output in variables? `command = "./echo.exe >&2"` with `p = Popen([command], shell=True, stdout=PIPE, stdin=PIPE)` shows output on the console on MacOS. – cdarke Feb 07 '19 at 13:55
  • 2
    Your echo program is using a buffered stream, which won't get flushed if it crashes. Make `stdout` unbuffered at startup via `setvbuf(stdout, NULL, _IONBF, 0)`. – Eryk Sun Feb 07 '19 at 21:26
  • @eryksun Great, that works. Thank you very much! As a follow up: is there generally no way to control this behaviour from outside the program (os level)? – user1683766 Feb 07 '19 at 22:18
  • 1
    There's no general way in Windows. Linux has the `stdbuf` program for this, which uses the dynamic linker's `LD_PRELOAD` hook to inject a shared library that modifies C standard I/O buffering. – Eryk Sun Feb 07 '19 at 22:29
  • 1
    Linux can implement something like `stdbuf` because there's one common C runtime (glibc) with global symbols. Windows processes typically have multiple CRTs, and symbols have to be explicitly imported and exported between modules. So even if there were a hook like `LD_PRELOAD`, the injected DLL would need a dependency on the specific C runtime library. It's more feasible now that we have the Universal CRT as a Windows component. – Eryk Sun Feb 07 '19 at 22:38

0 Answers0