My CTF Methodology

In this post, I examine the steps I take to approach a typical CTF in the form of a vulnerable target (also known as boot2root), and elaborate on steps at each phase.
My CTF Methodology
In: Attack, CTF, OSCP Prep

A Quick Few Points

  1. There are lots of CTFs and vulnerable boxes out there. One post is not going to cover every possible scenario, avenue, or approach.
  2. I do not proclaim to be the best at CTFs or vulnerable boxes. There is and will always be someone more skilled than me. But, I am motivated, curious, and very good at using Google.
  3. The more practice you get with a wide variety of targets, the better you'll get at finding patterns and forging your own methodology. At first, you'll likely mimic others' patterns, but will do so less as you get more practice.





Step 1. Nmap Scan

As is the case with most vulnerable boxes, we begin assessing the target by running a nmap scan to understand the following:

  • What is running on the target, what server names, what versions?
  • What client software would we need to connect and assess the target?
nmap -h

Nmap help message output

sudo nmap -Pn -p- -A -T4 -oN scan.txt <target_ip>

Slower, but more reliable

sudo nmap -Pn -p- -A --min-rate 5000 -oN scan.txt <target_ip>

Faster, target may drop packets, adjust the '--min-rate' if needed

sudo nmap -Pn -sU --top-ports 500 -A -T4 -oN udp-scan.txt <target_ip>

UDP scan example using '-T4', as going too fast may miss ports

Don't miss an opportunity to begin collecting as much information about the target as you can — even at the initial nmap scan! There's a lot of good information here to take note of.

Using the example nmap syntax above, this is going to run:

  • Service version scan
  • OS detection
  • Default script scan



Example Nmap Output

Nmap Scan of Vulnhub Funbox: 1

PORT    STATE SERVICE     VERSION
21/tcp  open  ftp         ProFTPD 1.3.3c
22/tcp  open  ssh         OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 a6:0e:30:35:3b:ef:43:44:f5:1c:d7:c6:58:64:09:92 (RSA)
|   256 c2:d8:bd:62:bf:13:89:28:f8:61:e0:a6:c4:f7:a5:bf (ECDSA)
|_  256 12:60:6e:58:ee:f2:bd:9c:ff:b0:35:05:83:08:71:b8 (ED25519)
25/tcp  open  smtp        Postfix smtpd
|_smtp-commands: funbox11, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN
80/tcp  open  http        Apache httpd 2.4.18 ((Ubuntu))
|_http-generator: WordPress 5.7.2
|_http-title: Funbox: Scriptkiddie
|_http-server-header: Apache/2.4.18 (Ubuntu)
110/tcp open  pop3        Dovecot pop3d
|_pop3-capabilities: RESP-CODES UIDL TOP SASL CAPA PIPELINING AUTH-RESP-CODE
139/tcp open  netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
143/tcp open  imap        Dovecot imapd
|_imap-capabilities: LOGINDISABLEDA0001 Pre-login more post-login have ENABLE LOGIN-REFERRALS SASL-IR capabilities IMAP4rev1 listed IDLE ID OK LITERAL+
445/tcp open  0�>_y      Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94%E=4%D=7/22%OT=21%CT=1%CU=39079%PV=Y%DS=2%DC=T%G=Y%TM=64BB5A4
OS:8%P=x86_64-pc-linux-gnu)SEQ(SP=104%GCD=1%ISR=105%TI=Z%II=I%TS=8)SEQ(SP=1
OS:04%GCD=3%ISR=105%TI=Z%II=I%TS=8)OPS(O1=M5B4ST11NW7%O2=M5B4ST11NW7%O3=M5B
OS:4NNT11NW7%O4=M5B4ST11NW7%O5=M5B4ST11NW7%O6=M5B4ST11)WIN(W1=7120%W2=7120%
OS:W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=N%T=40%W=7210%O=M5B4NNSNW7%CC
OS:=Y%Q=)T1(R=Y%DF=N%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=N)T5(R=Y
OS:%DF=N%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=N)T7(R=N)U1(R=Y%DF=N%T=40%I
OS:PL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: Hosts:  funbox11, FUNBOX11; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
| smb-os-discovery:
|   OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
|   Computer name: funbox11
|   NetBIOS computer name: FUNBOX11\x00
|   Domain name: \x00
|   FQDN: funbox11
|_  System time: 2023-07-22T06:24:33+02:00
| smb-security-mode:
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-time:
|   date: 2023-07-22T04:24:33
|_  start_date: N/A
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled but not required
|_clock-skew: mean: -39m59s, deviation: 1h09m16s, median: 0s
|_nbstat: NetBIOS name: FUNBOX11, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)



