-1

i have a homework assignment to capture a 4way handshake between a client and AP using scapy. im trying to use "aircrack-ng capture.pcap" to check for valid handshakes in the capture file i created using scapy

i launch the program using Popen. the program waits for user input so i have to kill it. when i try to get stdout after killing it the output is empty.

i've tried stdout.read(), i've tried communicate, i've tried reading stderr, and i've tried it both with and without shells

check=Popen("aircrack-ng capture.pcap",shell=True,stdin=PIPE,stdout=PIPE,stderr=PIPE)
check.kill()    
print(check.stdout.read())
jfleach
  • 501
  • 1
  • 8
  • 21
  • Can't you read before killing ? – Alex Apr 30 '19 at 12:47
  • 3
    You're not giving it any time at all to finish starting up and run the `echo`. You need to wait at least a *little* bit of time -- process startup isn't instant. – Charles Duffy Apr 30 '19 at 12:51
  • Also, if you don't want to let the program wait for input, why not `stdin=DEVNULL`, so its attempts to read input immediately return nothing? (On older versions of Python, `stdin=open('/dev/null', 'r')` will do the same). – Charles Duffy Apr 30 '19 at 12:52
  • Or, if you're going to keep `stdin=PIPE`, pass an empty string to `communicate()` as an argument to get the same effect. But either way, you can't kill a process an instant after it's invoked and expect it to have done anything at all. – Charles Duffy Apr 30 '19 at 12:53
  • Why not sending the required user input – Lee Apr 30 '19 at 12:56
  • 2
    Note too that `foo & bar` kicks off `foo` in a subprocess, which adds a bunch of complications to the correct operation of `kill()` (potentially causing the Python `read()` to hang while waiting for the forked-off subprocess to close its output file descriptors, even if the top-level shell process was killed successfully). If you aren't asking about that specific problem, much better to use `&&` rather than `&` to connect the `echo` to the `sleep`. – Charles Duffy Apr 30 '19 at 13:06
  • "the program waits for user input so i have to kill it" is a complete non-sequitur. If the program is waiting for user input, then either *give it user input* (using `communicate()` or `check.stdin.write()` etc.) or *make it unable to read user input* (by closing its stdin pipe). Possibly what you're actually looking for is [pexpect](https://pexpect.readthedocs.io/)? – Daniel Pryden Apr 30 '19 at 13:11
  • @DanielPryden, ...if they're trying to kill the program as soon as it wants to read input, that sounds to me like all they want is to close the stdin pipe (or pass something like `/dev/null` that's already at EOF). `pexpect` would be for a completely different use case, where they wanted to *provide* input. – Charles Duffy Apr 30 '19 at 13:15
  • @CharlesDuffy: That's true, but I read this question as an X-Y problem. I suspect that the program in question has some kind of CLI, and sending e.g. `quit\n` using `pexpect` would accomplish their goal without any need to kill the process in the first place. – Daniel Pryden Apr 30 '19 at 13:18
  • `communicate('quit\n')` would likely do just as well, if they're in an unlikely case where an EOF on stdin isn't treates an an end-of-input and thus end-of-program for a program that consumes commands from stdin. But, frankly, I rarely see such cases; indeed, I'd argue that if one exists, it's a bug; why would you ever want something that exits on `quit` or such to keep running when the user types ctrl+d at the TTY to send an EOF? – Charles Duffy Apr 30 '19 at 13:20
  • giving valid input causes the program to go off and do this whole long thing that i don't need it to do. attempting to pass an empty string causes the program to hang. attempting to sleep before killing and reading causes it to hang. attempting to read or communicate() before killing causes it to hang –  Apr 30 '19 at 21:45
  • i need to get the output of the program when its run then kill it because anything else causes it to hang. ive also tried piping ^c to the program but that causes it to error out –  Apr 30 '19 at 21:47
  • upon further testing it seems that killing it isnt actually the issue. it seems to hang on stdout.read()/communicate() no matter what i do beforehand (kill, sleep, devnull,etc) –  Apr 30 '19 at 22:06
  • Notice what I said about `foo & bar` and `foo && bar` being different? That's important. If you really do use `foo & bar`, then your `kill` will only kill the main shell, not the subshell that `foo &` spawns, but the `read()` can never finish until **all** the children are dead (or, to be more specific, until all programs that inherited write handles on the child's stdout are dead). – Charles Duffy Apr 30 '19 at 22:22
  • so i think the dummy command i put in my question might have confused some people. ill edit the status to have the actual command. sorry about that. should have just put the command originally but i figured not many people would be familiar with the program so it wouldnt matter –  Apr 30 '19 at 22:40
  • Anyhow, if you *really* want control, stop using `shell=True`, and create a separate `subprocess.Popen` object for each individual process you need. – Charles Duffy Apr 30 '19 at 22:40
  • According to https://www.aircrack-ng.org/doku.php?id=newbie_guide , `aircrack-ng` can run for a long time depending on the amount of leaked info in the pcap file. Are you sure it hangs? And according to https://www.aircrack-ng.org/doku.php?id=aircrack-ng , it doesn't use stdin unless you pass `-w -`, so your stdin concerns are irrelevant. – ivan_pozdeev Apr 30 '19 at 22:50
  • 1
    For your real command, there's no good reason to use `shell=True`. Change your command argument to `['aircrack-ng', 'capture.pcap']`, take out the `shell=True`, and thereafter the shell being killed but the copy of `aircrack-ng` it starts still being alive is one less set of things that can go wrong. – Charles Duffy Apr 30 '19 at 22:57
  • nah it was hanging indefinitely. i just found a solution though. if i use timeout 1 in the command it allows me to get output without hanging –  Apr 30 '19 at 22:57
  • 2
    `timeout 1` is a hack. Fix the reason your SIGTERM isn't routing to aircap-ng (which could well be the `shell=True` causing the TERM to go to the `sh` not the `aircap-ng` process) and you won't need that hack anymore. – Charles Duffy Apr 30 '19 at 22:58

