Manual Enumeration
When gaining initial access to a target,
one of the first things we should identify is the user context.
We can use the id command to gather user context information.
We can do so by connecting through SSH as the joe user to our Debian lab machine.
The output reveals that we are operating as the joe user,
which has a User Identifier and Group Identifier of 1000.
The user joe is also part of other groups that are out of scope for this Topic.
To enumerate all users, we can simply read the contents of the /etc/passwd file.
The passwd file lists several user accounts, including accounts used
by various services on the target machine such as www-data and sshd.
This indicates that a web server & an SSH server are likely installed on the system.
We can now zoom in on our current user’s data.
“joe” Indicates the username used for login.
This field typically contains the hashed version of the user’s password.
In this case, the value x means that the entire password hash
is contained in the /etc/shadow file.
Aside from the regular root user that has always a UID of 0,
Linux starts counting regular user IDs from 1000.
This value is also called real user ID.
This 1000 represents the user’s specific Group ID.
This field generally contains a description about the user,
often simply repeating username information.
This describes the user’s home directory prompted upon login.
And this indicates the default interactive shell, if one exists.
In addition to the joe user, we also notice another user named eve,
and we can infer this is a standard user since it has a configured home folder
/home/eve.
On the other hand, system services are configured with the nologin home folder,
where the nologin statement is used to block any remote
or local login for service accounts.
Enumerating all users on a target machine can help identify potential high-privilege
user accounts we could target in an attempt to elevate our privileges.
Next, a machine’s hostname can often provide clues about its functional roles.
More often than not, the hostnames will include identifiable abbreviations
such as web for a web server,
db for a database server, dc for a domain controller, and more.
On most Linux distributions,
we can find the hostname embedded in the command prompt.
However, we should rely only on system commands
to retrieve the target’s information,
since sometimes the prompt’s text can be deceiving.
We can discover the hostname with the aptly-named hostname command.
Enterprises often enforce a naming convention scheme for hostnames,
so they can be categorized by location,
description, operating system, and service level.
In our case, the hostname is comprised of only two parts:
the OS type and the description.
Identifying the role of a machine can help us focus our information gathering efforts
by increasing the context surrounding the host.
At some point during the privilege escalation process,
we may need to rely on kernel exploits that specifically exploit vulnerabilities
in the core of a target’s operating system
These types of exploits are built for a very specific type of target,
specified by a particular operating system and version combination.
Since attacking a target with a mismatched kernel exploit
can lead to system instability or even a crash,
we must gather precise information about the target.
The /etc/issue and /etc/release files
contain information about the operating system release and version.
The issue and os-release files located in the /etc directory
contain the operating system version, Debian 10,
and release-specific information, including the distribution codename buster
The command uname -a outputs the kernel version and architecture.
Next, let’s explore which running processes and services
may allow us to elevate our privileges.
For this to occur, the process must run in the context of a privileged account
and must either have insecure permissions
or allow us to interact with it in unintended ways.
We can list system processes (including those run by privileged users)
with the ps command.
We’ll use the a and x flags to list all processes with or without a tty
and the u flag to list the processes in a user-readable format.
The output lists several processes running as root
that are worth researching for possible vulnerabilities.
We’ll notice the ps command we ran
is also listed in the output, owned by the current user.
We can also filter the specific user-owned process from the output
with the appropriate username.
The next step in our analysis of the target host is to review available network
interfaces, routes, and open ports.
This information can help us determine if the compromised target is connected
to multiple networks and therefore could be used as a pivot.
The presence of specific virtual interfaces may also indicate the existence
of virtualization or antivirus software.
We can also investigate port bindings to see if a running service is only available
on a loopback address, rather than on a routable one.
Investigating a privileged program or service
listening on the loopback interface could expand our attack surface
and increase our probability of a successful privilege escalation attack.
Depending on the version of Linux, we can list the TCP/IP configuration
of every network adapter with either ifconfig or ip.
While the former command displays interface statistics,
the latter provides a compact version of the same information.
Both commands accept the a flag to display all information available.
Based on the output, the Linux client is also connected to more than one network.
We can display network routing tables with either route or routel,
depending on the Linux distribution and version.
Both commands provide similar information.
Finally, we can display active network connections and listening ports
using either netstat or ss,
both of which accept the same arguments.
For example, we can list all connections with -a,
avoid hostname resolution (which may stall the command execution) with -n,
and list the process name the connection belongs to with -p.
The output lists the various listening ports and active sessions,
including our own active SSH connection and its listening socket.
Continuing with our baseline enumeration, let’s focus next on firewall rules.
In general, we’re primarily interested in a firewall’s state, profile, and rules
during the remote exploitation phase of an assessment.
However, this information can also be useful during privilege escalation.
For example, if a network service is not remotely accessible
because it is blocked by the firewall,
it is generally accessible locally via the loopback interface.
If we can interact with these services locally,
we may be able to exploit them to escalate our privileges on the local system.
During this phase, we can also gather information about inbound and outbound
port filtering to facilitate port forwarding and tunneling
when it’s time to pivot to an internal network.
On Linux-based systems,
we must have root privileges to list firewall rules with iptables.
However, depending on how the firewall is configured,
we may be able to glean information about the rules as a standard user.
For example, the iptables-persistent package on Debian Linux
saves firewall rules in specific files under /etc/iptables by default.
These files are used by the system to restore netfilter rules at boot time.
These files are often left with weak permissions,
allowing them to be read by any local user on the target system.
We can also search for files created by the iptables-save command,
which is used to dump the firewall configuration
to a file specified by the user.
This file is then usually used as input for the iptables-restore command
and used to restore the firewall rules at boot time.
If a system administrator had ever run this command,
we could search the configuration directory (/etc)
or grep the file system for iptables commands to locate the file.
If the file has insecure permissions, we could use the contents
to infer the firewall configuration rules running on the system.
Since this file is read-only by any users other than root,
we can inspect its contents.
We’ll notice a non-default rule
that explicitly allows the destination port 1999.
This configuration detail stands out and should be noted for later investigation.
Next, let’s examine scheduled tasks that attackers commonly leverage
during privilege escalation attacks.
Systems acting as servers
often periodically execute various automated, scheduled tasks.
When these systems are misconfigured, or the user-created files are left with
insecure permissions, we can modify these files that will be executed
by the scheduling system at a high privilege level.
The Linux-based job scheduler is known as cron.
Scheduled tasks are listed under the /etc/cron.* directories,
where * represents the frequency at which the task will run.
For example, tasks that will be run daily can be found under /etc/cron.daily.
Each script is listed in its own subdirectory.
Listing the directory contents, we notice several tasks scheduled to run daily.
It is worth noting that system administrators often add their own
scheduled tasks in the /etc/crontab file.
These tasks should be inspected carefully for insecure file permissions,
since most jobs in this particular file will run as root.
To view the current user’s scheduled jobs,
we can run crontab followed by the -l parameter.
In the output, only the commented instructions are present,
meaning no cron job has been configured for the user joe.
However, if we try to run the same command with the sudo prefix,
we discover that a backup script is scheduled to run every minute.
Listing cron jobs using sudo reveals jobs run by the root user.
In this example, it shows a backup script running as root.
If this file has weak permissions,
we may be able to leverage it to escalate our privileges.
At some point, we may need to leverage an exploit to escalate our local privileges.
If so, our search for a working exploit begins with the enumeration
of all installed applications, noting the version of each.
We can use this information to search for a matching exploit.
Manually searching for this information
could be very time consuming and ineffective,
so we’ll learn how to automate this process in the next section.
However, we should know how to manually query installed packages as this is needed
to corroborate information obtained during previous enumeration steps.
Linux-based systems use a variety of package managers.
For example, Debian-based Linux distributions, like the one in our lab,
use dpkg, while Red Hatbased systems use rpm.
To list applications installed by dpkg on our Debian system, we can use dpkg -l.
This confirms what we expected earlier from enumerating listening ports:
the Debian 10 machine is, in fact, running a web server.
In this case, it is running Apache2.
As we previously mentioned, files with insufficient access restrictions
can create a vulnerability that may grant an attacker elevated privileges.
This most often happens when an attacker can modify scripts or binary files
that are executed under the context of a privileged account.
Sensitive files that are readable by an unprivileged user
may also contain important information
such as hard-coded credentials for a database
or a service account running with higher privileges.
Since it is not feasible to manually check the permissions of each file and directory
we need to automate this task as much as possible.
As a start, we can use find to identify files with insecure permissions.
In this example we are searching for every directory writable by the current user
on the target system.
We’ll search the whole root directory
and use the -writable argument to specify the attribute we are interested in.
We can also use -type d to locate directories,
and redirect errors to dev null.
Several directories seem to be world-writable,
including the /home/joe/.scripts directory,
which is the location of the cron script we found earlier.
This certainly warrants further investigation.
Let’s move further with our enumeration.
On most systems, drives are automatically mounted at boot time.
Because of this, it’s easy to forget about unmounted drives
that could contain valuable information.
We should always look for unmounted drives,
and if they exist, check the mount permissions.
On Linux-based systems, we can use mount to list all mounted filesystems.
In addition, the /etc/fstab file lists all drives that will be mounted at boot time.
The output reveals a swap partition and the primary ext4 disk of this Linux system
Furthermore, we can use lsblk to view all available disks
We’ll notice that the sda drive consists of three different numbered partitions.
In some situations, showing information for all local disks on the system
might reveal partitions that are not mounted.
Depending on the system configuration (or misconfiguration),
we then might be able to mount those partitions
and search for interesting documents, credentials,
or other information that could allow us to escalate our privileges
or get a better foothold in the network.
Another common privilege escalation technique
involves exploitation of device drivers and kernel modules.
We will explore actual exploitation tactics later in this Topic,
but first let’s examine some important enumeration techniques.
Since this technique relies on matching vulnerabilities
with corresponding exploits,
we’ll need to gather a list of drivers and kernel modules
that are loaded on the target.
We can enumerate the loaded kernel modules
using lsmod without any additional arguments.
Once we’ve collected the list of loaded modules and identifed those we want more
information about, such as libata.
We can use modinfo to find out more about the specific module.
We should note that this tool requires the full path to run.
Once we’ve obtained a list of drivers and their versions,
we are better positioned to find any relevant exploits.
Later in this Topic, we will explore various methods of privilege escalation.
However, there are a few specific enumerations we should cover
in this section
that could reveal interesting “shortcuts” to privilege escalation.
Aside from the rwx file permissions described previously,
two additional special rights pertain to executable files: setuid and setgid.
These are symbolized with the letter “s”.
If these two rights are set,
either an uppercase or lowercase “s” will appear in the permissions.
This allows the current user to execute the file with the rights of the owner
(setuid) or the owner’s group (setgid).
When running an executable,
it normally inherits the permissions of the user that runs it.
However, if the SUID permissions are set,
the binary will run with the permissions of the file owner.
This means that if a binary has the SUID bit set and the file is owned by root,
any local user will be able to execute that binary with elevated privileges.
When a user or a system-automated script launches a SUID application,
it inherits the UID/GID of its initiating script:
this is known as effective UID/GID,
which is the actual user that the OS verifies
to grant permissions for a given action.
Any user who manages to subvert a setuid root program
to call a command of their choice
can effectively impersonate the root user and gain all rights on the system.
Penetration testers regularly search for these types of files when they gain access
to a system as a way of escalating their privileges.
We can use find to search for SUID-marked binaries.
In this case, we are starting our search at the root directory,
searching for files, with the SUID bit set,
and discarding all error messages.
In this case, the command found several SUID binaries.
Exploitation of SUID binaries will vary based on several factors.
For example, if /bin/cp (the copy command) were SUID,
we could copy and overwrite sensitive files such as /etc/passwd.
A comprehensive list of Linux privilege escalation techniques
can be found in a compendium by g0tmi1k as well as in other,
more up-to-date, resources.
Having covered the main concepts behind manually enumerating Linux systems
for privilege escalation techniques,
we are going to learn how to automate the process in bulk in the upcoming section.