getopt: How to enforce two options to be present? - python

I am trying to make two options mandatory.
either both -l and -p should be there or -t and -p should be there.
opts, args = getopt.getopt(sys.argv[1:],":lt:p:c:", "listen","target","port","command"])
for o,a in opts:
if o in ("-l"):
print("Starting Listener on 0.0.0.0")
elif o in ("-t"):
if o in ("ip"):
print("Connecting")
else:
print("Else",(o))

A few notes:
Unless you have a good reason, I would suggest using Python's argparse. It is part of the standard library, it's flexible, simple to use and to extend, comes with nice features (like --help), and many people know it.
"Mandatory options" are generally discouraged from. Therefore, my suggestion would be to use one positional argument that needs to have a certain format.
For example:
import argparse
import ipaddress
# Assuming this is about an IP address and a port.
CONN_HELP = "Connection string must have the format IP_ADDRESS:PORT."
def valid_conn_str(conn):
"""Validate the connection string passed as a positional argument"""
# If the colon is missing, the connection string is malformatted.
if ":" not in conn:
raise argparse.ArgumentTypeError(CONN_HELP)
parts = conn.split(":")
# If there are two or more colons,
# the connection string is malformatted.
if len(parts) > 2:
raise argparse.ArgumentTypeError(CONN_HELP)
# If the port part of the connection string is not an integer,
# the connection string is malformatted.
try:
port = int(parts[1])
except ValueError:
raise argparse.ArgumentTypeError(CONN_HELP + " PORT must be an integer.")
# If the port number is larger than 65535,
# the connection string is malformatted
if port > 65535:
raise argparse.ArgumentTypeError(CONN_HELP + " PORT must be < 65535.")
# You could add similar checks in order to validate the IP address
# or whatever else you are expecting as the first part of the
# connection string.
#
# If it is indeed an IP address, you could use the
# ipaddress module for that, e.g.:
try:
ip_addr = ipaddress.ip_address(parts[0])
except ValueError:
raise argparse.ArgumentTypeError(CONN_HELP + " Invalid IP address.")
# When all checks have passed, return the values.
return parts[0], port
parser = argparse.ArgumentParser()
parser.add_argument(
"conn",
metavar="CONN_STRING",
# Use the validator function to check the format.
type=valid_conn_str,
help=CONN_HELP,
)
# args contains all arguments that have been passed in.
args = parser.parse_args()
# The validation function returns two values,
# therefore connection string argument is a tuple.
target = args.conn[0]
port = args.conn[1]
print(target)
print(port)
When someone calls the script now, with for example:
$ my_script.py hello:world
they will see
usage: my_script.py [-h] CONN_STRING
my_script.py: error: argument CONN_STRING: Connection string must have the format IP_ADDRESS:PORT. PORT must be an integer.
Running it with a valid port but invalid IP address
$ my_script.py hello:123
will give:
usage: my_script.py [-h] CONN_STRING
my_script.py: error: argument CONN_STRING: Connection string must have the format IP_ADDRESS:PORT. Invalid IP address.
Running with valid IP address and port
$ my_script.py 123.123.123:123
will print
123.123.123.123
123

Related

Python GetHostId.py getting sys.argv[1] IndexError: list index out of range