2 Answers2

1

While you shouldn't do this (trying to rely on hardcoded delays is inherently race-condition-prone), that the issue is caused by your kill() being delivered while sh is still starting up can be demonstrated by the problem being "solved" (not reliably, but sufficient for demonstration) by tiny little sleep long enough let the shell start up and the echo run:

import time
from subprocess import Popen, PIPE

check=Popen("echo hello && sleep 1000", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
time.sleep(0.01) # BAD PRACTICE: Race-condition-prone, use one of the below instead.
check.kill()    
print(check.stdout.read())

That said, a much better-practice solution would be to close the stdin descriptor so the reads immediately return 0-byte results. On newer versions of Python (modern 3.x), you can do that with DEVNULL:

import time
from subprocess import Popen, PIPE, DEVNULL

check=Popen("echo hello && read input && sleep 1000", 
            shell=True, stdin=DEVNULL, stdout=PIPE, stderr=PIPE)
print(check.stdout.read())

...or, with Python 2.x, a similar effect can be achieved by passing an empty string to communicate(), thus close()ing the stdin pipe immediately:

import time
from subprocess import Popen, PIPE

check=Popen("echo hello && read input && sleep 1000", 
            shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
print(check.communicate('')[0])
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • `time.sleep` is unreliable. I cannot approve advising such bad practice to a newbie. – ivan_pozdeev Apr 30 '19 at 13:01
  • 1
    Oh, I definitely agree that there's a race condition there. The point isn't to endorse or condone the practice, the point is to demonstrate that their problem was caused by `sh` being interrupted while still trying to start up. And it definitely succeeds in that demonstration. – Charles Duffy Apr 30 '19 at 13:02
  • thank you for the suggestions! unfortunately passing an empty string to the program also causes it to hang so i need to kill it. i forgot to mention i tried DEVNULL in my post sorry. also i just tried sleeping before killing it and that seems to cause a hang as well –  Apr 30 '19 at 21:41
  • Notice the `read input && sleep 1000` in my examples? That's important; the examples all work, but they depend on the program exiting if it can't read from stdin... which is something well-behaved programs generally do. – Charles Duffy Apr 30 '19 at 22:25
0

Never, and I mean, never kill a process as part of normal operation. There's no guarantee whatsoever how far it has proceeded by the time you kill it, so you cannot expect any specific results from it in such a case.

  • To explicitly pass nothing to a subprocess as input to prevent hanging when it tries to read stdin:

  • Use <process>.communicate(), or use subprocess.check_output() instead of Popen to read output reliably

    • A process, in the general case, is not guaranteed to output any data at any particular moment due to I/O buffering. So you need to read the output stream after the process completes to be sure you've got everything.
    • At the same time, you need to keep reading the stream in the meantime if the process can produce enough output to fill an I/O buffer1. Otherwise, it will hang waiting for you to read the buffered data. If both stdout and stderr are PIPEs, you need to read them both, in parallel -- i.e. in different threads.
      • communicate() and check_output (that uses the former under the hood) achieve this by reading stdout and stderr in two separate threads.
  • Prefer convenience functions to Popen for common use cases -- in your case, check_output -- as they take care of all the aforementioned caveats for you.


1Pipes are fully buffered and a typical buffer size is 64KB

ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • thanks for the suggestions! unfortunately passing an empty string to the program causes it to hang as well so i need to kill it –  Apr 30 '19 at 21:42
  • @0rphon `sleep 1000` sleeps for 1000 seconds (~16,5 minutes). If your command is completely different, provide a [mcve] for us to be able to say what's happening. – ivan_pozdeev Apr 30 '19 at 21:59
  • i have a homework assignment to capture a 4way handshake between a client and AP using scapy. im trying to use "aircrack-ng capture.pcap" to check for valid handshakes in the capture file i created using scapy –  Apr 30 '19 at 22:17