Even running the default nmap scripts can reveal a good deal of information about the services running on the target. There are additional enum and brute scripts that often don't get run as a default scan.

Make sure you carefully look over the nmap script scan output, as this can have details such as:

  • FTP file enumeration
  • NFS share names
  • HTTP robots.txt and redirects
  • DNS names
  • Emails
  • Computer names
  • OS versions
  • And, much more



Open Ports

Identifying Services

Using the example nmap scan output from above, we can see that nmap was able to pull service banners for all open ports that it could connect to.

21/tcp  open  ftp         ProFTPD 1.3.3c
  ^       ^    ^             ^
  |       |    |             '----------- Application and version (banner)
  |       |    |
  |       |    '----------------- Well-known service
  |       |
  |       '------------- Port state
  |
  '----------- Port number
               and Transport protocol

You can find a list of well-known services that nmap tracks at /usr/share/nmap/nmap-services. It's this database that allows nmap to assume the service type running a particular port.

⚠️
Note that for some services — like web servers — the system administrator can often alter or remove the service banner, so this may not always be complete or accurate.

Take note of all the services running on the target and the version numbers of said services. By looking at the services, you'll also know which client software you'll need to do some manual testing. Based on the example nmap output above, we know we'll possibly need:

  • FTP client
  • SSH client
  • SMTP / POP3 (mail) client
  • HTTP (web) client
  • SMB client



No Service Banners?

5040/tcp  open  unknown

Example of an open, but unknown service

What if nmap was able to connect to the port, but was unable to pull a service name and version from the port? There could be various reasons for this, some of the most common being:

  • The port was opened on the box in an attempt to trick you
  • The port is coupled with another service and doesn't reveal banners
  • The service running on the port may be malfunctioning

You can try to manually pull banners from the service, but in all likelihood, you'll just have to move on:

nc -nv <target-ip> <target-port>

Try netcat with verbose output, press 'Enter' a couple times to see if there's any output



Cataloging Possible Exploits

We are NOT exploiting anything at this phase. Just take inventory of service names and versions to determine what might be an avenue to a shell on the target.

When I want to get an idea of any public exploits that might be available for any service(s), I will typically search on Google or Exploit Database.

  • Google:
    • ProFTPD 1.3.3c exploit
    • ProFTPD 1.3.3c exploit site:github.com
  • Exploit Database:

In the case of a CTF, a Denial of Service exploit wouldn't do us much good, but a Command Execution or File Inclusion exploit would be very interesting.





Step 2. Service Enumeration

Active Directory Specific

ℹ️
If the target is part of an Active Directory domain, or a standalone domain controller, then I take a few additional steps in addition to following my General Procedure below.

Check the Port Signature

💡
We can almost always be certain when we've encountered a domain controller by looking at its port signature

PORT     STATE SERVICE
53/tcp   open  domain
88/tcp   open  kerberos-sec
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
389/tcp  open  ldap
445/tcp  open  microsoft-ds
464/tcp  open  kpasswd5
593/tcp  open  http-rpc-epmap
636/tcp  open  ldapssl
3268/tcp open  globalcatLDAP
3269/tcp open  globalcatLDAPssl

A typical port signature for an Active Directory domain controller, especially apparent due to DNS, SMB, Kerberos, and LDAP being open on the box



Identify the Local Domain

Next, we should check the nmap output for the RootDSE and any potential hostname (e.g. DC01.domain.tld). Once, established, we should add the domain and hostname to our /etc/hosts file.

target_ip='10.10.10.22'
sudo nmap -Pn --script ldap-rootdse.nse $target_ip

If you've only run a basic nmap scan and need to enumerate the RootDSE

target_ip='10.10.10.22'
target_domain='domain.tld'
target_hostname="DC01.${target_domain}"