i am working on a python script to be a multi tool for getting DNS information on servers in a enterprise env. so far the script i have is using python 3.5. i am using argparse for creating command line options, which i am trying to create an if/ elif / else statement which contains the different selections. the main error message i am getting is:
./GetHostName.py
Traceback (most recent call last):
File "./GetHostName.py", line 34, in <module>
remoteServer = sys.argv[1]
IndexError: list index out of range
that is when the command is run by itself.
when it is run with a host name at the end ./GetHostName.py hostName
it gives this message:
GetHostName.py: error: unrecognized arguments: hostName
I didn't put real name of server for security issues....
When i use the argparse options say like the -f option for getting the FQDN, it gives this response...
./GetHostName.py -f hostName
3
-f
from the way it appears, it is taking the -f as input for the server name, when it should only be the input for argparse input. i have tried everything to fix it that i can think of. i have encased the main code body in a main function, that didn't work so i removed it. i use the try: statement and exception statements. that didn't work. i am wondering if there is something just basically wrong with my programming logic at this point...
this here is the code from the script:
#!C:\Bin\Python35\python.exe
#
# import libraries
import sys, os
import argparse as ap
import socket
# Command Line interface setup
def argParse():
#Command Line arg parse
parser=ap.ArgumentParser(description='A tool to get a remote servers DNS information.')
parser.add_argument("-a", "--address", default="fqdn", help="Gets IP address from host name.")
parser.add_argument("-f", "--fqdn", default="fqdn", help="Gets the FQDN address of server.")
parser.add_argument("-d", "--addrinfo", default="fqdn", help="Gets the FQDN address of server.")
parser.add_argument("-l", "--local", default="fqdn", help="Gets info on local host.")
parser.add_argument("-Pr", "--proto", default="fqdn", help="Translate an Internet protocol name to a constant suitable for passing as the (optional) third argument to the socket() function.")
parser.add_argument("-n", "--nameinfo", default="fqdn", help="Gets name and port on remote host.")
parser.add_argument("-Sn", "--servbyname", default="fqdn", help="Translate an Internet service name and protocol name to a port number for that service.")
parser.add_argument("-Sp", "--servbyport", default="fqdn", help="Translate an Internet port number and protocol name to a service name for that service.")
parser.add_argument("-t", "--timeout", default="fqdn", help="Return the default timeout in seconds for new socket objects.")
parser.add_argument("-v", "--verbose", default="fqdn", help="Increase output verbosity")
return parser.parse_args()
#remoteServer = input().strip().split()
args=argParse()
if args.fqdn:
remoteServer = sys.argv[1]
print (len(sys.argv))
remoteServerIP = socket.getfqdn(remoteServer)
print (remoteServerIP)
elif args.address:
remoteServer = sys.argv[2]
print (len(sys.argv))
remoteServerIP = socket.gethostbyname(remoteServer)
print (remoteServerIP)
elif args.addrinfo:
remoteServer = sys.argv[3]
print (len(sys.argv))
remoteServerIP = socket.getaddrinfo(remoteServer)
print (remoteServerIP)
elif args.local:
remoteServer = sys.argv[4]
print (len(sys.argv))
remoteServerIP = socket.gethostname()
print (remoteServerIP)
elif args.proto:
remoteServer = sys.argv[5]
print (len(sys.argv))
remoteServerIP = socket.getprotobyname(remoteServer)
print (remoteServerIP)
elif args.servbyname:
remoteServer = sys.argv[6]
print (len(sys.argv))
remoteServerIP = socket.getservbyname(remoteServer)
print (remoteServerIP)
elif args.servbyport:
remoteServer = sys.argv[7]
print (len(sys.argv))
remoteServerIP = socket.getservbyport(remoteServer)
print (remoteServerIP)
elif args.timeout:
remoteServer = sys.argv[8]
print (len(sys.argv))
remoteServerIP = socket.getdefaulttimeout(remoteServer)
print (remoteServerIP)
elif args.verbose:
remoteServer = sys.argv[9]
print (len(sys.argv))
remoteServerIP = socket.gethostbyaddr(remoteServer)
print (remoteServerIP)
else:
args.nameinfo
remoteServer = sys.argv[10]
print (len(sys.argv))
remoteServerIP = socket.getnameinfo(remoteServer)
print (remoteServerIP)
any help would be appreciated. please note that when i run a script with just this in it, it works just fine:
#!C:\Bin\Python35\python.exe
#
import sys, os
import argparse
import socket
# Command Line interface setup
def main():
remoteServer = sys.argv[1]
remoteServerIP = socket.gethostbyaddr(remoteServer)
print (remoteServerIP)
if __name__ == '__main__':
main()
thanks in advance.
-Betzelel
P.S. the code may look out of format, due to having to copy and paste into this blog, and manually putting 4 spaces on to each line to get it to show up as code lol.
I've had this problem long time ago. To fix this I've changed
parser.add_argument("-a", "--address", default="fqdn", help="Gets IP address from host name.")
in
parser.add_argument("-a", "--address", default="fqdn", help="Gets IP address from host name.", dest=ipfromhostname)
So for getting the value from -a you have to change
remoteServer = sys.argv[1]
in
remoteServer = args.ipfromhostname
where ipfromhostname is the dest value.
EDIT:
You have to do this operation for every parser.add_argument

ValueError: invalid literal for int() with base 10: '192.168.1.1'

an error ValueError: invalid literal for int() with base 10:
def ports():
os.system("clear")
target = raw_input("Select a Target IP : ")
target = int(target)
os.system("nmap -O -Pn %s") % target
sys.exit();
any help please ?
Don't try to convert the IP to int.
Instead, deal with it as a string.
Your real problem is with the line os.system("nmap -O -Pn %s") % target because os.system() returns an int (the status exit code of the command) and then you try to use the % operator on an int and a string. . It should be os.system("nmap -O -Pn %s" % target).
You have to pass a string to os.system but there is a python-nmap api that will do what you want without any need for system calls and give you a lot more options, to scan a host it is as simple as:
import nmap
ip = "127.0.0.1"
scanner = nmap.PortScanner()
scanner.scan("-O -Pn {}".format(ip))
You can also easily do asynchronous scans:
import nmap
nma = nmap.PortScannerAsync()
def callback_result(host, scan_result):
print('------------------')
print(host, scan_result)
nma.scan(hosts='192.168.1.0/30', arguments='-sP', callback=callback_result)
while nma.still_scanning():
print("Waiting >>>")
nma.wait(2) # you can do whatever you want but I choose to wait after the end of the scan
There are numerous examples here. os.system is also not going to give you any output to parse, if you were doing a system call you should be using subprocess
Thanks to #deepspace I found the answer:
os.system("clear")
target = raw_input("Select a Target IP : ")
os.system("nmap -O -Pn %s" % target)
sys.exit();

