Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for a prompt from a subprocess before sending stdin input

I have a linux x86 binary that asks for a password and will print out whether the password is correct or incorrect. I would like to use python to fuzz the input.

Below is a screenshot of me running the binary, then giving it the string "asdf", and receiving the string "incorrect"

Screenshot:

Screenshot

So far, I have tried to use the Python3 subprocess module in order to

  1. run the binary as a subprocess
  2. receive the prompt for a password
  3. send a string.
  4. receive the response

Here is my script

p = subprocess.Popen("/home/pj/Desktop/L1/lab1",stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print (p.communicate()[0])

the result of running this script is

b'Please supply the code: \nIncorrect\n'

I am expecting to receive only the prompt however the binary is returning a response of incorrect as well before I have gotten the chance to send my input.

How can I improve my script in order to interact with this binary successfully?

like image 642
user10443042 Avatar asked Jan 23 '19 04:01

user10443042


1 Answers

Read the documentation carefully (emphasis mine):

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.

So, you're sending nothing to the process, and reading all of stdout at once.


In your case, you don't really need to wait for the prompt to send data to the process because streams work asynchronously: the process will get your input only when it tries to read its STDIN:

In [10]: p=subprocess.Popen(("bash", "-c","echo -n 'prompt: '; read -r data; echo $data"),stdin=subprocess.PIPE,stdout=subprocess.PIPE)

In [11]: p.communicate('foobar')
Out[11]: ('prompt: foobar\n', None)

If you insist on waiting for the prompt for whatever reason (e.g. your process checks the input before the prompt, too, expecting something else), you need to read STDOUT manually and be VERY careful how much you read: since Python's file.read is blocking, a simple read() will deadlock because it waits for EOF and the subprocess doesn't close STDOUT -- thus doesn't produce EOF -- until it get input from you. If the input or output length is likely to go over stdio's buffer length (unlikely in your specific case), you also need to do stdout reading and stdin writing in separate threads.

Here's an example using pexpect that takes care of that for you (I'm using pexpect.fdexpect instead of pexpect.spawn suggested in the doc 'cuz it works on all platforms):

In [1]: import pexpect.fdpexpect

In [8]: p=subprocess.Popen(("bash", "-c","echo -n 'prom'; sleep 5; echo 'pt: '; read -r data; echo $data"),stdin=subprocess.PIPE,stdout=subprocess.PIPE)

In [10]: o=pexpect.fdpexpect.fdspawn(p.stdout.fileno())

In [12]: o.expect("prompt: ")
Out[12]: 0

In [16]: p.stdin.write("foobar")    #you can communicate() here, it does the same as
                                    # these 3 steps plus protects from deadlock
In [17]: p.stdin.close()
In [18]: p.stdout.read()
Out[18]: 'foobar\n'
like image 145
ivan_pozdeev Avatar answered Nov 13 '22 05:11

ivan_pozdeev