Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
start at code cleanup; PH in this branch will not work
- Loading branch information
Showing
11 changed files
with
478 additions
and
378 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf8 -*- | ||
# PlumHound (phCLIManagement.py) - Management of Command Line Arguments | ||
# https://github.com/PlumHound/PlumHound | ||
# License GNU GPL3 | ||
|
||
# Import Python Modules | ||
import argparse | ||
|
||
def SetupArguments(): | ||
parser = argparse.ArgumentParser(description="BloodHound Wrapper for Blue/Purple Teams; v01.070a", add_help=True, epilog="For more information see https://plumhound.DefensiveOrigins.com") | ||
pgroupc = parser.add_argument_group('DATABASE') | ||
pgroupc.add_argument("-s", "--server", type=str, help="Neo4J Server", default="bolt://localhost:7687") | ||
pgroupc.add_argument("-u", "--username", default="neo4j", type=str, help="Neo4J Database Useranme") | ||
pgroupc.add_argument("-p", "--password", default="neo4jj", type=str, help="Neo4J Database Password") | ||
pgroupc.add_argument("--UseEnc", default=False, dest="UseEnc", help="Use encryption when connecting.", action='store_true') | ||
|
||
pgroupx = parser.add_mutually_exclusive_group(required="True") | ||
pgroupx.add_argument("--easy", help="Test Database Connection, Returns Domain Users to stdout", action='store_true') | ||
pgroupx.add_argument("-x", "--TaskFile", dest="TaskFile", type=str, help="Specify a PlumHound TaskList File") | ||
pgroupx.add_argument("-q," "--QuerySingle", dest="QuerySingle", type=str, help="Specify a Single Cypher Query") | ||
pgroupx.add_argument("-bp," "--BusiestPath", dest="BusiestPath", nargs='+', default=False, type=str, help="Find the X Shortest Paths that give the most users a path to Domain Admins. Need to specified [short|all] for shortestpath and the number of results. Ex: PlumHound -cu all 3") | ||
pgroupx.add_argument("-ap," "--AnalyzePath", dest="AnalyzePath", nargs='+', default=False, type=str, help="Analyze 'Attack Paths' between two nodes and find which path needs to be remediated to brake the path.") | ||
|
||
pgroupo = parser.add_argument_group('OUTPUT', "Output Options (For single cypher queries only. --These options are ignored when -x or --easy is specified.") | ||
pgroupo.add_argument("-t", "--title", dest="title", default="Adhoc Query", type=str, help="Report Title for Single Query [HTML,CSV,Latex]") | ||
pgroupo.add_argument("--of", "--OutFile", dest="OutFile", default="PlumHoundReport", type=str, help="Specify a Single Cypher Query") | ||
pgroupo.add_argument("--op", "--OutPath", dest="path", default="reports//", type=str, help="Specify an Output Path for Reports") | ||
pgroupo.add_argument("--ox", "--OutFormat", dest="OutFormat", default="stdout", type=str, help="Specify the type of output", choices=['stdout', 'HTML', 'CSV']) | ||
|
||
pgrouph = parser.add_argument_group('HTML', "Options for HTML Output (For single queries or TaskLists") | ||
pgrouph.add_argument("--HTMLHeader", dest="HTMLHeader", type=str, default="template//head.html", help="HTML Header (file) of Report") | ||
pgrouph.add_argument("--HTMLFooter", dest="HTMLFooter", type=str, default="template//tail.html", help="HTML Footer (file) of Report") | ||
pgrouph.add_argument("--HTMLCSS", dest="HTMLCSS", type=str, default="template//html.css", help="Specify a CSS template for HTML Output") | ||
|
||
pgroupv = parser.add_argument_group('VERBOSE' "Set verbosity") | ||
pgroupv.add_argument("-v", "--verbose", type=int, default="100", help="Verbosity 0-1000, 0 = quiet") | ||
|
||
args = parser.parse_args() | ||
|
||
return args | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf8 -*- | ||
# PlumHound (phCheckPython.py) - Check for appropriate version of python | ||
# https://github.com/PlumHound/PlumHound | ||
# License GNU GPL3 | ||
|
||
import sys | ||
|
||
def CheckPython(): | ||
if sys.version_info < (3, 0, 0): | ||
print(__file__ + ' requires Python 3, while Python ' + str(sys.version[0] + ' was detected. Terminating. ')) | ||
sys.exit(1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf8 -*- | ||
# PlumHound (phDatabase.py) Database Connection Management | ||
# https://github.com/PlumHound/PlumHound | ||
# License GNU GPL3 | ||
|
||
from neo4j import GraphDatabase | ||
from lib.phLoggy import Loggy as Loggy | ||
|
||
# Setup Database Connection | ||
def setup_database_conn(phArgs): | ||
Loggy(phArgs.verbose,900, "------ENTER: SETUP_DATABASE_CONN-----") | ||
Loggy(phArgs.verbose,200, "[!] Attempting to connect to your Neo4j project using {}:{} @ {} {}.".format(phArgs.username, phArgs.password, phArgs.server, "[ENCRYPTED]" if args.UseEnc else "[UNECNCRYPTED]")) | ||
try: | ||
if args.UseEnc: | ||
Loggy(phArgs.verbose,200, " Using Neo4j encryption") | ||
driver_connection = GraphDatabase.driver(phArgs.server, auth=(phArgs.username, phArgs.password), encrypted=True) | ||
else: | ||
Loggy(phArgs.verbose,200, " Not using Neo4j encryption") | ||
driver_connection = GraphDatabase.driver(phArgs.server, auth=(phArgs.username, phArgs.password), encrypted=False) | ||
Loggy(phArgs.verbose,200, "[+] Success!") | ||
return driver_connection | ||
except Exception: | ||
Loggy(phArgs.verbose,100, "There was a problem. Check username, password, and server parameters.") | ||
Loggy(phArgs.verbose,100, "[X] Database connection failed!") | ||
exit() | ||
Loggy(phArgs.verbose,900, "------EXIT: SETUP_DATABASE_CONN-----") | ||
|
||
def close_database_con(phArgs,connectiondriver): | ||
Loggy(phArgs.verbose,900, "------ENTER: CLOSE_DATABASE_CONN-----") | ||
connectiondriver.close | ||
return False | ||
Loggy(phArgs.verbose,200, "[+] Closed Database Connection!") | ||
exit() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf8 -*- | ||
# PlumHound (phLoggy.py) - Logging Function | ||
# https://github.com/PlumHound/PlumHound | ||
# License GNU GPL3 | ||
|
||
# Loggy Function for lazy debugging | ||
def Loggy(hurdle, level, notice): | ||
if level <= hurdle: | ||
if level <= 100: | ||
print("[*]" + notice) | ||
elif level < 500: | ||
print("[!]" + notice) | ||
else: | ||
print("[*]" + notice) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf8 -*- | ||
# PlumHound - Output Delivery and Parsing Functions(phdeliver.py) | ||
# https://github.com/PlumHound/PlumHound | ||
# License GNU GPL3 | ||
|
||
#Python Libraries | ||
import sys | ||
import ast | ||
import csv | ||
from tabulate import tabulate | ||
from datetime import date | ||
|
||
#Plumhound Modules | ||
from lib.phLoggy import Loggy as Loggy | ||
|
||
|
||
def SenditOut(list_KeysList, Processed_Results_List, OutFormat, OutFile, OutPath, Title, HTMLHeader, HTMLFooter, HTMLCSS): | ||
Loggy(900, "------ENTER: SENDITOUT-----") | ||
# Quick fix if keys returned no records to properly rebuild the keys list of 0, instead of int(0) | ||
if isinstance(list_KeysList, int): | ||
list_KeysList = [] | ||
output = "" | ||
|
||
if OutFormat == "CSV": | ||
Loggy(100, "Beginning Output CSV:" + OutPath + OutFile) | ||
with open(OutPath + OutFile, "w", newline="") as f: | ||
Loggy(500, "KeyType: " + str(type(list_KeysList))) | ||
Loggy(500, "KeyList: " + str((list_KeysList))) | ||
writer = csv.writer(f) | ||
ModKeyList = ast.literal_eval("[" + str(list_KeysList) + "]") | ||
Loggy(500, "KeyTypeMod: " + str(type(ModKeyList))) | ||
Loggy(500, "KeyListMod: " + str(ModKeyList)) | ||
writer.writerows(ModKeyList) | ||
Loggy(500, "ResultsType: " + str(type(Processed_Results_List))) | ||
Loggy(999, "ResultsList: " + str(Processed_Results_List)) | ||
writer.writerows(Processed_Results_List) | ||
return True | ||
|
||
if OutFormat == "STDOUT": | ||
print() | ||
output = tabulate(Processed_Results_List, list_KeysList, tablefmt="simple") | ||
print(output) | ||
return True | ||
|
||
if OutFormat == "HTML": | ||
Loggy(100, "Beginning Output HTML:" + OutFile) | ||
|
||
output = tabulate(Processed_Results_List, list_KeysList, tablefmt="html") | ||
HTMLCSS_str = "" | ||
HTMLHeader_str = "" | ||
HTMLFooter_str = "" | ||
HTMLPre_str = "<HTML><head>" | ||
HTMLMId_str = "</head><Body>" | ||
HTMLEnd_str = "</body></html>" | ||
if HTMLHeader: | ||
with open(HTMLHeader, 'r') as header: | ||
HTMLHeader_str = header.read() | ||
HTMLHeader_str = ReplaceHTMLReportVars(HTMLHeader_str, Title) | ||
|
||
if HTMLFooter: | ||
with open(HTMLFooter, 'r') as footer: | ||
HTMLFooter_str = footer.read() | ||
HTMLFooter_str = ReplaceHTMLReportVars(HTMLFooter_str, Title) | ||
|
||
if HTMLCSS: | ||
with open(HTMLCSS, 'r') as css: | ||
HTMLCSS_str = "<style>\n" + css.read() + "\n</style>" | ||
|
||
Loggy(500, "File Writing " + OutPath + OutFile) | ||
output = HTMLPre_str + HTMLCSS_str + HTMLMId_str + HTMLHeader_str + output + HTMLFooter_str + HTMLEnd_str | ||
fsys = open(OutPath + OutFile, "w") | ||
fsys.write(output) | ||
fsys.close | ||
return True | ||
Loggy(900, "------EXIT: SENDITOUT-----") | ||
|
||
|
||
def FullSenditOut(Processed_Results_List, OutPath, HTMLHeader, HTMLFooter, HTMLCSS): | ||
Loggy(900, "------ENTER: FULLSENDITOUT-----") | ||
|
||
list_KeysList = ["Title", "Count", "Further Details"] | ||
OutFile = "Report.html" | ||
Title = "Full Report Details" | ||
|
||
Loggy(100, "Beginning Output HTML:" + OutFile) | ||
|
||
for entry in Processed_Results_List: | ||
filename = entry[2] | ||
entry[2] = "<a href=\"" + filename + "\">Details</a>" | ||
|
||
output = str(tabulate(Processed_Results_List, list_KeysList, tablefmt="html")) | ||
output = output.replace("<","<") | ||
output = output.replace(">",">") | ||
output = output.replace(""",'"') | ||
|
||
HTMLCSS_str = "" | ||
HTMLHeader_str = "" | ||
HTMLFooter_str = "" | ||
HTMLPre_str = "<HTML><head>" | ||
HTMLMId_str = "</head><Body>" | ||
HTMLEnd_str = "</body></html>" | ||
if HTMLHeader: | ||
with open(HTMLHeader, 'r') as header: | ||
HTMLHeader_str = header.read() | ||
HTMLHeader_str = ReplaceHTMLReportVars(HTMLHeader_str, Title) | ||
|
||
if HTMLFooter: | ||
with open(HTMLFooter, 'r') as footer: | ||
HTMLFooter_str = footer.read() | ||
HTMLFooter_str = ReplaceHTMLReportVars(HTMLFooter_str, Title) | ||
|
||
if HTMLCSS: | ||
with open(HTMLCSS, 'r') as css: | ||
HTMLCSS_str = "<style>\n" + css.read() + "\n</style>" | ||
|
||
Loggy(500, "File Writing " + OutPath + OutFile) | ||
output = HTMLPre_str + HTMLCSS_str + HTMLMId_str + HTMLHeader_str + output + HTMLFooter_str + HTMLEnd_str | ||
fsys = open(OutPath + OutFile, "w") | ||
fsys.write(output) | ||
fsys.close | ||
Loggy(100, "Full report written to Report.html") | ||
return True | ||
Loggy(900, "------EXIT: FULLSENDITOUT-----") | ||
|
||
|
||
def ReplaceHTMLReportVars(InputStr, Title): | ||
sOutPut = InputStr.replace("--------PH_TITLE-------", str(Title)) | ||
sOutPut = sOutPut.replace("--------PH_DATE-------", str(date.today())) | ||
return sOutPut |
Oops, something went wrong.