"someVariable is None" always false w/ value derived from optparse result

I'm really getting mad 'cause of a problem I do not manage to get through while programming a simple and didactic portscanner in python. Here's the code:
def main():
parser = optparse.OptionParser("usage%prog "+\
"-H <target host> -p <target port>")
parser.add_option('-H', dest='tgtHost', type='string', \
help='specify target host')
parser.add_option('-p', dest='tgtPort', type='string', \
help='specify target port[s] separated by comma')
(options, args) = parser.parse_args()
tgtHost = options.tgtHost
tgtPorts = str((options.tgtPort)).replace(",", " ").split()
if (tgtHost is None) | (tgtPorts is None):
print '[-] You must specify a target host and port[s].'
exit(0)
it all works as expected, apart from one thing: the (tgtPorts is None) check does not seem to work, while the tgtHost control works fine. In other words, this is what happens without a specified -H option:
$ python portscanner.py -p 21
[-] You must specify a target host and port[s].
while with the host and without -p here's what happens:
$ python portscanner.py -H 1234
[+] Scan Results for: 0.0.4.210
Scanning port None
Traceback (most recent call last):
File "portscanner.py", line 45, in <module>
main()
File "portscanner.py", line 43, in main
portScan(tgtHost, tgtPorts)
File "portscanner.py", line 29, in portScan
connScan(tgtHost, int(tgtPort))
ValueError: invalid literal for int() with base 10: 'None'
So the script throws an error because it cannot convert None to int, and that's the point of the consistence check. I've already tried to change (tgtPorts is None) in (tgtPorts[0] is None), but nothing changed. Googled for it as well, but noone seems to have had the same problem. Any ideas?
You have a string with the word 'None' in it, not the None object.
You made it a string here:
tgtPorts = str((options.tgtPort)).replace(",", " ").split()
Rather than use str() there, test for options.tgtPort having a true value (e.g. not None or an empty string):
if options.tgtPort:
tgtPorts = options.tgtPort.replace(",", " ").split()
Note that | is bitwise OR, you should really use or instead. I'd test for the options first, then parse:
if not (options.tgtHost and options.tgtPort):
print '[-] You must specify a target host and port[s].'
exit(1)
Here both omitting the options and not specificing a value is an error.
Personally, I'd use the argparse module here and use required arguments, with the ports argument set to nargs='+' to capture one or more values. Error handling is then done by argparse as well.

Socket function getting error

