From bf296384acca716bc308e0ff0c97977e013f5d66 Mon Sep 17 00:00:00 2001 From: Rad10 Date: Thu, 13 Aug 2020 21:01:51 -0400 Subject: [PATCH 1/9] Remade help screen to resemble parent program (#16) --- searchsploit.py | 75 +++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/searchsploit.py b/searchsploit.py index 96f96e4..e6f52e6 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -116,43 +116,56 @@ def scrapeRC(): """ # Arguments -parserCommands = parser.add_mutually_exclusive_group() - parser.add_argument("searchTerms", nargs="*") -parser.add_argument("-c", "--case", action="store_true", - help="Perform a case-sensitive search (Default is inSEnsITiVe).") -parser.add_argument("-e", "--exact", action="store_true", - help="Perform an EXACT match on exploit title (Default is AND) [Implies \"-t\"].") -parser.add_argument("-i", "--ignore", action="store_true", - help="Adds any redundant term in despite it possibly giving false positives.") -parser.add_help = True -parser.add_argument("-j", "--json", action="store_true", - help="Show result in JSON format.") +searchHeader = parser.add_argument_group( + "Search Terms", "These arguments are used to manipulate the results of a search to get more specific searches.") +searchHeader.add_argument("-c", "--case", action="store_true", + help="Perform a case-sensitive search (Default is inSEnsITiVe).") +searchHeader.add_argument("-e", "--exact", action="store_true", + help="Perform an EXACT match on exploit title (Default is AND) [Implies \"-t\"].") +searchHeader.add_argument("-i", "--ignore", action="store_true", + help="Adds any redundant term in despite it possibly giving false positives.") +# TODO: Add strict +searchHeader.add_argument("-t", "--title", action="store_true", + help="Search JUST the exploit title (Default is title AND the file's path).") +searchHeader.add_argument("--exclude", nargs="*", type=str, default=list(), metavar="[terms]", + help="Remove certain terms from the results. Option best added after all other terms have been gathered.") + +outputHeader = parser.add_argument_group( + "Output", "These arguments drastically change the output given by the program. This can vary from how the results are listed to giving information on one specific exploit.") +outputHeader.add_argument("-j", "--json", action="store_true", + help="Show result in JSON format.") +outputHeader.add_argument("-o", "--overflow", action="store_true", + help="Exploit titles are allowed to overflow their columns.") +# TODO: Add verbose +outputHeader.add_argument("-w", "--www", action="store_true", + help="Show URLs to Exploit-DB.com rather than the local path.") +outputHeader.add_argument("--id", action="store_true", + help="Display the EDB-ID value rather than local path.") + +outputHeader.add_argument("--colour", action="store_false", + help="Disable colour highlighting in search results.") + +commandsHeader = parser.add_argument_group( + "EDB Tools", "These commands involve functions on individual EDB's.") +parserCommands = commandsHeader.add_mutually_exclusive_group() +parserCommands.add_help = True parserCommands.add_argument("-m", "--mirror", type=int, default=None, metavar="[EDB-ID]", help="Mirror (aka copies) an exploit to the current working directory.") -parser.add_argument("-o", "--overflow", action="store_true", - help="Exploit titles are allowed to overflow their columns.") +parserCommands.add_argument("-x", "--examine", type=int, default=None, + metavar="[EDB-ID]", help="Examine (aka opens) the exploit using \$PAGER.") parserCommands.add_argument("-p", "--path", type=int, default=None, metavar="[EDB-ID]", help="Show the full path to an exploit (and also copies the path to the clipboard if possible).") -parser.add_argument("-t", "--title", action="store_true", - help="Search JUST the exploit title (Default is title AND the file's path).") -parser.add_argument("-u", "--update", action="store_true", - help="Check for and install any exploitdb package updates (deb or git).") -parser.add_argument("-w", "--www", action="store_true", - help="Show URLs to Exploit-DB.com rather than the local path.") -parserCommands.add_argument("-x", "--examine", type=int, default=None, - metavar=("[EDB-ID]"), help="Examine (aka opens) the exploit using \$PAGER.") -parser.add_argument("--colour", action="store_false", - help="Disable colour highlighting in search results.") -parser.add_argument("--id", action="store_true", - help="Display the EDB-ID value rather than local path.") -parser.add_argument("--nmap", metavar="file.xml", nargs="?", type=argparse.FileType("r"), default=None, const=os.sys.stdin, - help="Checks all results in Nmap's XML output with service version (e.g.: nmap -sV -oX file.xml).\nUse \"-v\" (verbose) to try even more combinations") -parser.add_argument("--version", action="version", - version="%(prog)s {0}".format(VERSION)) -parser.add_argument("--exclude", nargs="*", type=str, default=list(), metavar="[terms]", - help="Remove certain terms from the results. Option best added after all other terms have been gathered.") +parserCommands.add_argument("-u", "--update", action="store_true", + help="Check for and install any exploitdb package updates (deb or git).") +parserCommands.add_argument("--version", action="version", + version="%(prog)s {0}".format(VERSION)) + +automationHeader = parser.add_argument_group( + "Automation", "This involves all tools that involve plugins from other tools, such as nmap.") +automationHeader.add_argument("--nmap", metavar="file.xml", nargs="?", type=argparse.FileType("r"), default=None, const=os.sys.stdin, + help="Checks all results in Nmap's XML output with service version (e.g.: nmap -sV -oX file.xml).\nUse \"-v\" (verbose) to try even more combinations") # Argument variable parseArgs = parser.parse_args() From 326edc95a6ce770532a21634756636d85d78b974 Mon Sep 17 00:00:00 2001 From: Rad10 Date: Thu, 13 Aug 2020 21:16:31 -0400 Subject: [PATCH 2/9] Path displays no longer show db title at start of path --- searchsploit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/searchsploit.py b/searchsploit.py index e6f52e6..f9d6829 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -468,6 +468,11 @@ def searchsploitout(): if parseArgs.www: # if requesting weblinks. shapes the output for urls lines[1] = "https://www.exploit-db.com/" + \ lines[1][:lines[1].index("/")] + "/" + lines[2] + + # substring path with title + if lines[1].startswith(name_array[i].lower()): + lines[1] = lines[1][len(name_array[i]) + 1:] + if parseArgs.colour: for term in terms: lines[0] = highlightTerm(lines[0], term) From 12b15becb4801cf5dac46cb392b9eb77a8dae32f Mon Sep 17 00:00:00 2001 From: Rad10 Date: Thu, 13 Aug 2020 21:18:16 -0400 Subject: [PATCH 3/9] Renamed run to main --- searchsploit.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/searchsploit.py b/searchsploit.py index f9d6829..22d9dd9 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -683,7 +683,7 @@ def examine(id): ################## -def run(): +def main(): """ Main function of script. hooks rest of functions """ @@ -742,4 +742,5 @@ def run(): searchsploitout() -run() +if __name__ == "__main__": + main() From 2a14681b42cdf52a4e5d2b29bc82dd5722a5cf79 Mon Sep 17 00:00:00 2001 From: Nick Cottrell Date: Thu, 17 Sep 2020 15:07:52 -0400 Subject: [PATCH 4/9] Created version searching functions --- searchsploit.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/searchsploit.py b/searchsploit.py index 22d9dd9..149727a 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -2,6 +2,7 @@ from sys import argv, exit import os import argparse +import re # Default options COL = 0 @@ -309,6 +310,41 @@ def findExploit(id): else: return i, exploit +baseVersion = re.compile(r'((?:(?:<)\s*)|(?:[RrVv]))?((?:\d+(?:\.(?:\d+|x))+)|(?:\d+))') + +def hasVersion(term: str)->bool: + """ Returns true if the string contains any numbers that could resemble a verison + """ + return (len(baseVersion.findall(term)) > 0) + +def getVersion(term: str): + return baseVersion.findall(term) # returns the first found object, taking tuple out of list + +def cmpVersion(leftVersion: str, rightVersion: str)->int: + """ Compares the two versions and returns an integer depending on which one if newer + """ + leftComponent = leftVersion.split(".") + rightComponent = rightVersion.split(".") + for i in range(min(len(leftComponent), len(rightComponent))): + if (leftComponent[i] != rightComponent[i]): + # Dealing with wildcard scenarios + if ("x" in leftComponent[i]): + if (len(re.findall("((?:{0})|(?:{1}))$".format(leftComponent[i],leftComponent[i].replace("x","\\d*")), rightComponent[i])) > 0): + # if the string matches itself or regex replacing all x's with \d's, then it is a match + return 0 + elif ("x" in rightComponent[i]): + if (len(re.findall("((?:{0})|(?:{1}))$".format(rightComponent[i],rightComponent[i].replace("x","\\d*")), leftComponent[i])) > 0): + # if the string matches itself or regex replacing all x's with \d's, then it is a match + return 0 + + # Removing excess chars + # print(leftComponent[i], rightComponent[i]) + for j in re.findall("([A-Za-z]+)", leftComponent[i]): + leftComponent[i] = leftComponent[i].replace(j, "") + for j in re.findall("([A-Za-z]+)", rightComponent[i]): + rightComponent[i] = rightComponent[i].replace(j, "") + return int(rightComponent[i]) - int(leftComponent[i]) + return len(rightComponent) - len(leftComponent) # if they are equal up their shortest version, then the latest is the longest version def validTerm(argsList): """ Takes the terms inputed and returns an organized list with no repeats and no poor word choices From 0d40924dde7c75971a8dd3da60cc04b3d5c860c7 Mon Sep 17 00:00:00 2001 From: Nick Cottrell Date: Thu, 17 Sep 2020 15:08:19 -0400 Subject: [PATCH 5/9] added section in valid term to turn versions into versions --- searchsploit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/searchsploit.py b/searchsploit.py index 149727a..3568de4 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -379,6 +379,11 @@ def validTerm(argsList): print("if you want to search with them anyway, run the command again with the -i arguement") exit() + # Converting certain terms into version terms + for i in range(len(argsList)): + if hasVersion(argsList[i]): + argsList[i] = getVersion(argsList[i])[0] # Getting first result from list + return argsList From 8a1896452dad3f0e9f313e34df26cc692ad91b11 Mon Sep 17 00:00:00 2001 From: Nick Cottrell Date: Thu, 17 Sep 2020 15:09:00 -0400 Subject: [PATCH 6/9] Implemented version search into database search --- searchsploit.py | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/searchsploit.py b/searchsploit.py index 3568de4..01897a9 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -407,25 +407,50 @@ def searchdb(path="", terms=[], cols=[], lim=-1): db = dbFile.read().split('\n') for lines in db: if (lines != ""): - for ex in parseArgs.exclude: + for ex in parseArgs.exclude: # Removing positive lines with excluded values if parseArgs.case and ex in lines: break elif ex in lines.lower(): break else: for term in terms: - if parseArgs.title: - line = lines.split(",")[2] - if parseArgs.case: - if term not in line: + if type(term) is str: # separate versions from search terms + if parseArgs.title: + line = lines.split(",")[2] + if parseArgs.case: + if term not in line: + break + elif term not in line.lower(): break - elif term not in line.lower(): - break - elif parseArgs.case: - if term not in lines: + elif parseArgs.case: + if term not in lines: + break + elif term not in lines.lower(): break - elif term not in lines.lower(): - break + elif type(term) is tuple: + line = lines.split(",")[2] + versions = getVersion(line) + versionCompatible = False # helps for the or status here + if (len(versions) == 0): + break # if no versions could be detected in line, throw out line + if (term[0] != "") and "<" not in term[0]: # version has r or s in front + for v in versions: + if (v[0].lower() != term[0]): + continue # skip this if version doesnt start with same tag + versionCompatible = versionCompatible or (cmpVersion(term[1], v[1]) >= 0) + # this will only ever be true if just one is true + if not versionCompatible: + break + elif "<" in term[0]: + for v in versions: + versionCompatible = versionCompatible or (cmpVersion(term[1], v[1]) <= 0) + if not versionCompatible: + break + else: + for v in versions: + versionCompatible = versionCompatible or (cmpVersion(term[1], v[1]) >= 0) + if not versionCompatible: + break else: for i in cols: space = lines.split(",") From 6db94f9334e7786621c3f852ff1da96194803357 Mon Sep 17 00:00:00 2001 From: Nick Cottrell Date: Thu, 17 Sep 2020 15:09:54 -0400 Subject: [PATCH 7/9] Fixed highlight term for version tuple use --- searchsploit.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/searchsploit.py b/searchsploit.py index 01897a9..4a96268 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -210,7 +210,7 @@ def drawline(lim): print(line) -def highlightTerm(line, term): +def highlightTerm(line: str, term)->str: """ Part one of new highlighting process. Highlights by adding :8 and :9 as escape characters as ansi takes several lines. the rest is compiled in separater unless autocomp is true\n @line: the phrase to be checked\n @term: the term that will be found in line and used to highlight the line\n @@ -220,6 +220,10 @@ def highlightTerm(line, term): if not parseArgs.colour: return line + # Adjustments for if term is version tuple + if type(term) is tuple: + term = term[1] # TODO: Make way to highlight line if fits version parameters + marker = 0 # marks where the term is first found term = term.lower() From 9079c5395e266175b95d71d7829b35fb8fd3e14e Mon Sep 17 00:00:00 2001 From: RadioLogic Date: Tue, 22 Sep 2020 11:34:27 -0400 Subject: [PATCH 8/9] Added Strict Function (#17) --- searchsploit.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/searchsploit.py b/searchsploit.py index 4a96268..1c53c9a 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -127,7 +127,8 @@ def scrapeRC(): help="Perform an EXACT match on exploit title (Default is AND) [Implies \"-t\"].") searchHeader.add_argument("-i", "--ignore", action="store_true", help="Adds any redundant term in despite it possibly giving false positives.") -# TODO: Add strict +searchHeader.add_argument("-s", "--strict", action="store_true", + help="Perform a strict search, so input values must exist, disabling fuzzy search for version range e.g. \"1.1\" would not be detected in \"1.0 < 1.3\"") searchHeader.add_argument("-t", "--title", action="store_true", help="Search JUST the exploit title (Default is title AND the file's path).") searchHeader.add_argument("--exclude", nargs="*", type=str, default=list(), metavar="[terms]", @@ -385,8 +386,9 @@ def validTerm(argsList): # Converting certain terms into version terms for i in range(len(argsList)): - if hasVersion(argsList[i]): - argsList[i] = getVersion(argsList[i])[0] # Getting first result from list + if not parseArgs.strict and hasVersion(argsList[i]): + # Getting first result from list + argsList[i] = getVersion(argsList[i])[0] return argsList @@ -432,6 +434,9 @@ def searchdb(path="", terms=[], cols=[], lim=-1): elif term not in lines.lower(): break elif type(term) is tuple: + # skipping if strict + if parseArgs.strict: + break line = lines.split(",")[2] versions = getVersion(line) versionCompatible = False # helps for the or status here From c4e5c6cb2a26a78789e75e09bb9f01ca8a366ed6 Mon Sep 17 00:00:00 2001 From: RadioLogic Date: Tue, 22 Sep 2020 11:50:52 -0400 Subject: [PATCH 9/9] Formatted Document --- searchsploit.py | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/searchsploit.py b/searchsploit.py index 1c53c9a..1e51fb4 100755 --- a/searchsploit.py +++ b/searchsploit.py @@ -211,7 +211,7 @@ def drawline(lim): print(line) -def highlightTerm(line: str, term)->str: +def highlightTerm(line: str, term) -> str: """ Part one of new highlighting process. Highlights by adding :8 and :9 as escape characters as ansi takes several lines. the rest is compiled in separater unless autocomp is true\n @line: the phrase to be checked\n @term: the term that will be found in line and used to highlight the line\n @@ -223,7 +223,8 @@ def highlightTerm(line: str, term)->str: # Adjustments for if term is version tuple if type(term) is tuple: - term = term[1] # TODO: Make way to highlight line if fits version parameters + # TODO: Make way to highlight line if fits version parameters + term = term[1] marker = 0 # marks where the term is first found term = term.lower() @@ -315,17 +316,23 @@ def findExploit(id): else: return i, exploit -baseVersion = re.compile(r'((?:(?:<)\s*)|(?:[RrVv]))?((?:\d+(?:\.(?:\d+|x))+)|(?:\d+))') -def hasVersion(term: str)->bool: +baseVersion = re.compile( + r'((?:(?:<)\s*)|(?:[RrVv]))?((?:\d+(?:\.(?:\d+|x))+)|(?:\d+))') + + +def hasVersion(term: str) -> bool: """ Returns true if the string contains any numbers that could resemble a verison """ return (len(baseVersion.findall(term)) > 0) + def getVersion(term: str): - return baseVersion.findall(term) # returns the first found object, taking tuple out of list + # returns the first found object, taking tuple out of list + return baseVersion.findall(term) -def cmpVersion(leftVersion: str, rightVersion: str)->int: + +def cmpVersion(leftVersion: str, rightVersion: str) -> int: """ Compares the two versions and returns an integer depending on which one if newer """ leftComponent = leftVersion.split(".") @@ -334,11 +341,11 @@ def cmpVersion(leftVersion: str, rightVersion: str)->int: if (leftComponent[i] != rightComponent[i]): # Dealing with wildcard scenarios if ("x" in leftComponent[i]): - if (len(re.findall("((?:{0})|(?:{1}))$".format(leftComponent[i],leftComponent[i].replace("x","\\d*")), rightComponent[i])) > 0): + if (len(re.findall("((?:{0})|(?:{1}))$".format(leftComponent[i], leftComponent[i].replace("x", "\\d*")), rightComponent[i])) > 0): # if the string matches itself or regex replacing all x's with \d's, then it is a match return 0 elif ("x" in rightComponent[i]): - if (len(re.findall("((?:{0})|(?:{1}))$".format(rightComponent[i],rightComponent[i].replace("x","\\d*")), leftComponent[i])) > 0): + if (len(re.findall("((?:{0})|(?:{1}))$".format(rightComponent[i], rightComponent[i].replace("x", "\\d*")), leftComponent[i])) > 0): # if the string matches itself or regex replacing all x's with \d's, then it is a match return 0 @@ -349,7 +356,9 @@ def cmpVersion(leftVersion: str, rightVersion: str)->int: for j in re.findall("([A-Za-z]+)", rightComponent[i]): rightComponent[i] = rightComponent[i].replace(j, "") return int(rightComponent[i]) - int(leftComponent[i]) - return len(rightComponent) - len(leftComponent) # if they are equal up their shortest version, then the latest is the longest version + # if they are equal up their shortest version, then the latest is the longest version + return len(rightComponent) - len(leftComponent) + def validTerm(argsList): """ Takes the terms inputed and returns an organized list with no repeats and no poor word choices @@ -413,14 +422,14 @@ def searchdb(path="", terms=[], cols=[], lim=-1): db = dbFile.read().split('\n') for lines in db: if (lines != ""): - for ex in parseArgs.exclude: # Removing positive lines with excluded values + for ex in parseArgs.exclude: # Removing positive lines with excluded values if parseArgs.case and ex in lines: break elif ex in lines.lower(): break else: for term in terms: - if type(term) is str: # separate versions from search terms + if type(term) is str: # separate versions from search terms if parseArgs.title: line = lines.split(",")[2] if parseArgs.case: @@ -439,25 +448,28 @@ def searchdb(path="", terms=[], cols=[], lim=-1): break line = lines.split(",")[2] versions = getVersion(line) - versionCompatible = False # helps for the or status here + versionCompatible = False # helps for the or status here if (len(versions) == 0): - break # if no versions could be detected in line, throw out line - if (term[0] != "") and "<" not in term[0]: # version has r or s in front + break # if no versions could be detected in line, throw out line + if (term[0] != "") and "<" not in term[0]: # version has r or s in front for v in versions: if (v[0].lower() != term[0]): - continue # skip this if version doesnt start with same tag - versionCompatible = versionCompatible or (cmpVersion(term[1], v[1]) >= 0) + continue # skip this if version doesnt start with same tag + versionCompatible = versionCompatible or ( + cmpVersion(term[1], v[1]) >= 0) # this will only ever be true if just one is true if not versionCompatible: break elif "<" in term[0]: for v in versions: - versionCompatible = versionCompatible or (cmpVersion(term[1], v[1]) <= 0) + versionCompatible = versionCompatible or ( + cmpVersion(term[1], v[1]) <= 0) if not versionCompatible: break else: for v in versions: - versionCompatible = versionCompatible or (cmpVersion(term[1], v[1]) >= 0) + versionCompatible = versionCompatible or ( + cmpVersion(term[1], v[1]) >= 0) if not versionCompatible: break else: