Non-blocking read on subprocess output without flush() and new line - python

I have a program that I can't modify. This program print data to stdout WITHOUT flushing it or put \n inside and wait for input and so on.
My question is how can I, from a Python script, print in real-time stdout and write into his stdin? I found some ways to write into his stdin without closing the programs but the problem remains for printing his stdout too.
In fact, there are some issues with thread, Popen and fcntl to print in real time his output but they all assumes that the program flush stdout after each print and include \n in their output.
To be clear, let's say I have a program test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void)
{
char name[100], age[100], location[100];
fprintf (stdout, "\nWhat is your name : ");
fgets (name, sizeof (name), stdin);
name[strcspn (name, "\n")] = '\0';
fprintf (stdout, "How old are you : ");
fgets (age, sizeof (age), stdin);
age[strcspn (age, "\n")] = '\0';
fprintf (stdout, "Where do you live : ");
fgets (location, sizeof (location), stdin);
location[strcspn (location, "\n")] = '\0';
fprintf (stdout, "\nHello %s, you have %s years old and live in
%s\n\n", name, age, location);
return 0;
}
How can I, from a Python script, output the first fprintf() then writing answer into his stdin and so on in the same way I could simply launch test.c?
The purpose is to control data sent to this program from a script and still having return on what happens.

You may use the following program which is heavily relying on the subprocess package from the standard Python language:
def run_program(cmd, sinput=''):
"""Run the command line and output (ret, sout, serr)."""
import subprocess
proc = subprocess.Popen(cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
sin = proc.stdin
sout, serr = proc.communicate(sinput.encode("utf-8"))
ret = proc.wait()
return (ret, sout, serr)
So, the program you just wrote in C would give the following:
cmd = ['myprogram']
sinput = 'myname\n6\nWhitechapel\n'
(ret, sout, serr) = run_program(cmd, sinput)
Note that you cannot avoid the \n character simply because it is used by your target program to know that you send the answer to a question.

Related

Python - How to read from long-running subprocess?

Reading from a long-running subprocess seems to be possible, but I can only get it to work if the subprocess is python3 -i:
>>> sub = subprocess.Popen(['python3', '-i'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> sub.stdin.write(b'2+3\n')
4
>>> sub.stdin.flush()
>>> sub.stdout.readline()
b'5\n'
Attempting the same thing with a small echo program does not work:
>>> sub = subprocess.Popen(['./echo'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> sub.stdin.write(b'2+3\n')
4
>>> sub.stdin.flush()
>>> sub.stdout.readline()
^ Hangs here until interrupted with Ctrl-C
echo reads from STDIO and echoes when it gets a newline. It works fine when run directly. Here is the code for it:
#include <iostream>
#include <unistd.h>
int main() {
char message[64];
uint8_t cursor = 0;
while(true) {
read(STDIN_FILENO, message + cursor, 1);
if(message[cursor++] == '\n') {
message[cursor] = 0;
printf(message);
cursor = 0;
}
}
}
I've tried a few other techniques like .communicate() with similar results. Oddly, subprocess.Popen(['python3'], ...) fails where the same call works when running python3 -i, even though python3 and python3 -i seem to do the same thing when run at the terminal.
The issue turned out to be buffering within the subprocess' stdout. I solved it by setting line-buffering using setvbuf (stdout, NULL, _IOLBF, 1024);. If no buffering at all is desired, setvbuf (stdout, NULL, _IONBF, 0); can be used.

How to properly capture output of process using pwntools

I'm currently confused on how to use the pwntools library for python3 for exploiting programs - mainly sending the input into a vulnerable program.
This is my current python script.
from pwn import *
def executeVuln():
vulnBin = process("./buf2", stdin=PIPE, stdout=PIPE)
vulnBin.sendlineafter(': ','A'*90)
output = vulnBin.recvline(timeout=5)
print(output)
executeVuln()
The program I'm trying to exploit is below - This isn't about how to exploit the program, more on using the script to properly automate it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 176
#define FLAGSIZE 64
void flag(unsigned int arg1, unsigned int arg2) {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
exit(0);
}
fgets(buf,FLAGSIZE,f);
if (arg1 != 0xDEADBEEF)
return;
if (arg2 != 0xC0DED00D)
return;
printf(buf);
}
void vuln(){
char buf[BUFSIZE];
gets(buf);
puts(buf);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid();
setresgid(gid, gid, gid);
puts("Please enter your string: ");
vuln();
return 0;
}
The process is opened fine.
sendlineafter blocks until it sends the line and so if it doesn't match it waits indefinitely. However, it runs fine and so the input should be sent.
output should receive 90 A's from recvLine due to
puts(buffer) outputting the inputted string.
However, all that is returned is
b'', which seems to indicate that the vulnerable program isn't receiving the input and returning an empty string.
Anyone know what's causing this?
With the above programs, I'm getting the print(output) as b'\n' (not b'') and here is the explanation for it.
The puts() statement outputs a newline character at the end and it is not read by the sendlineafter() call, which in-turn leads the stray newline character to be read by the below recvline() printing b'\n'.
Why the newline character is not by read by sendlineafter()? Because the sendlineafter() is just a combination of recvuntil() and sendline(), where recvuntil() only reads till delimiter leaving characters after. (pwntools docs)
So the solution for this is to read the newline character with sendlineafter() like below (or by calling recvline() twice),
from pwn import *
def executeVuln():
vulnBin = process("./buf2", stdin=PIPE, stdout=PIPE)
vulnBin.sendlineafter(b': \n',b'A'*90)
output = vulnBin.recvline(timeout=5)
print(output)
executeVuln()
Output:
[+] Starting local process './buf2': pid 3493
[*] Process './buf2' stopped with exit code 0 (pid 3493)
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n'
Note:
I added the strings as bytes in sendlineafter() to remove the below BytesWarning.
program.py:5: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
vulnBin.sendlineafter(': \n','A'*90)

Interprocess Communication Python [duplicate]

This question already has answers here:
How do I pass a string into subprocess.Popen (using the stdin argument)?
(12 answers)
Closed 8 years ago.
I'm writing a script in Python that should communicate with a software
"ConsoleApplication.exe"(wrote in C); this last one once started waits for a fixed
lenght command(5 bytes) from his "stdin" and generates (after 2-3 seconds) on
his "stdout" an output that I should read on my Python script.
#
//This is the "ConsoleApplication.c" file
#include <stdio.h>
#include <function.h>
char* command[5];
int main()
{
while(1)
{
scanf("%s\n", &command);
output = function(command);
print("%s\n", output);
}
}
#
#this is Python code
import subprocess
#start the process
p = subprocess.Popen(['ConsoleApplication.exe'], shell=True, stderr=subprocess.PIPE)
#command to send to ConsoleApplication.exe
command_to_send = "000648"
#this seems to work well but I need to send a command stored into a buffer and if Itry
#to use sys.stdout.write(command_to_send)nothing will happen. The problem seem that
#sys.stdout.write expect an object I/O FILE
while True:
out = p.stderr.read(1)
if out == '' and p.poll() != None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
#
Any suggestions? How can I fix it?
I tried to use
stdout = p.communicate(input='test\n')[0]
but Im getting the following error at runtime:
"TypeError: 'str' does not support the buffer interface"
I also tried this
from subprocess import Popen, PIPE, STDOUT
p = Popen(['ConsoleApplication.exe'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
out, err = p.communicate(input='00056\n'.encode())
print(out)
out, err = p.communicate(input='00043\n'.encode())
print(out)
but I get this error:
"ValueError: Cannot send input after starting communication"
Looks like this question has your solution
Python - How do I pass a string into subprocess.Popen (using the stdin argument)?
Use the Popen.communicate() method

Is sys.stderr a block device or character device in Python3?

In C language, if I write codes like this:
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
fprintf(stdout,"hello-std-out");
fprintf(stderr,"hello-std-err");
sleep(1);
}
return 0;
}
The stdout will not be displayed because it's a block device. But stderr will be displayed because it's not.
However, if I write similar codes in Python3:
import sys
import time
if __name__ == '__main__':
while True:
sys.stdout.write("hello-std-out")
sys.stderr.write("hello-stderr")
time.sleep(1)
Both stdout and stderr will not be displayed if I don't flush these buffers. Does that mean sys.stderr is also a block device in Python?
If you don't see stderr then you are on Python3 where text IO layer is line-bufferred when connected to a tty and block-bufferred otherwise regardless -u option.
The bufferring issues are unrelated to character/block devices.

Python subprocess module: parent child communication not working

I'm trying to run the following code as a subprocess
#include<stdio.h>
int main()
{
int a;
printf("Hello\n");
fprintf(stderr, "Hey\n");
scanf("%d", &a);
printf("%d\n", a);
return 0;
}
This script works fine: write to stdin, read from stdout and from stderr.
#!/usr/bin/python
import subprocess
p1=subprocess.Popen("/mnt/test/a.out", stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
p1.stdin.write('1\n')
print p1.stdout.readline()
print p1.stderr.readline()
print p1.stdout.readline()
But this script fails to read any output from stdout and gets blocked there even though the C program does print to stdout before demanding any input. Why is it that I'm unable to read anything from stdout?
#!/usr/bin/python
import subprocess
p1=subprocess.Popen("/mnt/test/a.out", stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
print p1.stdout.readline()
p1.stdin.write('1\n')
print p1.stderr.readline()
print p1.stdout.readline()
You need to flush the stream first. This will make sure all data is actually written to the stream.
#include<stdio.h>
int main()
{
int a;
printf("Hello\n");
fprintf(stderr, "Hey\n");
fflush(stdout); // <--
scanf("%d", &a);
printf("%d\n", a);
return 0;
}
By default, stderr is unbuffered, which is why you don't need to flush it. stdout however is fully buffered, unless it points to a terminal, then it line-buffered (i.e. the \n would automatically trigger flushing.
Have a look here, for setbuf() and setvbuf().
I don't see something like
stdout_data, stderr_data = p1.communicate()
in your code
Popen.communicate(input=None)
Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. The optional input argument should be a string to be sent to the child process, or None, if no data should be sent to the child.
communicate() returns a tuple (stdoutdata, stderrdata).
Note that if you want to send data to the process’s stdin, you need to create the Popen object with stdin=PIPE. Similarly, to get anything other than None in the result tuple, you need to give stdout=PIPE and/or stderr=PIPE too.
Note The data read is buffered in memory, so do not use this method if the data size is large or unlimited.
See docs.python.org
A function I keep in my utility-belt to wrap calling an external program using subprocess is this (modify to suit your needs):
def __run(self, cmd):
"""wrapper, makes it easy to call an external program.
return the result as a newline-separated list
"""
args = shlex.split(cmd)
try:
p = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
retdata = p.communicate()[0]
p.wait()
except OSError, e:
print >>sys.stderr, "Execution failed:", e
return (p.returncode, retdata.split('\n'))
Just place your command as you would write it on the cmd-line in a variable an call the function e.g.:
cmd = r'git branch -r'
data = self.__run(cmd)

Categories

Resources