Im trying to do a socket.gethostbyname from a list of subdomains concatenated with an argument value but am getting an error. Im not sure if the loop isnt working correctly and the first try is just not a valid subdomain+domain. or if it just isnt working at all?
ERROR
Traceback (most recent call last):
File "./attack2.py", line 40, in <module>
print subcheck(returned_list, arguments['--domain'])
File "./attack2.py", line 31, in subcheck
socket.gethostbyname(sub + domain)
socket.gaierror: [Errno -2] Name or service not known
CODE
#!/usr/bin/python
"""
Description:
Basic Domain bruteforcer
Usage:
attack2.py (-f <file>) (-d <domain>) [-t 10] [-v]
attack2.py -h | --help
Arguments:
-f --file File to read potential Sub-domains from. (Required)
-d --domain Domain to bruteforce. (Required)
Options:
-h --help Show this screen.
-p --proxy Proxy address and port. [default: http://127.0.0.1:8080] (Optional)
-t --thread Thread count. (Optional)
-v --verbose Turn debug on. (Optional)
"""
import socket
from docopt import docopt
def fread(dwords):
flist = open(dwords).readlines()
return [s.replace('\n', '.') for s in flist]
def subcheck(subdomain, domain):
for sub in subdomain:
socket.gethostbyname(sub + domain)
return output
if __name__ == "__main__":
arguments = docopt(__doc__, version='0.1a')
print arguments
print fread(arguments['--file'])
returned_list = fread(arguments['--file'])
print subcheck(returned_list, arguments['--domain'])
NEW def subcheck code
def subcheck(subdomain, domain):
for sub in subdomain:
try:
#print "%s %d" % (sub+domain,len(sub+domain))
print socket.gethostbyname(sub + domain)
except:
print "Some error"
NEW OUTPUT
173.194.34.150
173.194.34.137
Some error
Some error
None
domain.google.com and stuff.google.com are invalid domains. Getting rid of those will fix the problem.
None is returned because you aren't returning anything in your subcheck function. So when you invoke it like this:
print subcheck(returned_list, arguments['--domain'])
it will be forced to return, and print None. Remove the print in front of it and you won't see None.
"[Errno -2] Name or service not known" sounds to me like your program cannot reach a DNS server. Can you resolve the names in some other way? e.g. using `dig'. I think if socket.gethostbyname cannot resolve the name, it returns a different error; when I try it, I get: "No address associated with hostname".

Using Python subprocess and string formating

I am attempting to call a windows command with relevant arguments using python's subprocess . The command is executing and the arguments and their values look to be correct, however It only seems to be working correctly when using the "local mode" -l.
I'm getting an invalid argument/option error when using the remote mode. Could any point out where im going wrong?
Could anyone point out how to format the subprocess.check_ouput() arguments correctly to include the variables given at commandline when executing the script? As you can see ive tryd using string formating, both old and new to try get it working as I cant workout how to add the last domain variable inbetween the filter (/FI) argument value without string formatting.
expected commandline to execute
tasklist /V /S 192.168.1.122 /U 'DOMAIN'\'USERNAME' /P 'PASSWORD' /FI "USERNAME eq 'DOMAIN'\*"
with this commandline example of the script:
hunter.py -d DOMAIN -u USERNAME -p PASSWORD -s servers.txt
This is the error:
ERROR: Invalid argument/option - '/S 192.168.1.122'.
Type "TASKLIST /?" for usage.
Clearly the argument is correct "visually" correct anyway, here is the usage for the tasklist:
Description:
This tool displays a list of currently running processes on
either a local or remote machine.
Parameter List:
/S system Specifies the remote system to connect to.
/U [domain\]user Specifies the user context under which
the command should execute.
/P [password] Specifies the password for the given
user context. Prompts for input if omitted.
/M [module] Lists all tasks currently using the given
exe/dll name. If the module name is not
specified all loaded modules are displayed.
/SVC Displays services hosted in each process.
/APPS Displays Store Apps and their accociated processes.
/V Displays verbose task information.
/FI filter Displays a set of tasks that match a
given criteria specified by the filter.
/FO format Specifies the output format.
Valid values: "TABLE", "LIST", "CSV".
/NH Specifies that the "Column Header" should
not be displayed in the output.
Valid only for "TABLE" and "CSV" formats.
/? Displays this help message.
This is the python code i have so far;
#!/usr/bin/python
"""
Description:
Used for checking users logged into a list of servers.
Usage:
hunter.py [-u <username>] [-p <password>] [-s <FILE>] (-d <domain>)
hunter.py (-d <domain>) (-l)
hunter.py -h | --help
hunter.py --version
Options:
-l --local
-u --username
-h --help Show this screen.
--version Show version.
-p --password
-d --domain
-s --serverfile=FILE
"""
from docopt import docopt
import subprocess
from subprocess import CalledProcessError
def tldomain(serverlist, domain, username, password):
nlist = serverlist
for serverl in nlist:
try:
print subprocess.check_output(["tasklist", "/V", "/S " + serverl, "/U" + domain, "\\" + username, "/P" + password, "/FI", "'USERNAME eq %s\\\*'"]) % domain
except CalledProcessError as e:
print(e.returncode)
def tllocal(domain):
try:
cmd = 'tasklist /V /FI "USERNAME eq {0}\\*"' .format(domain)
subprocess.call(cmd)
except OSError as e:
print e
def getservers(servers):
slist = open(servers).readlines()
return [s.replace('\n', '') for s in slist]
if __name__ == "__main__":
arguments = docopt(__doc__, version='0.1a')
print arguments
if (arguments['--local']) == False:
serverlist = getservers(arguments['--serverfile'])
tldomain(serverlist, arguments['<domain>'], arguments['<username>'], arguments['<password>'])
else:
tllocal(arguments['<domain>'])
Pass in your arguments as separate elements in the list and apply the string formatting to the last element, no the output of the subprocess.check_output() call:
print subprocess.check_output(
["tasklist", "/V", "/S", serverl, "/U", domain + "\\" + username,
"/P", password, "/FI", "USERNAME eq %s\\*" % domain])
Note that I also removed the ' quoting from the last argument, leave that to the subprocess module.
This also assumes that domain is always a non-empty string; if that's not the case, use:
if domain:
domain += '\\'
print subprocess.check_output(
["tasklist", "/V", "/S", serverl, "/U", domain + username,
"/P", password, "/FI", "USERNAME eq %s*" % domain])
e.g. only use the \ backslash when domain is actually specified.

Categories