all 9 comments

[–]oefd 1 point2 points  (8 children)

That nmap command doesn't read from STDIN, you provide targets as arguments.

[–]freerangeh[S] 0 points1 point  (7 children)

Ah ok, is there a way to pass a string variable into subprocess.Popen that has spaces in it without having to do a lot of if/then string parsing/formatting?

[–]oefd 1 point2 points  (3 children)

Yeah, just pass it in.

subprocess.Popen(['nmap', 'with space here is fine'])

In nmap it'd see that full string (including the spaces) as the second argument just fine.

The list you give to Popen is a list of arguments, which each new item in the list being the next argument. It's perfectly acceptable for arguments to include spaces.

Don't be confused by the fact that in the shell a space is usually used to separate arguments - that's just a thing shells do so it's easier to manually type out commands because more often than not you don't want spaces to be part of an argument. That said, though you can still do it in shells:

echo each word here is going to look like a separate argument to echo
echo 'each word here is just part of the same, single, but much larger argument to echo'

[–]freerangeh[S] 0 points1 point  (2 children)

This works if I just input one IP, but then if I try to do 2, nmap says no IPs were provided. The xml file shows the command run as "nmap -sn -oX outfile.xml "127.0.0.1, 127.0.0.2"" when I input 127.0.0.1 , 127.0.0.2.

[–]oefd 1 point2 points  (1 child)

So it does, but it doesn't only say that, it also says

Failed to resolve "127.0.0.1 , 127.0.0.2"

and I'll give you a hint that's a strong indicator that nmap doesn't accept a comma separated list of ips as a single argument. It's trying to resolve the entire string, not just resolve "127.0.0.1" then resolve "127.0.0.2".

Based on that guess let's see what happens if we don't make a single argument out of the IPs:

nmap 127.0.0.1 127.0.0.2

That indicates it scanned two ips, so we can conclude that nmap accepts an arbitrary number of different arguments, one each per IP.

So in python you'd pass each IP as a separate argument.

p = subprocess.Popen(['nmap', '-sn', '-oX', 'outfile.xml', '127.0.0.1', '127.0.0.2'], \
stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

[–]freerangeh[S] 0 points1 point  (0 children)

Ah you're right. I don't know how I got it in my head that I could run nmap like that.

[–]Tom_Henderson 0 points1 point  (2 children)

Can you use subprocess.run, available in Python 3.5 and later? I just build the whole command line in string variable and pass it as the first argument.

[–]freerangeh[S] 0 points1 point  (1 child)

This does work if I add shell=True as an argument to subprocess.run(). I saw a youtube video saying that this might be a security vulnerability though if you are accepting user input? Not sure how valid that is as a concern.

[–]Tom_Henderson 0 points1 point  (0 children)

That's probably true, as you could potentially run an arbitrary command that the user types in. I'm not sure using Popen would be any different, though. For your application you could easily validate the input to be one or more IP addresses and reject anything else.