Home >Backend Development >Python Tutorial >C programs and subprocesses
I wrote this simple c program to explain a more difficult problem with the same characteristics.
#include <stdio.h> int main(int argc, char *argv[]) { int n; while (1){ scanf("%d", &n); printf("%d\n", n); } return 0; }
It works as expected.
I also wrote a subprocess script to interact with the program:
from subprocess import popen, pipe, stdout process = popen("./a.out", stdin=pipe, stdout=pipe, stderr=stdout) # sending a byte process.stdin.write(b'3') process.stdin.flush() # reading the echo of the number print(process.stdout.readline()) process.stdin.close()
The problem is that if I run the python script, the execution freezes on readline()
. In fact, if I interrupt the script, I get:
/tmp » python script.py ^ctraceback (most recent call last): file "/tmp/script.py", line 10, in <module> print(process.stdout.readline()) ^^^^^^^^^^^^^^^^^^^^^^^^^ keyboardinterrupt
If I change my python script:
from subprocess import popen, pipe, stdout process = popen("./a.out", stdin=pipe, stdout=pipe, stderr=stdout) with process.stdin as pipe: pipe.write(b"3") pipe.flush() # reading the echo of the number print(process.stdout.readline()) # sending another num: pipe.write(b"4") pipe.flush() process.stdin.close()
I got this output:
» python script.py b'3\n' Traceback (most recent call last): File "/tmp/script.py", line 13, in <module> pipe.write(b"4") ValueError: write to closed file
This means that the first input was sent correctly and the read is complete.
I can't really find anything that explains this behavior; can anyone help me understand? Thanks in advance
[Edit]: Since there are many points that need clarification, I added this edit. I'm training on buffer overflow exploits using rop
techniques, and I'm writing a python script to achieve this. In order to exploit this vulnerability, I need to discover the libc
address and have the program restart without terminating due to aslr. Since the script will be executed on the target machine and I don't know which libraries are available, then I will use subprocess since it is built into python. Without going into detail, the attack sends a series of bytes
on the first scanf with the goal of leaking the libc
base address and restarting the program; then it sends the second payload to get a shell through which I will communicate in interactive mode.
that's why:
\n
: my payload will not align or may cause a failurestdin
openChange these:
Send separators between numbers read by a c program. scanf(3) accepts any non-numeric byte as a delimiter. For the simplest buffering, send newlines from python (e.g. .write(b'42\n')
). Without delimiters, scanf(3) will wait indefinitely for more digits.
Flush the output after each write (in c and python).
This worked for me:
#include <stdio.h> int main(int argc, char *argv[]) { int n; while (1){ scanf("%d", &n); printf("%d\n", n); fflush(stdout); /* i've added this line only. */ } return 0; }
import subprocess p = subprocess.popen( ('./a.out',), stdin=subprocess.pipe, stdout=subprocess.pipe) try: print('a'); p.stdin.write(b'42 '); p.stdin.flush() print('b'); print(repr(p.stdout.readline())); print('c'); p.stdin.write(b'43\n'); p.stdin.flush() print('d'); print(repr(p.stdout.readline())); finally: print('e'); print(p.kill())
The reason the original c program works correctly when run interactively in a terminal window is that in c, the output is automatically refreshed when a newline character (\n
) is written to the terminal. Therefore printf("%d\n", n);
will end up executing fflush(stdout);
implicitly.
The reason the original c program doesn't work when run from python using subprocess
is that it writes the output to a pipe (instead of the terminal) and does not automatically flush to the pipe. What is happening is that the python program is waiting for bytes and the c program is not writing those bytes to the pipe but it is waiting for more bytes (in the next scanf
) so both programs Both are waiting for each other indefinitely. (However, a partial auto-refresh will occur after a few kibs have been output (usually 8192 bytes). But a single decimal number is too short to trigger this operation.)
If the c program cannot be changed, then you should use terminal devices instead of pipes to communicate between the c and python programs. pty
The python module can create terminal devices, which worked for me with your original c program:
import os, pty, subprocess master_fd, slave_fd = pty.openpty() p = subprocess.popen( ('./a.out',), stdin=slave_fd, stdout=slave_fd, preexec_fn=lambda: os.close(master_fd)) try: os.close(slave_fd) master = os.fdopen(master_fd, 'rb+', buffering=0) print('a'); master.write(b'42\n'); master.flush() print('b'); print(repr(master.readline())); print('c'); master.write(b'43\n'); master.flush() print('d'); print(repr(master.readline())); finally: print('e'); print(p.kill())
If you don't want to send newlines from python, here is a solution without newlines that worked for me:
import os, pty, subprocess, termios master_fd, slave_fd = pty.openpty() ts = termios.tcgetattr(master_fd) ts[3] &= ~(termios.ICANON | termios.ECHO) termios.tcsetattr(master_fd, termios.TCSANOW, ts) p = subprocess.Popen( ('./a.out',), stdin=slave_fd, stdout=slave_fd, preexec_fn=lambda: os.close(master_fd)) try: os.close(slave_fd) master = os.fdopen(master_fd, 'rb+', buffering=0) print('A'); master.write(b'42 '); master.flush() print('B'); print(repr(master.readline())); print('C'); master.write(b'43\t'); master.flush() print('D'); print(repr(master.readline())); finally: print('E'); print(p.kill())
The above is the detailed content of C programs and subprocesses. For more information, please follow other related articles on the PHP Chinese website!