echo -e "${target_ip}\t\t${target_domain} ${target_hostname}" | sudo tee -a /etc/hosts`

Run these commands to populate your /etc/hosts file



DNS

If we've established the local domain for the Active Directory environment, we should attempt to enumerate any DNS records for use when assessing other protocols.

target_ip='10.10.10.22'
target_domain='domain.tld'
host -T -l $target_domain $target_ip

Attempt a zone transfer from the DNS server on the target. If configured correctly, the zone transfer should be refused.

target_ip='10.10.10.22'
target_domain='domain.tld'
dns_wordlist='/usr/share/seclists/Discovery/DNS/namelist.txt'
gobuster dns -r $target_ip -d $target_domain -w $dns_wordlist -t 100

If the zone transfer fails, you can try and manually enumerate records in the target domain



LDAP

A quick win would be the ability to enumerate LDAP records anonymously, as this would allow us to gather a great deal of information about interesting users, groups, and other domain records.

target_domain='domain.tld'
target_hostname="DC01.${target_domain}"
domain_component=$(echo $target_domain | tr '\.', '\n' | xargs -I % echo "DC=%" | paste -sd, -)

ldapsearch -x -H ldap://$target_hostname -b $domain_component

If configured correctly, you should see an error saying that a successful bind must be completed, meaning you need a credential

ldapsearch -x -H ldap://$target_hostname -b $domain_component 'objectClass=*'

However, if you are able to anonymously query LDAP, this is an example command to pull everything from LDAP

LdapSearch | 0xBEN | Notes
When to Use You’ll know when you’ve found a domain controller, because it will have several ports…

More example commands here



SMB

If we can connect to SMB anonymously, it's worth checking to see if we can enumerate object RIDs anonymously as well. RID cycling would allow us to enumerate a list of users and groups on the computer for further use during testing.

target_ip='10.10.10.22'
smbclient -N -L //$target_ip

If you can connect to SMB with a null session (and maybe even list shares), we can try and enumerate more and potentially map shares

smbclient -N //$target_ip/share_name

Connect to a SMB share via null session

# nxc replaces crackmapexec
nxc smb $target_ip -u 'anonymous' -p '' --rid-brute 3000
nxc smb $target_ip -u '' -p '' --rid-brute 3000

If configured correctly, you should see a permissions error, indicating the tests have failed



Kerberos

If you haven't yet managed to compile a list of users from one of the other methods above, we can attempt to use Kerberos pre-authentication and a word list to find usernames.

If we've found some usernames, we can then see if any of them are configured with UF_DONT_REQUIRE_PREAUTH, pull some AS-REP hashes, and attempt to crack them offline.

Kerberos Pre-Auth User... | 0xBEN | Notes
How it Works We can send a request for a TGT --- without a pre-authentication hash --- to the Kerbe…

Attempt to find valid usernames and save them to a log file, then testing for AS-REP hashes and attempting to crack them. If you manage to crack an AS-REP hash for a user account, you could then spray this password around and see what you can access (or at a minimum, dump LDAP).

ℹ️
At this point, if your Active Directory specific testing hasn't produced any meaningful results, it's time to move onto the General Procedure



General Procedure

This is my generalized approach to every target:

  1. File servers
  2. Web servers
  3. Everything else

I do it this way, because I work my way up by level of effort and amount of time involved to enumerate these services.

  • File servers are quick and easy to assess, requiring only a widely available client
  • Web servers require more work and enumeration due to more complex configurations that are possible on the server
  • Everything else comes last when the first two aren't available or they haven't yielded enough info

1. File servers — FTP/SMB

  • May allow anonymous access or may be configured with default credentials
  • This is an excellent opportunity to gather more information from files
  • Additional information may include usernames, passwords, config files, etc
  • This information maybe useful when assessing other services

2. Web — HTTP/HTTPS

  • Web is just as simple as opening your web browser
  • Navigate to the target IP or domain name and just start clicking around
  • Make a note of potential input points that could be abused
  • Web pages may contain usernames, passwords, interesting source code, etc

3. Everything else

  • Start probing other ports, try to understand how they behave
  • Lots of Googling, probably something on HackTricks about it
  • Try other nmap scans to see if additional ports are revealed; UDP or X-Mas scans, for example.





DNS — UDP/53 & TCP/53

I'm putting this one at the top — above file servers — because this is one of those easy things you can try that can be potentially high-impact.

If you found — for example — in your nmap scan that a web server had a TLS certificate with a commonName=mysite.test and there is a DNS server running, you should test to see if a zone transfer is possible.

target_domain='mysite.test'
target_ip='10.10.100.44'
host -T -l $target_domain $target_ip

Using the host command -l requests a zone transfer

If the zone transfer is successful, you could potentially reveal additional HTTP server names to assess later.

target_ip='10.10.100.44'
target_domain='mysite.test'
dns_wordlist='/usr/share/seclists/Discovery/DNS/namelist.txt'

gobuster dns -r $target_ip -d $target_domain -w $dns_wordlist -t 100

If the zone transfer fails, you can try and manually enumerate records in the target domain

💡
While the gobuster dns scan is running, go ahead and start your tests on other ports.



FTP — TCP/21

ftp anonymous@10.10.100.44

Check for anonymous FTP access on the target

When prompted for a password, simply press the Enter key and see if it will allow you to login. If it does, try the following:

  • ls to list files on the server
  • get to retrieve files on the server
  • less or more to read files from the FTP shell
  • cd to change into any potential directories
  • put to test write permissions as a way to perhaps chain an exploit with another service

We're trying to uncover:

  • Usernames
  • Passwords
  • Configuration files
  • Source code
  • Backups
  • Anything interesting

If there's a lot of files and folders, you could do a recursive download and parse the files locally.



SMB — TCP/139 & TCP/445

List Shares

smbclient -N -L //10.10.100.44

No username specified, and '-N' for passwordless authentication

If you are able to anonymously list shares, then there's a decent chance you may be able to map shares.

Shares that aren't interesting from a files perspective are, for example:

  • IPC$
  • print$



Mapping Shares

smbclient -N //10.10.100.44/myshare

Map the 'myshare' share anonymously

If you are able to map the share anonymously, try the following:

  • ls to list files on the server
  • get to retrieve files on the server
  • less or more to read files from the SMB shell
  • cd to change into any potential directories
  • put to test write permissions as a way to perhaps chain an exploit with another service

Like FTP, we're trying to discover anything interesting. If there's a lot of files and folders, you could do a recursive download and parse the files locally.



HTTP — TCP/80 & TCP/443

Initial Questions

The first things I want to establish with the web service are:

  • Are there any noticeable differences between the http:// and the https:// versions of the apps running on the web server? In other words, is http:// redirecting to https://, are they duplicates, or they completely different in behavior and presentation?
  • Is the server making use of any ServerName (virtual host) directives that would cause different pages to load depending on the the hostname the client requests?



Test the Raw IP Address

  • http://10.10.100.44
  • https://10.10.100.44
  • If the server loads different content at each unique scheme
    • There are distinct configurations per port
    • Plan on testing the servers independently
  • If the server redirects TCP/80 to TCP/443
    • Only need to test TCP/443 (https)



Testing Virtual Hosts


The Host header is what the server is looking at to determine which virtual host configuration to serve content from. If you're interested in learning more about enumerating virtual hosts, you can see my notes here.

We can create a local name resolution entry by editing our /etc/hosts file, adding the DNS names we saw in the nmap output or any successful zone transfer.

sudo nano /etc/hosts

Edit the /etc/hosts file

# Custom Entry
10.10.100.44 mysite.test dev.mysite.test admin.mysite.test

Add these hostnames pointing to '10.10.100.44'

Again, as before, test the server names against both HTTP and HTTPS and see if there are any behavioral differences.

  • http://mysite.test and https://mysite.test
  • http://dev.mysite.test and https://dev.mysite.test
  • http://admin.mysite.test and https://admin.mysite.test
  • If different domain names load the same content
    • No difference in page content between http://mysite.test and http://subdomain.mysite.test
    • Safe to assume no virtual hosts are being used
    • You can most likely test the server using the raw IP address
  • If different domain names load unique content
    • https://mysite.test and https://subdomain.mysite.test load completely different pages
    • More than likely this server is using virtual hosts
    • Test each virtual host as an individual server



Walking the Happy Path

Walking the “happy path” :: Pwning OWASP Juice Shop

At this stage, we just want to use the web page as a normal user would.

  • Not doing anything malicious
  • Click links and provide expected inputs in standard fields
  • Doing things that a normal user would expectedly do
  • Navigating to the URLs we've discovered at this point
    • Raw IP addresses
    • Domain names
    • HTTP/HTTPS
  • Just click around on links and interact
  • Enter input as a normal user would
  • Sign up for an account and view the application as an authenticated user
  • Trying to understand the application behavior



Checking the Page Source

  • Press CTRL + U
  • Check the page source for any servers that need to be tested
    • Raw IP address
    • Domain names
    • HTTP/HTTPS
  • Look for anything interesting visible client side
    • Typically in the HTML comments
    • Usernames
    • Passwords
    • Directory names
    • File names
    • Etc



Check for Robots and Sitemap

  • http://10.10.100.44/robots.txt or https://mysite.test/robots.txt
  • https://10.10.100.44/sitemap.xml or https://mysite.test/sitemap.xml
  • robots.txt and sitemap.xml direct legitimate web crawlers and search engines
  • They're only effective to the extent bots respect them
  • Can also reveal some interesting and sensitive directories or pages
  • robots.txt may show an entry for the /admin directory or similar
    • A legitimate web crawler respects this and does not crawl the directory
    • Malicious users will see this an opportunity to explore



Directory and File Enumeration

  • Interesting files and directories may be "hidden" or not directly exposed
  • We can send a series of HTTP requests to determine if a file or directory exists
    • HTTP 20x and HTTP 30x responses would be interesting
    • HTTP 403 could be interesting from the perspective of, "What are we NOT allowed to access?"
  • I typically use gobuster or feroxbuster
  • Choose whichever tool is most comfortable for you
gobuster dir -u http://10.10.100.44 -w /usr/share/seclists/Discovery/Web-Content/directory-list-big.txt -x php,html -r -t 100 -o gobuster80.txt

HTTP enumeration

gobuster dir -k -u https://dev.mysite.test -w /usr/share/seclists/Discovery/Web-Content/big.txt -x php,html -r -t 100 -o gobuster443.txt

HTTPS domain-specific enumeration example

  • These enumeration scans could reveal:
    • "Hidden" pages or pages with unintended access
    • Path-based applications such as CMS (eg. WordPress or Drupal)
  • Blogs or CMS platforms may be unpatched or use unpatched plugins
    • Should check the version numbers for public exploits
    • wp-scan can be helpful when enumerating WordPress



Testing the Application

By now, you should have plenty of places to begin looking for potential vulnerabilities with the web applications running on the server.

You want to begin testing the web applications at various points:

  • Vulnerable service or plugin versions
  • URL parameters
  • Login forms
  • Search fields
  • File uploads
  • Etc.

And, you could test for things like:

  • Path traversal
  • Local and/or remote file inclusion
  • Content type filter bypass
  • SQL injection
  • Default credentials
  • Credential stuffing
  • Password spraying
  • Much, much more



Unknown Ports and Services

Sometimes you will see uncommon services, uncommon port bindings, and high-number port bindings on certain CTF targets. The key here is being able to tell the difference between:

  • An uncommon or high port bound to a service
    • Often included by CTF authors to confuse you
  • A dynamic port bound to something like RPC
    • Just part of the operating system or another service like NFS

Fortunately, nmap does a good job of identifying services bound to ports, except for when it can't grab service banners from the port. So, you should be able to identify RPC from other port bindings with ease.

Examples

49152/tcp open  msrpc        Microsoft Windows RPC  
49153/tcp open  msrpc        Microsoft Windows RPC  
49154/tcp open  msrpc        Microsoft Windows RPC  
49155/tcp open  msrpc        Microsoft Windows RPC  
49156/tcp open  msrpc        Microsoft Windows RPC
  • Very common to see on Windows targets
  • These are dynamic RPC port bindings and can typically be ignored
2869/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
5357/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
10243/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
  • These port bindings aid in network device discovery
  • Can typically be ignored
8080/tcp open  http    Apache httpd 2.4.52 ((Ubuntu))
  • This is a very typical alternate HTTP port binding
  • Explore this further if prior testing has been unsuccessful
5040/tcp  open  unknown
  • Showing an example of nmap failing to detect a service
  • I would completely ignore this port unless everything else failed
  • If you decide to probe this further, try:
    • Manually grabbing a service banner using netcat or telnet
    • Open Wireshark and interact with the service and inspect the packets
PORT      STATE SERVICE      VERSION
8014/tcp  open  http         Apache httpd
60000/tcp open  http         Apache httpd 2.4.38
  • nmap has identified these as alternative Apache HTTP server bindings
  • Given the odd port numbers, I'd typically spend little time probing these until later
  • You could use curl or your browser to open them up briefly and see what they look like, but do so much later after testing more common services
PORT      STATE SERVICE      VERSION
1883/tcp  open  mqtt
  • An example of an atypical service you could see on a box
  • When you encounter a new service for the first time, search on Google
  • You could search something like: tcp 1883 mqtt pentest
  • HackTricks often has excellent articles on getting you started with testing services with which you may be unfamiliar
1883 - Pentesting MQTT (Mosquitto) - HackTricks
PORT      STATE SERVICE VERSION
2222/tcp  open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
  • An example of an atypical SSH server binding
  • The same SSH principles apply to this server as well
    • Do not try and pentest this service early on, just ignore it initially
    • If you come across a SSH key or a username and password later, that would be a good time to test those credentials on something like this



Information Re-Use

If you've discovered useful information while probing HTTP or some other service, you should always consider how this information may be able to be used with services you previously looked at.

For example, maybe you found:

  • Usernames and/or passwords
  • SSH private keys
  • Wordlists
  • Backups

Can any of this be used to go back and get more information from another service?





Step 3. Exploit

By now, you should have plenty of information and a very educated guess about the potential exploit you're planning to use against the target.

✔️
All of the information gathering and preparation you've done up until this point should directly inform the exploit you're going to use.

You may end up using a publicly available exploit or you may end up chaining multiple vulnerabilities together. For example, you may have a FTP file upload and local file inclusion chained exploit to get a reverse shell.





Step 4. Post-Exploit Enumeration and Privilege Escalation

Congratulations! You've got a shell on the target and you're probably full of adrenaline. Now's the time keep a level head and methodically enumerate the target environment to plot the path to full system ownership.

💡
Keep in mind that some of the usernames and/or passwords you enumerated before could be useful to you even at this point in the process.



Post-Exploit Enumeration

At this phase, we want to take inventory of the environment. We want to get a lay of the land and figure out as much as we can before we start attacking anything internally.

ℹ️
I try to enumerate as much as I can manually, and in a targeted fashion, before relying on privilege escalation scripts. In fact — for me at least — privilege escalation scripts are always my last resort when all else fails. 

You can find the most up-to-date list of post-exploit enumeration tricks here:

WriteupTemplates/Generic-Writeup-Template.md at main · 0xBEN/WriteupTemplates
Contribute to 0xBEN/WriteupTemplates development by creating an account on GitHub.



Operating Environment

OS & Kernel

  • Windows

    • systeminfo or Get-ComputerInfo to print system overview
    • Check environment variables:
      • CMD: set
      • PowerShell: Get-ChildItem Env:\
  • *nix

    • uname -a to print kernel information
    • cat /etc/os-release to print OS release information
    • Check environment variables:
      • env or set

Current User

  • Windows

    • whoami /all to list:
      • Username
      • Group memberships
      • Privileges
  • *nix

    • id to list username and group memberships
    • sudo -l to check sudo permissions



Users and Groups

Local Users

  • Windows

    • net user or Get-LocalUser to list users
    • net user <username> or Get-LocalUser <username> | Select-Object * to enumerate details about specific users
    • Can you dump and pass/crack hashes from SAM using your current access?
  • *nix

    • cat /etc/passwd to list users

Local Groups

Document here any interesting group(s) after running the below commands:

  • Windows

    • net localgroup or Get-LocalGroup to list groups
    • net localgroup <group_name> or Get-LocalGroupMember <group_name> | Select-Object * to enumerate users of specific groups
  • *nix

    • cat /etc/group
    • cat /etc/group | grep <username> to check group memberships of specific users

Domain Users (if domain-joined)

Document here any interesting username(s) after running the below commands:

  • Windows

  • *nix

    • Check if joined to a domain

      • /usr/sbin/realm list -a
      • /usr/sbin/adcli info <realm_domain_name>
    • No credential:

      • Check for log entries containing possible usernames

        • find /var/log -type f -readable -exec grep -ail '<realm_domain_name>' {} \; 2>/dev/null

        • Then, grep through each log file and remove any garbage from potential binary files:

          • Using strings: strings /var/log/filename | grep -i '<realm_domain_name>'
          • If strings not available, try using od: od -An -S 1 /var/log/filename | grep -i '<realm_domain_name>'
          • If od not available, try grep standalone: grep -iao '.*<realm_domain_name>.*' /var/log/filename
        • Validate findings:

          • Check if discovered usernames are valid: getent passwd <domain_username>
          • If valid, check user group memberships: List id <domain_username>
        • Check domain password and lockout policy for password spray feasibility

      • See Domain Groups, as certain commands there can reveal some additional usernames

    • With a domain credential:

      • If you have a valid domain user credential, you can try ldapsearch
      • Dump all objects from LDAP: ldapsearch -x -H ldap://dc-ip-here -D 'CN=username,DC=realmDomain,DC=realmTLD' -W -b 'DC=realmDomain,DC=realmTLD' 'objectClass=*'
      • Dump all users from LDAP: ldapsearch -x -H ldap://dc-ip-here -D 'CN=username,DC=realmDomain,DC=realmTLD' -W -b 'DC=realmDomain,DC=realmTLD' 'objectClass=account'
    • If you're root on the domain-joined host:

      • You can try best-effort dumping the SSSD cache:

        • Using strings: strings /var/lib/sss/db/cache_<realm_domain_name>.ldb | grep -iE '[ou|cn]=.*user.*' | grep -iv 'disabled' | sort -u
        • If strings not available, try using od: od -An -S 1 /var/lib/sss/db/cache_<realm_domain_name>.ldb | grep -iE '[ou|cn]=.*user.*' | grep -iv 'disabled' | sort -u
        • If od not available, try grep standalone: grep -iao '.*<realm_domain_name>.*' /var/lib/sss/db/cache_<realm_domain_name>.ldb | sed 's/[^[:print:]\r\t]/\n/g' | grep -iE '[ou|cn]=.*user.*' | grep -iv disabled
      • You can transfer the SSSD TDB cache for local parsing

        • Default file path: /var/lib/sss/db/cache_<realm_domain_name>.tdb
        • You can dump this file with tools such as tdbtool or tdbdump

Domain Groups (if domain-joined)

Document here any interesting group(s) after running the below commands:

  • Windows

  • *nix

    • Check if joined to a domain

      • /usr/sbin/realm list -a
      • /usr/sbin/adcli info <realm_domain_name>
    • No credential:

    • With a domain credential:

      • If you have a valid domain user credential, you can try ldapsearch
      • Dump all objects from LDAP: ldapsearch -x -H ldap://dc-ip-here -D 'CN=username,DC=realmDomain,DC=realmTLD' -W -b 'DC=realmDomain,DC=realmTLD' 'objectClass=*'
      • Dump all groups from LDAP: ldapsearch -x -H ldap://dc-ip-here -D 'CN=username,DC=realmDomain,DC=realmTLD' -W -b 'DC=realmDomain,DC=realmTLD' 'objectClass=group'
    • If you're root on the domain-joined host:

      • You can try dumping the SSSD cache:

        • Using strings: strings /var/lib/sss/db/cache_<realm_domain_name>.ldb | grep -i '<realm_domain_name>'
        • If strings not available, try using od: od -An -S 1 /var/lib/sss/db/cache_<realm_domain_name>.ldb | grep -i '<realm_domain_name>'
        • If od not available, try grep standalone: grep -iao '.*<realm_domain_name>.*' /var/lib/sss/db/cache_<realm_domain_name>.ldb | sed 's/[^[:print:]\r\t]/\n/g' | grep -iE '[ou|cn]=.*group.*' | grep -i '^CN='
      • You can transfer the SSSD TDB cache for local parsing

        • Default file path: /var/lib/sss/db/cache_<realm_domain_name>.tdb
        • You can dump this file with tools such as tdbtool or tdbdump



Network Configurations

Network Interfaces

  • Windows

    • ipconfig or Get-NetAdapter
  • *nix

    • ip address or ifconfig

Open Ports

Looking for any ports listening internally, or any that may have been firewalled and unreachable at the initial nmap scan.

  • Windows

    • netstat -ano | findstr /i listening or Get-NetTCPConnection -State Listen
  • *nix

    • netstat -tanup | grep -i listen or ss -tanup | grep -i listen

ARP Table (if pivoting to other hosts)

  • Windows

    • arp -a or Get-NetNeighbor output
  • *nix

    • ip neigh or arp -a output

Routes (if pivoting to other hosts)

  • Windows

    • route print or Get-NetRoute output
  • *nix

    • ip route or route output

Ping Sweep (if pivoting to other hosts)

If the host has access to additional routes / interfaces:

  • Look at the IP address space and network mask
  • Find a ping sweep script that will work for the target network
  • Or you could try:
    • Transfering nmap or some other host discover tool to the host
    • Set up a SOCKS proxy and try a port scan through the foothold



Processes and Services

Interesting Processes

First...
Enumerate processes:

  • Windows

    • tasklist
    • Get-Process
    • Get-CimInstance -ClassName Win32_Process | Select-Object Name, @{Name = 'Owner' ; Expression = {$owner = $_ | Invoke-CimMethod -MethodName GetOwner -ErrorAction SilentlyContinue ; if ($owner.ReturnValue -eq 0) {$owner.Domain + '\' + $owner.User}}}, CommandLine | Sort-Object Owner | Format-List
  • *nix

    • ps aux --sort user

Then...
Focus on:

  • Any interesting processes run by users/administrators
  • Any vulnerable applications
  • Any intersting command line arguments visible

Interesting Services

  • Windows

    • First...
      Enumerate services:
      • sc.exe query
        • Then sc.exe qc <service-name>
          • List the configuration for any interesting services
      • Get-CimInstance -ClassName Win32_Service | Select-Object Name, StartName, PathName | Sort-Object Name | Format-List
    • Then...
      Check for things like:
      • Vulnerable service versions
      • Unquoted service path
      • Service path permissions too open?
        • Can you overwrite the service binary?
        • DLL injection?
  • *nix

    • First...
      Enumerate services:
      • service --status-all or systemctl list-units --type=service --state=running
    • Then...
      Check for things like:
      • Vulnerable service versions
      • Configuration files with passwords or other information
      • Writable unit files: systemctl list-units --state=running --type=service | grep '\.service' | awk -v FS=' ' '{print $1}' | xargs -I % systemctl status % | grep 'Loaded:' | cut -d '(' -f 2 | cut -d ';' -f 1 | xargs -I % find % -writable 2>/dev/null
      • Writable service binaries

Then...
Focus on:

  • Any interesting services or vulnerabilities
  • Any vulnerable service versions
  • Any intersting configuration files



Scheduled Tasks

Interesting Scheduled Tasks

First...
Enumerate scheduled tasks:

  • Windows

    • schtasks /QUERY /FO LIST /V | findstr /i /c:taskname /c:"run as user" /c:"task to run"
    • Get-CimInstance -Namespace Root/Microsoft/Windows/TaskScheduler -ClassName MSFT_ScheduledTask | Select-Object TaskName, @{Name = 'User' ; Expression = {$_.Principal.UserId}}, @{Name = 'Action' ; Expression = {($_.Actions.Execute + ' ' + $_.Actions.Arguments)}} | Format-List
  • *nix

    • crontab -l
    • cat /etc/cron* 2>/dev/null
    • cat /var/spool/cron/crontabs/* 2>/dev/null

Then...
Focus on:

  • Any interesting scheduled tasks
  • Any writable paths in the scheduled task
  • Any intersting command line arguments visible



Interesting Files

C:\InterestingDir\Interesting-File1.txt

  • Windows

    • Check for writable files and directories
    • Check for configuration files with passwords and other interesting info
    • Check for scripts with external dependencies that can be overwritten or changed
    • Some interesting places to check
      • Check $PATH variable for current user for possible interesting locations
      • Also check for hidden items
      • PowerShell History File: (Get-PSReadLineOption).HistorySavePath
      • I reference %SYSTEMDRIVE%, as C: is not always the system volume
        • %SYSTEMDRIVE%\interesting_folder
        • %SYSTEMDRIVE%\Users\user_name
          • Desktop, Downloads, Documents, .ssh, etc
          • AppData (may also have some interesting things in Local, Roaming)
        • %SYSTEMDRIVE%\Windows\System32\drivers\etc\hosts
        • %SYSTEMDRIVE%\inetpub
        • %SYSTEMDRIVE%\Program Files\program_name
        • %SYSTEMDRIVE%\Program Files (x86)\program_name
        • %SYSTEMDRIVE%\ProgramData
        • %SYSTEMDRIVE%\Temp
        • %SYSTEMDRIVE%\Windows\Temp
      • Check the Registry for passwords, configurations, interesting text
        • HKEY_LOCAL_MACHINE or HKLM
        • HKEY_CURRENT_USER or HKCU
        • Search the HKLM hive recursively for the word password
          • reg query HKLM /f password /t REG_SZ /s
  • *nix

    • Check for SUID binaries
      • find / -type f -perm /4000 -exec ls -l {} \; 2>/dev/null
    • Check for interesting / writable scripts, writable directories pr files
      • find /etc -writable -exec ls -l {} \; 2>/dev/null
      • `find / -type f ( -user $(whoami) -o -group $(whoami) ) -exec ls -l {} ; 2>/dev/null
    • Check for configuration files with passwords and other interesting info
    • Check for scripts with external dependencies that can be overwritten or changed
    • Use strings on interesting binaries to check for relative binary names and $PATH hijacking
    • Some interesting places to check (check for hidden items)
      • Check $PATH variable for current user for possible interesting locations
      • /interesting_folder
      • /home/user_name
        • .profile
        • .bashrc, .zshrc
        • .bash_history, .zsh_history
        • Desktop, Downloads, Documents, .ssh, etc.
        • PowerShell History File: (Get-PSReadLineOption).HistorySavePath
      • /var/www/interesting_folder
      • /var/mail/user_name
      • /opt/interesting_folder
      • /usr/local/interesting_folder
      • /usr/local/bin/interesting_folder
      • /usr/local/share/interesting_folder
      • /etc/hosts
      • /tmp
      • /mnt
      • /media
      • /etc
        • Look for interesting service folders
        • Check for readable and/or writable configuration files
        • May find cleartext passwords





Privilege Escalation

Depending on the information you enumerated above, your path to privilege escalation could take any direction.

  • You might find a server listening internally running vulnerable software
  • An internal service might be using default credentials, leading to some other exploit
  • You might find a SUID binary that gives you root access
  • You might have SeImpersonatePrivilege allowing for a Potato attack
  • You may find a kernel exploit
  • The possibilities are endless, and that's why putting all of your effort into solid enumeration strategies will always pay off.
More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.