├── README.md ├── gtfo.py ├── gtfo.sh └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # GTFOPlus 2 | 3 | GTFOPlus is a helper script that relies on the GTFOBins repo to identify standard Linux binaries that could assist with privilege escalation. 4 | 5 | Deploy a gtfo.sh script to enumerate these binaries on your target machine. 6 | 7 | Example Usage: 8 | 9 | python3 gtfo.py -b awk -l shell | Spawning a shell with awk 10 | python3 gtfo.py -b awk -l all | Show all GTFO capabilities of awk 11 | python3 gtfo.py -b awk -l all --verbose | Increase verbosity + ascii art 12 | 13 | Using the gtfo.sh agent script: 14 | 15 | ./gtfo.sh > gtf.out | Run this on target machine. 16 | python3 gtfo.py -f gtf.out -l all | Show all capabilities for all 17 | | binaries gathered from gtfo.sh 18 | ## Setup 19 | 20 | In the same directory as that you cloned this repo, clone the GTFOBins Repo. 21 | 22 | git clone https://github.com/GTFOBins/GTFOBins.github.io.git 23 | python3 -m pip install -r requirements.txt 24 | 25 | ## Requirements 26 | 27 | python3 28 | pyyaml 29 | 30 | TODO: 31 | 32 | * Make gtf.out parser better. (Add parser for groups, perms, ownership etc.) 33 | * Pull GTFO bin capabilities from the repo as well. 34 | -------------------------------------------------------------------------------- /gtfo.py: -------------------------------------------------------------------------------- 1 | import argparse, os, sys, textwrap, yaml 2 | splash = """ 3 | ▄▄▄▄▄▄▄ ▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄ ▄ ▄▄▄▄ ▄ ▄▄▄▄▄▄▄ 4 | ▄ ▄▄ ░ ▀▀ ██▄▄▄▄ ▄▄ ░ ▄▄ ██ ▄▄ ██ ██ ▄▄ ██ ▄▄ ░ ▀▀ ▄ 5 | ▀▀▀█ ██ ▀▀██ ██ ▒ ▄▄ ██▀▀ ██ ▒ ██ ██ ▀▀▀ ██ ▒ ██ ██ ▒ ██ ▀▀▀▀▀██ █▀▀▀ 6 | ██▄▄▄█ ▄██▄▄▄██ ██ ░ ▀█▄▄▄█▀ ██ ░ ▄▄▄▄▄██ ▀█▄▄▄█▀ ██▄▄▄█▀ v0.1 7 | """ 8 | parser = argparse.ArgumentParser(description='GTFO+') 9 | parser.add_argument('-b', dest='bin',help='GTFO Bin') 10 | parser.add_argument('-l', dest='list',help='Capabilities to List') 11 | parser.add_argument('-f', dest='file',help='Read bins from file') 12 | parser.add_argument("--quiet", dest='quiet',help="Decreate Output Verbosity",action="store_true") 13 | parser.add_argument("--verbose", dest='verbose',help="increase output verbosity",action="store_true") 14 | args = parser.parse_args() 15 | binName = args.bin 16 | listWhat = args.list 17 | infile = args.file 18 | quietout = args.quiet 19 | verbose = args.verbose 20 | 21 | usage = """ 22 | gtfo.py is a helper for identifying standard Linux binaries that could assist 23 | with privilege escalation. 24 | Deploy a gtfo.sh script to enumerate these binaries on your target machine. 25 | 26 | Example Usage: 27 | 28 | python3 gtfo.py -b awk -l shell | Spawning a shell with awk 29 | python3 gtfo.py -b awk -l all | Show all GTFO capabilities of awk 30 | python3 gtfo.py -b awk -l all --verbose | Increase verbosity + ascii art 31 | python3 gtfo.py -f gtf.out -l all | Show all capabilities for all 32 | | binaries gathered from gtfo.sh 33 | """ 34 | 35 | gtfoPath = 'GTFOBins.github.io/_gtfobins/' 36 | 37 | listCommands = [ "all","bind-shell","capabilities","command","file-download","file-read","file-upload","file-write","library-load","limited-suid","non-interactive-bind-shell","non-interactive-reverse-shell","reverse-shell","shell","sudo","suid" ] 38 | 39 | def loadBin(binFile): 40 | with open(binFile,'r') as stream: 41 | try: 42 | cleaned = stream.readlines() 43 | cleaned = cleaned[:-1] 44 | cleaned = ''.join(cleaned) 45 | data = yaml.load(cleaned,Loader=yaml.FullLoader) 46 | except yaml.YAMLError as exc: 47 | print(exc) 48 | return data 49 | 50 | gtfoInfo = { 51 | "bind-shell": { 52 | "name": "Bind shell", 53 | "desc": "It can bind a shell to a local port to allow remote network access." 54 | }, 55 | "capabilities": { 56 | "name": "Capabilities", 57 | "desc": "It can manipulate its process UID and can be used on Linux as a backdoor to maintain elevated privileges with the CAP_SETUID capability set. This also works when executed by another binary with the capability set." 58 | }, 59 | "command": { 60 | "name": "Command", 61 | "desc": "It can be used to break out from restricted environments by running non-interactive system commands." 62 | }, 63 | "file-download": { 64 | "name": "File download", 65 | "desc": "It can download remote files." 66 | }, 67 | "file-read": { 68 | "name":"File read", 69 | "desc":"It reads data from files, it may be used to do privileged reads or disclose files outside a restricted file system." 70 | }, 71 | "file-upload": { 72 | "name": "File upload", 73 | "desc": "It can exfiltrate files on the network." 74 | }, 75 | "file-write": { 76 | "name": "File write", 77 | "desc": "It writes data to files, it may be used to do privileged writes or write files outside a restricted file system." 78 | }, 79 | "library-load": { 80 | "name": "Library load", 81 | "desc": "It loads shared libraries that may be used to run code in the binary execution context." 82 | }, 83 | "limited-suid": { 84 | "name": "Limited SUID", 85 | "desc": "It runs with the SUID bit set and may be exploited to access the file system, escalate or maintain access with elevated privileges working as a SUID backdoor. If it is used to run commands it only works on systems like Debian that allow the default sh shell to run with SUID privileges." 86 | }, 87 | "non-interactive-bind-shell": { 88 | "name": "Non-interactive bind shell", 89 | "desc": "It can bind a non-interactive shell to a local port to allow remote network access." 90 | }, 91 | "non-interactive-reverse-shell": { 92 | "name": "Non-interactive reverse shell", 93 | "desc": "It can send back a non-interactive reverse shell to a listening attacker to open a remote network access." 94 | }, 95 | "reverse-shell": { 96 | "name": "Reverse shell", 97 | "desc": "It can send back a reverse shell to a listening attacker to open a remote network access." 98 | }, 99 | "shell": { 100 | "name": "Shell", 101 | "desc": "It can be used to break out from restricted environments by spawning an interactive system shell." 102 | }, 103 | "sudo": { 104 | "name": "Sudo", 105 | "desc": "It runs in privileged context and may be used to access the file system, escalate or maintain access with elevated privileges if enabled on sudo." 106 | }, 107 | "suid": { 108 | "name": "SUID", 109 | "desc": "It runs with the SUID bit set and may be exploited to access the file system, escalate or maintain access with elevated privileges working as a SUID backdoor. If it is used to run sh -p, omit the -p argument on systems like Debian that allow the default sh shell to run with SUID privileges." 110 | } 111 | } 112 | 113 | print(splash) 114 | 115 | def getBinInfo(qBin): 116 | binTitle = "───[ {} ]".format(binName) 117 | titleFill = "─"*(80-(len(binName)+7)) 118 | print(binTitle+titleFill) 119 | try: 120 | print(" "+"\n".join(textwrap.wrap(qBin["description"], 75))) 121 | except KeyError: 122 | print(" ") 123 | binProp = qBin["functions"] 124 | if listWhat == "all": 125 | for gbin in binProp: 126 | gtfoDesc = gtfoInfo[gbin]["desc"] # from local 127 | binCMD = binProp[gbin][0]["code"].split("\n") # from file 128 | print("┌─ {}".format(gbin)) 129 | print("│ "+"\n│ ".join(textwrap.wrap(gtfoDesc, 75))) 130 | print("└─ Usage:\n") 131 | for cmd in binCMD: 132 | if cmd == "": 133 | pass 134 | else: 135 | print(" {}".format(cmd)) 136 | print(" ") 137 | else: 138 | gbin = listWhat 139 | try: 140 | gtfoDesc = gtfoInfo[gbin]["desc"] #from local 141 | binCMD = binProp[gbin][0]["code"].split("\n") # from file 142 | print("┌─ {}".format(gbin)) # was gtfoName 143 | print("│ "+"\n│ ".join(textwrap.wrap(gtfoDesc, 75))) 144 | print("└─ Usage:\n") 145 | for cmd in binCMD: 146 | if cmd == "": 147 | pass 148 | else: 149 | print(" {}".format(cmd)) 150 | print(" ") 151 | except: 152 | pass 153 | 154 | def quietBinInfo(qBin): 155 | padding = " "*(round((80-len(binName)))) # To right align bin names 156 | print("{}{}".format(padding,binName)) # that makes it easier to 157 | print("{}{}".format(padding,'-'*len(binName))) # read :) 158 | binProp = qBin["functions"] 159 | if listWhat == "all": 160 | for gbin in binProp: 161 | binCMD = binProp[gbin][0]["code"].split("\n") 162 | print("[{}]".format(gbin)) 163 | for cmd in binCMD: 164 | if cmd == "": 165 | pass 166 | else: 167 | print(" {}".format(cmd)) 168 | print(" ") 169 | else: 170 | gbin = listWhat 171 | try: 172 | binCMD = binProp[gbin][0]["code"].split("\n") 173 | print("[{}]".format(gbin)) 174 | for cmd in binCMD: 175 | if cmd == "": 176 | pass 177 | else: 178 | print(" {}".format(cmd)) 179 | print(" ") 180 | except: 181 | pass 182 | 183 | def getAvailableBins(): 184 | filez = os.listdir(gtfoPath) 185 | availableBins = [] 186 | for f in filez: 187 | if f[0] == ".": 188 | pass 189 | else: 190 | bb = f.split(".md")[0] 191 | availableBins.append(bb) 192 | return availableBins 193 | 194 | def parseInfile(inputFile): 195 | with open(inputFile) as f: 196 | binz = [] 197 | binListing = f.readlines() 198 | for b in binListing: # clean this ish up 199 | b = b.split(" ") 200 | p = b[-1:] # Skipping over groups for now 201 | p = p[0].split("\n")[0] 202 | p = p.split("/")[-1:] 203 | binz.append(p[0]) 204 | return binz 205 | 206 | def showCapabilities(): 207 | for i in range(0,len(listCommands)): 208 | print(" " + listCommands[i]) 209 | 210 | # Main functionality 211 | try: 212 | # IF we are processing a gtfo.sh agent output 213 | if infile: 214 | binz = parseInfile(infile) # Should create an object of the bins in the file output 215 | binsAvailable = getAvailableBins() 216 | print("[!] Listing {} GTFO capabilities for {}\n".format(listWhat,infile)) 217 | for b in binz: 218 | if b in binsAvailable: 219 | binArg = b 220 | binName = b 221 | binSelect = gtfoPath+binArg+".md" 222 | binInfo = loadBin(binSelect) 223 | if verbose: 224 | getBinInfo(binInfo) 225 | else: 226 | quietBinInfo(binInfo) 227 | # Otherwise we are processing a single binary 228 | else: 229 | if listWhat not in listCommands: 230 | exit() 231 | binArg = binName 232 | binSelect = gtfoPath+binArg+".md" 233 | binInfo = loadBin(binSelect) 234 | print("[!] Listing {} GTFO capabilities for {}\n".format(listWhat,binArg)) 235 | if verbose: 236 | getBinInfo(binInfo) 237 | else: 238 | quietBinInfo(binInfo) 239 | except: 240 | print(usage) 241 | binsAvailable = getAvailableBins() 242 | print("[Available Binaries] Specify with the -b flag") 243 | for b in binsAvailable: 244 | if b == binsAvailable[-1]: 245 | print(b) 246 | else: 247 | print(b,end=', ') 248 | print("\n[Available Capabilities] Specify with the -l flag") 249 | showCapabilities() 250 | -------------------------------------------------------------------------------- /gtfo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | groups 3 | bins=("apt-get" "apt" "aria2c" "ash" "awk" "base64" "bash" "busybox" "cat" "chmod" "chown" "cp" "cpulimit" "crontab" "csh" "curl" "cut" "dash" "date" "dd" "diff" "docker" "easy_install" "ed" "emacs" "env" "expand" "expect" "facter" "find" "finger" "flock" "fmt" "fold" "ftp" "gdb" "git" "grep" "head" "ionice" "jjs" "journalctl" "jq" "jrunscript" "ksh" "ld.so" "less" "ltrace" "lua" "mail" "make" "man" "more" "mount" "mv" "mysql" "nano" "nc" "nice" "nl" "nmap" "node" "od" "perl" "pg" "php" "pico" "pip" "puppet" "python" "red" "rlwrap" "rpm" "rpmquery" "rsync" "ruby" "scp" "sed" "setarch" "sftp" "shuf" "smbclient" "socat" "sort" "sqlite3" "ssh" "stdbuf" "strace" "tail" "tar" "taskset" "tclsh" "tcpdump" "tee" "telnet" "tftp" "time" "timeout" "ul" "unexpand" "uniq" "unshare" "vi" "vim" "watch" "wget" "whois" "wish" "xargs" "xxd" "zip" "zsh") 4 | for i in "${bins[@]}" 5 | do 6 | if which $i > /dev/null; then 7 | ls -lah $(which $i) 8 | fi 9 | done 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | --------------------------------------------------------------------------------