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.
Related
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
Trying to make my script more generic so I added some flags. My problem is the help only works if you type -h , obviously. I want to envoke -h when no flags are selected.
For example:
python 0_log_cleaner.py
Traceback (most recent call last):
File "0_log_cleaner.py", line 51, in <module>
getFiles(options.path,options.org_phrase,options.new_phrase,options.org_AN,options.new_AN,options.dst_path)
File "0_log_cleaner.py", line 37, in getFiles
for filename in os.listdir(path):
TypeError: coercing to Unicode: need string or buffer, NoneType found
but if I add -h I get:
python 0_log_cleaner.py -h
Usage: Example:
python 0_log_cleaner.py --sp original_logs/ --dp clean_logs/ --od CNAME --nd New_CNAME --oan 10208 --nan NewAN
Options:
-h, --help show this help message and exit
--sp=PATH Path to the source logs ie original_logs/
--dp=DST_PATH Path to where sanitized logs will be written to ie
clean_logs
--od=ORG_PHRASE original domain name ie www.clientName.com, use the command
-od clientName
--nd=NEW_PHRASE domain name to replace -od. ie -od clientName -nd domain
makes all log that use to be www.clientName.com into
www.domain.com
--oan=ORG_AN original AN number
--nan=NEW_AN AN number to replace original. ie -oan 12345 -nan AAAA1
replaces all instances of the AN number 12345 with AAAA1
EDIT 3 ANSWER
sample of my code to produce ^
import argparse
import sys
usage = "Description of function"
parser = argparse.ArgumentParser(description=usage)
parser.add_argument("--sp", dest="path", help='Path to the source logs ie logs/')
...
...(additional add arugments)
args = parser.parse_args()
def getFiles(path,org_phrase,new_phrase,org_AN,new_AN,dst_path):
if not len(sys.argv) > 1:
parser.print_help()
else:
run your logic
borrowed from here : Argparse: Check if any arguments have been passed
Here's how the final code looks like:
import argparse
import sys
usage = "Description of function"
parser = argparse.ArgumentParser(description=usage)
parser.add_argument("--sp", dest="path", help='Path to the source logs ie logs/')
...
...(additional add arugments)
args = parser.parse_args()
def getFiles(path,org_phrase,new_phrase,org_AN,new_AN,dst_path):
if not len(sys.argv) > 1:
parser.print_help()
else:
run your logic
If someone is still interested in a (very simple) solution:
parser = argparse.ArgumentParser()
parser.add_argument("jfile", type=str, help="Give the JSON file name.")
parser.add_argument("--output", type=str, help="Type in the final excel files name.")
try:
args = parser.parse_args()
return args
except:
parser.print_help()
My professor wanted the script to force the -h / --help page even when there are too few arguments. Instead of going like "python SCRIPT.py -h".
So what I did here was like: "Try to parse the arguments. And if it works, give them back to the main methode. Otherwise, if you fail (except), print the help(). Okay? Nice". ;)
Without knowing the method you are parsing with, I will assume the following (comment me if I am wrong or edit your question with some code on how you handle your parsing):
You are parsing everything and putting it in a variable. let parsed be that variable.
You are checking parsed for the existence of any of your option flags.
You probably not checking for the non-existence of arguments:
parsed = '' <- empty string
# or if you are using a list:
# parsed = []
if parsed: <- if parsed is not empty ("" or []) returns true
Do your stuff here, because you have options now
else: <- Differently options were not provided
Invoke the same method that you invoke when the option is -h
Also as #dhke suggests, consider using argparse if you are not using it already!
EDIT #1:
Translated for your specific case:
args = parser.parse_args() <-- ending line of your provided code
if not args:
parser.print_help()
else:
Do your stuff
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".
This question already has answers here:
Why does passing variables to subprocess.Popen not work despite passing a list of arguments?
(5 answers)
Closed 1 year ago.
1 import subprocess
2 raw = raw_input("Filename:").lower()
3 ip = raw_input("Host:").lower()
4 cmd = subprocess.call("tcpdump -c5 -vvv -w" + " raw " + " ip ",shell=True)
So this is my script. I everything works besides one key objective, using the raw input.
It allows me to input anything i want, but when it goes to saving the file or using an ip/host doe doesn't actually do anything.
Sure it gives me the packets, but from the localhost not the host i type in.
how i know this isn't working is cause my first raw input is the filename, so i put in test, when i look in the folder were my script is, it produces a file called "raw" meaning, its not actually taking my input only using whats inside my "X"...
So i make a few chances to come to this:
1 import subprocess
2 raw = raw_input("Filename:").lower()
3 ip = raw_input("Host:").lower()
4 cmd = subprocess.call("tcpdump -c5 -vvv -w" + raw + "host" + ip,shell=True)
Which is great because it actually calls for the -w but it saves it now as rawhostip instead of "raw"s input.
for reference this is what the command looks like in the terminal:
tcpdump -c5 -vvv -w savename host wiki2
the only two variabls are savename and wiki2 the rest are needed for the command to work.
with this script i get this error:
import subprocess
raw = raw_input("Filename:").lower()
ip = raw_input("Host:").lower()
cmd = subprocess.call("tcpdump -c5 -vvv -w" + raw, "host" + ip,shell=True)
Error:
Traceback (most recent call last):
File "te.py", line 4, in <module>
cmd = subprocess.call("tcpdump -c5 -vvv -w" + raw, "host" + ip,shell=True)
File "/usr/lib/python2.6/subprocess.py", line 480, in call
return Popen(*popenargs, **kwargs).wait()
File "/usr/lib/python2.6/subprocess.py", line 583, in __init__
raise TypeError("bufsize must be an integer")
TypeError: bufsize must be an integer
I am at a lost. Any help will be great, yes I know look at subprocess's doc's on site:X, I have I need a human to teach me, I don't understand what I am reading.
My question is how do I work with these variables.
Don't use shell=True. That should be False.
You are making subtle mistakes with the input. Specifically, if you have two strings:
>>> s1 = 'Hello'
>>> s2 = 'Hi'
>>> s1 + s2
'HelloHi'
Notice, there is no space between Hello and Hi. So don't do this. (Your line 4)
You should do (the good way):
>>> raw = raw_input('Filename: ')
Filename: test
>>> ip = raw_input('Host: ')
Host: 192.168.1.1
>>> command = 'tcpdump -c5 -vvv -w {0} {1}'.format(raw, ip) # the command goes here
>>> subprocess.call(command.split(), shell=False) # call subprocess and pass the command as a list using split
Now it should work.
You should not use the string form ob the subprocess functions. Try:
subprocess.check_call(["tcpdump", "-c5", "-vvv", "-w", raw, "host", ip])
I am trying to pass '-f nameoffile' to the program when I call it from the command line. I got this from the python sites documentation but when I pass '-f filename' or '--file=filename' it throws the error that I didnt pass enough arguments. If i pass -h the programs responds how it should and gives me the help. Any ideas? I imagine its something simple that I am overlooking. Any and all help is great, thanks, Justin.
[justin87#el-beasto-loco python]$ python openall.py -f chords.tar
Usage: openall.py [options] arg
openall.py: error: incorrect number of arguments
[justin87#el-beasto-loco python]$
#!/usr/bin/python
import tarfile
import os
import zipfile
from optparse import OptionParser
def check_tar(file):
if tarfile.is_tarfile(file):
return True
def open_tar(file):
try:
tar = tarfile.open(file)
tar.extractall()
tar.close()
except tarfile.ReadError:
print "File is somehow invalid or can not be handled by tarfile"
except tarfile.CompressionError:
print "Compression method is not supported or data cannot be decoded"
except tarfile.StreamError:
print "Is raised for the limitations that are typical for stream-like TarFile objects."
except tarfile.ExtractError:
print "Is raised for non-fatal errors when using TarFile.extract(), but only if TarFile.errorlevel== 2."
def check_zip(file):
if zipfile.is_zipfile(file):
return True
def open_zip(file):
try:
zip = zipfile.ZipFile(file)
zip.extractall()
zip.close()
#open the zip
print "GOT TO OPENING"
except zipfile.BadZipfile:
print "The error raised for bad ZIP files (old name: zipfile.error)."
except zipfile.LargeZipFile:
print "The error raised when a ZIP file would require ZIP64 functionality but that has not been enabled."
rules = ((check_tar, open_tar),
(check_zip, open_zip)
)
def checkall(file):
for checks, extracts in rules:
if checks(file):
return extracts(file)
def main():
usage = "usage: %prog [options] arg"
parser = OptionParser(usage)
parser.add_option("-f", "--file", dest="filename",
help="read data from FILENAME")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("incorrect number of arguments")
file = options.filename
checkall(file)
if __name__ == '__main__':
main()
Your problem is probably the if len(args) != 1:. That is looking for an additional argument (i.e. not an option). If you remove that check and look at your options dictionary you should see {'filename': 'blah'}.
Your input filename isn't an option to the program, it's an argument:
def main():
usage = "Usage: %prog [options] FILE"
description = "Read data from FILE."
parser = OptionParser(usage, description=description)
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("incorrect number of arguments")
file = args[0]
checkall(file)
You can usually tell the difference because options generally have sensible defaults while arguments don't.
After parsing the options out of the argument list, you check that you were passed an argument. This is independent of the argument to -f. It sounds like you're just not passing this argument. Since you also don't actually use this argument, you should probably just remove the check on len(args).
You should set the 'action' attribute in the 'add_option()' method to 'store', this tells the optparse object to store the argument immediately following the option flag, though this is the default behavior. The value following the flag will then be stored in 'options.filename' and not in args. I also think that the
if len(args) != 1:
is also an issue, you will get the same message if len(args) is greater than or less than 1.