One thing you have to keep in mind when securing your UNIX-based systems is the presence of binaries that have inappropriate permissions.
I recently released a working proof-of-concept (POC) code for identifying vulnerable UNIX binaries that have their SUID bit set.
A little background:
The Set owner user ID (SUID) bit denotes which user ID a given binary should be run as. Generally, a UNIX program inherits permissions from the user that runs it (i.e. program A executed by user Alice will have Alice’s permissions); with the SUID bit set, the program will run with the given user’s ID instead (e.g. program A has SUID bit set to its owner, Bob; when Alice runs program A, it will run as user Bob).
In some cases, hackers can abuse the SUID having been set in certain programs as a means for privilege escalation; in other words, it may be possible for a hacker to leverage a program running with a different user ID to attain a foothold as said user. This is especially problematic if the binary’s owner is root.
The website GTFObins maintains a curated list of binaries that - if they were to have their SUID bit set - can result in privilege escalation as described above.
Now, there are several good tools that exist out there already that an attacker can use in their post-exploitation efforts to identify binaries on the compromised system, such as linPEAS and suid3num. However, in some of my penetration testing exercises, I haven’t been able to get either to work (due to an absence of python on the compromised system or shell restrictions); additionally, there are some instances where I might have remote code execution (RCE) on a target, but not an interactive shell.
In such instances, it can still be useful to identify vulnerable binaries. This niche circumstance is where my tool, SUIDcheck, is meant to serve.
The Program
SUIDcheck takes in as an argument the name of a file containing a list of binary paths. The recommended command to run to generate such a list is:
find / -user root -perm -4000 -print 2>/dev/null
or
find / -perm -u=s -type f 2>/dev/null
The script then queries GTFObins to attain the latest binaries before evaluating the output.
#!/usr/bin/env python3
#Save the output from searching for SUID binaries:
#find / -perm -u=s -type f 2>/dev/null
#find / -user root -perm -4000 -print 2>/dev/null
import requests #needed to inquire GTFObins latest list
from bs4 import BeautifulSoup #needed to parse GTFObins HTML response
import sys #needed for ingesting commandline arguments
import os.path
from os import path
IBlack="\033[0;90m" # Black
IRed="\033[0;91m" # Red
IGreen="\033[0;92m" # Green
IYellow="\033[0;93m" # Yellow
IBlue="\033[0;94m" # Blue
IPurple="\033[0;95m" # Purple
ICyan="\033[0;96m" # Cyan
IWhite="\033[0;97m" # White
banner = IBlue + "████████████████████████████████████████████████████\n"
banner += ICyan + "█─▄▄▄▄█▄─██─▄█▄─▄█▄─▄▄▀█─▄▄▄─█─█─█▄─▄▄─█─▄▄▄─█▄─█─▄█\n"
banner += ICyan + "█▄▄▄▄─██─██─███─███─██─█─███▀█─▄─██─▄█▀█─███▀██─▄▀██\n"
banner += IWhite + "▀▄▄▄▄▄▀▀▄▄▄▄▀▀▄▄▄▀▄▄▄▄▀▀▄▄▄▄▄▀▄▀▄▀▄▄▄▄▄▀▄▄▄▄▄▀▄▄▀▄▄▀\n"
print(banner)
#Default binaries found on UNIX
#Pulled from suid3num
defaults = ["arping", "at", "bwrap", "chfn", "chrome-sandbox", "chsh", "dbus-daemon-launch-helper", "dmcrypt-get-device", "exim4", "fusermount", "gpasswd", "helper", "kismet_capture", "lxc-user-nic", "mount", "mount.cifs", "mount.ecryptfs_private", "mount.nfs", "newgidmap", "newgrp", "newuidmap", "ntfs-3g", "passwd", "ping", "ping6", "pkexec", "polkit-agent-helper-1", "pppd", "snap-confine", "ssh-keysign", "su", "sudo", "traceroute6.iputils", "ubuntu-core-launcher", "umount", "VBoxHeadless", "VBoxNetAdpCtl", "VBoxNetDHCP", "VBoxNetNAT", "VBoxSDL", "VBoxVolInfo", "VirtualBoxVM", "vmware-authd", "vmware-user-suid-wrapper", "vmware-vmx", "vmware-vmx-debug", "vmware-vmx-stats", "Xorg.wrap"]
def overhead():
#Error messaging in case there isn't a proper number of arguments
if len(sys.argv) != 2:
print("Improper number of arguments")
sys.exit(0)
else:
#check if the argument passed is a file
existCheck = path.exists(sys.argv[1])
if existCheck == False:
print("[-] Specified file '" + str(sys.argv[1]) + "' does not exist")
sys.exit(0)
fileCheck = path.isfile(sys.argv[1])
if fileCheck == False:
print("[-] Specified file '" + str(sys.argv[1]) + "' is not a file")
sys.exit(0)
arg = sys.argv[1]
overhead()
print("[+] Checking with gtfobins")
response = requests.get('https://gtfobins.github.io/#+suid')
resp = requests.get('https://gtfobins.github.io/#+reverse%20shell')
if response.status_code == 200 and resp.status_code == 200:
print("[+] Parsing gtfobins results")
elif response.status_code == 404 or resp.status_code == 404:
sys.exit("[-] Unable to contact gtfobins")
soup = BeautifulSoup(response.text, 'html.parser')
rsoup = BeautifulSoup(resp.text, 'html.parser')
gtfolist = {}
revlist = {}
#find all of the html href tags that contain #suid
#ex: /gtfobins/cupsfilter/#suid
#Then filter out the path before/after the vulnerable binary
#ex: cupsfilter
for tag in soup.find_all('a', href=True):
if tag['href'].endswith('#suid') and tag['href'][0] != '#':
suid = tag['href']
suid = suid[10:]
entry = suid.split("/",1)
gtfolist[str(entry[0])] = "suid"
#print(entry[0])
for tag in rsoup.find_all('a', href=True):
if tag['href'].endswith('#reverse-shell') and tag['href'][0] != '#':
suid = tag['href']
suid = suid[10:]
entry = suid.split("/",1)
revlist[str(entry[0])] = "suid"
#print(entry[0])
#print(gtfolist)
#Ingesting the file
with open(str(arg)) as f:
lines = f.read().splitlines()
#read all the lines from the file
#check it against the database
print()
print(IRed + "SUID binaries discovered!")
for line in lines:
binary = os.path.basename(line)
if binary in gtfolist:
print(IWhite + binary)
print()
print(IGreen + "These binaries may be used to attain a reverse shell")
for line in lines:
binary = os.path.basename(line)
if binary in revlist:
print(IWhite + binary)
print()
print(IYellow + "These binaries are installed on linux by default")
for line in lines:
binary = os.path.basename(line)
if binary in defaults:
print(IWhite + binary)
To be sure, the code is nothing fancy; but it made for a nice project that I could work on.