[![Logo](https://www.paloaltonetworks.com/wp-content/uploads/2021/07/PANW_Parent.png)](https://www.paloaltonetworks.com/)  
[![Unit42 Logo](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/unit42-logo-white.svg)](https://unit42.paloaltonetworks.com/)  
Menu

* [Tools](https://unit42.paloaltonetworks.com/tools/)
* [ATOMs](https://unit42.paloaltonetworks.com/atoms/)
* [Security Consulting](https://www.paloaltonetworks.com/unit42)
* [About Us](https://unit42.paloaltonetworks.com/about-unit-42/)
* [**Under Attack?**](https://start.paloaltonetworks.com/contact-unit42.html)  
  English
* [English](https://unit42.paloaltonetworks.com/unit42-decrypting-lockcrypt-ransomware/)
* [Japanese](https://unit42.paloaltonetworks.com/ja/unit42-decrypting-lockcrypt-ransomware/)
* [Threat Research Center](https://unit42.paloaltonetworks.com "Threat Research")
* [Threat Research](https://unit42.paloaltonetworks.com/category/threat-research/ "Threat Research")
* [Ransomware](https://unit42.paloaltonetworks.com/category/ransomware/ "Ransomware")  
  [Ransomware](https://unit42.paloaltonetworks.com/category/ransomware/)

# Decrypting the LockCrypt Ransomware

![Clock Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-clock.svg) 10 min read

* ![Profile Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-profile-grey.svg)  
  By:
  
  * [Tomer Harpaz](https://unit42.paloaltonetworks.com/author/tomer-harpaz/)

* ![Published Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-calendar-grey.svg)  
  Published:July 27, 2018

* ![Tags Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-category.svg)  
  Categories:
  
  * [Ransomware](https://unit42.paloaltonetworks.com/category/ransomware/)
  * [Threat Research](https://unit42.paloaltonetworks.com/category/threat-research/)

* ![Tags Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-tags-grey.svg)  
  Tags:
  
  * [Decryptor](https://unit42.paloaltonetworks.com/tag/decryptor/)
  * [LockCrypt](https://unit42.paloaltonetworks.com/tag/lockcrypt/)
  * [Tool](https://unit42.paloaltonetworks.com/tag/tool/)

* [![Download Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-download.svg)](https://unit42.paloaltonetworks.com/unit42-decrypting-lockcrypt-ransomware/?pdf=download&lg=en&_wpnonce=7570bbad6f "Click here to download")

* [![Print Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-print.svg)](https://unit42.paloaltonetworks.com/unit42-decrypting-lockcrypt-ransomware/?pdf=print&lg=en&_wpnonce=7570bbad6f "Click here to print")

Share![Down arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/down-arrow.svg)

* ![Link Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-share-link.svg)
* [![Link Email](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-sms.svg)](mailto:?subject=Decrypting%20the%20LockCrypt%20Ransomware&body=Check%20out%20this%20article%20https%3A%2F%2Funit42.paloaltonetworks.com%2Funit42-decrypting-lockcrypt-ransomware%2F "Share in email")
* [![Facebook Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-fb-share.svg)](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Funit42.paloaltonetworks.com%2Funit42-decrypting-lockcrypt-ransomware%2F "Share in Facebook")
* [![LinkedIn Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-linkedin-share.svg)](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Funit42.paloaltonetworks.com%2Funit42-decrypting-lockcrypt-ransomware%2F&title=Decrypting%20the%20LockCrypt%20Ransomware "Share in LinkedIn")
* [![Twitter Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-twitter-share.svg)](https://twitter.com/intent/tweet?url=https%3A%2F%2Funit42.paloaltonetworks.com%2Funit42-decrypting-lockcrypt-ransomware%2F&text=Decrypting%20the%20LockCrypt%20Ransomware "Share in Twitter")
* [![Reddit Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-reddit-share.svg)](https://www.paloaltonetworks.com//www.reddit.com/submit?url=https%3A%2F%2Funit42.paloaltonetworks.com%2Funit42-decrypting-lockcrypt-ransomware%2F&ts=markdown "Share in Reddit")
* [![Mastodon Icon](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-mastodon-share.svg)](https://mastodon.social/share?text=Decrypting%20the%20LockCrypt%20Ransomware%20https%3A%2F%2Funit42.paloaltonetworks.com%2Funit42-decrypting-lockcrypt-ransomware%2F "Share in Mastodon")
  Overview  
  LockCrypt, also known as EncryptServer2018, is a ransomware family that has been in the wild since mid 2017 and is still active today. The malware was reversed and [analyzed thoroughly](https://blog.malwarebytes.com/threat-analysis/2018/04/lockcrypt-ransomware/) by Malwarebytes Labs in April, which correctly concluded that the unsophisticated home-made encryption used in this malware seems to be breakable.  
  However, the Malwarebytes researchers did not publish any decryptor for this malware, and we couldn't find any other public decryption method online either. The only other clue was a [news post](https://www.bleepingcomputer.com/news/security/lockcrypt-ransomware-cracked-due-to-bad-crypto/) which referred victims to Michael Gillespie ([@demonslay335](https://twitter.com/demonslay335)) for help with its decryption.  
  We contacted Michael, who did some [really nice work](https://download.bleepingcomputer.com/demonslay335/LockCryptDecrypter.zip) together with [@hasherezade](https://twitter.com/hasherezade) and [@FraMauronz](https://twitter.com/framauronz), but whose recovery method was incomplete -- it required a large known plaintext file (1MB), could not recover all filenames, and in some cases could not decrypt 1-2 bytes in the end of the file.  
  In this blog post we will describe our analysis of the home-made encryption that the malware used, how we broke it, and how the encryption key can be recovered in case you have at least 25KB of known plaintext. This scenario is very realistic, since LockCrypt encrypts all of the files it can find, including application files like DLLs which can be easily recovered by installing the same software version on a different computer. Scripts and instructions for recovering files are included in the final section of the report.  
  Note that a new variant of this malware with better encryption [has recently been spotted](https://twitter.com/demonslay335/status/1002209455646429186). We have not analyzed this new version, but it appears to use much stronger encryption.

Initial analysis  
Disassembling the malware encryption, we found that the malware authors chose to encrypt the files using some custom-made encryption which looked pretty weak. It really surprised us that in 2018 you could still encounter a ransomware with custom encryption models, when you can easily use Windows APIs to encrypt data in a way that will take billions of years to crack using current and foreseeable hardware (for example for 128-bit AES with a key that was generated using cryptographically safe methods).  
The disassembly of the encryption functions is equivalent to the following Python code (equivalent C code for the encrypt() function can be seen in [Malwarebyte's analysis](https://blog.malwarebytes.com/threat-analysis/2018/04/lockcrypt-ransomware/) and might be easier to understand because of the pointer operations):  
def grouper(iterable, n, fillvalue=None): """returns a generator which iterates iterable in groups of size n. in case the last group is incomplete, it is padded with fillvalue""" args = \[iter(iterable)\] \* n return itertools.izip\_longest(fillvalue=fillvalue, \*args) def rol(val, n, width): """equivalent to the x86 rol opcode""" return (val \<\< n) \& ((1\<\<width)-1) | \\ ((val \& ((1\<\<width)-1)) \>\> (width-n)) def encrypt(key, plain): size = len(plain)-2 enc = io.BytesIO(plain) # phase 1 key\_cyclic = grouper(itertools.cycle(key), 4) for \_ in xrange(0, size\&(~0x3), 2): # align the size of the data to a multiple of 4 bytes # get the next key dword k = "".join(key\_cyclic.next()) k = struct.unpack("\<I", k)\[0\] # get the next data dword d = enc.read(4) d = struct.unpack("\<I", d)\[0\] # XOR them and put it back to the data e = k^d e = struct.pack("\<I", e) enc.seek(-4, os.SEEK\_CUR) enc.write(e) # go back 2 bytes in the data stream (so that loop iterations overlap) enc.seek(-2, os.SEEK\_CUR) # phase 2 enc.seek(0, os.SEEK\_SET) key\_cyclic = grouper(itertools.cycle(key), 4) for i in xrange(0, size\&(~0x3), 4): # get the next key dword k = "".join(key\_cyclic.next()) k = struct.unpack("\<I", k)\[0\] # get the next data dword d = enc.read(4) d = struct.unpack("\<I", d)\[0\] # rotate the data dword e = rol(d, 5, 32) # XOR it with the key dword e = e^k # swap the byte order e = struct.pack("\>I", e) # put the data dword back enc.seek(-4, os.SEEK\_CUR) enc.write(e) return enc.getvalue() def encrypt\_file(key, file): # len(key) == 25000 # leave the first 4 bytes as-is file.seek(4, os.SEEK\_SET) # encrypt the rest of the first 1MB of the file plain\_data = file.read(0x100000 - 4) enc\_data = encrypt(key, plain\_data) file.seek(4, os.SEEK\_SET) file.write(enc\_data) # leave the rest of the file as is

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | def grouper(iterable, n, fillvalue=None): """returns a generator which iterates iterable in groups of size n. in case the last group is incomplete, it is padded with fillvalue""" args = \[iter(iterable)\] \* n return itertools.izip\_longest(fillvalue=fillvalue, \*args) def rol(val, n, width): """equivalent to the x86 rol opcode""" return (val \<\< n) \& ((1\<\<width)-1) | \\ ((val \& ((1\<\<width)-1)) \>\> (width-n)) def encrypt(key, plain): size = len(plain)-2 enc = io.BytesIO(plain) # phase 1 key\_cyclic = grouper(itertools.cycle(key), 4) for \_ in xrange(0, size\&(~0x3), 2): # align the size of the data to a multiple of 4 bytes # get the next key dword k = "".join(key\_cyclic.next()) k = struct.unpack("\<I", k)\[0\] # get the next data dword d = enc.read(4) d = struct.unpack("\<I", d)\[0\] # XOR them and put it back to the data e = k^d e = struct.pack("\<I", e) enc.seek(-4, os.SEEK\_CUR) enc.write(e) # go back 2 bytes in the data stream (so that loop iterations overlap) enc.seek(-2, os.SEEK\_CUR) # phase 2 enc.seek(0, os.SEEK\_SET) key\_cyclic = grouper(itertools.cycle(key), 4) for i in xrange(0, size\&(~0x3), 4): # get the next key dword k = "".join(key\_cyclic.next()) k = struct.unpack("\<I", k)\[0\] # get the next data dword d = enc.read(4) d = struct.unpack("\<I", d)\[0\] # rotate the data dword e = rol(d, 5, 32) # XOR it with the key dword e = e^k # swap the byte order e = struct.pack("\>I", e) # put the data dword back enc.seek(-4, os.SEEK\_CUR) enc.write(e) return enc.getvalue() def encrypt\_file(key, file): # len(key) == 25000 # leave the first 4 bytes as-is file.seek(4, os.SEEK\_SET) # encrypt the rest of the first 1MB of the file plain\_data = file.read(0x100000 - 4) enc\_data = encrypt(key, plain\_data) file.seek(4, os.SEEK\_SET) file.write(enc\_data) # leave the rest of the file as is |

The following conclusions are apparent:

1. The transformation defined by phase 1 is cyclic with a cycle length of 12500 bytes
2. The transformation defined by phase 2 is cyclic with a cycle length of 25000 bytes
3. Excluding edge cases (we'll discuss those later on), each plain bit is XOR-ed to 3 key bits (two during phase 1 and another one during phase 2)
4. If we "undo" the bit shifts done in Phase 2 (swapping back the byte order and ROR-ing back 5 bits), both phases together can be described as a stream cipher with a cyclic key of length 25000 (which is a function of the original key)

We will explain the last two conclusions using the following diagrams which visualize the transformation of data bytes 4:8 while going through the different steps of the encryption function. The ⊕ symbol between the rows indicates the bits have been XOR-ed with eachother.  
Before any sort of transformation, the bits of bytes 4:8 look like:  
[![0 before](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/0-before-500x13.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/0-before.png)  
After the 2nd iteration of the phase 1 loop:  
[![](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/1-after-phase-1-after-i-2.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/1-after-phase-1-after-i-2.png)

After the 3rd iteration of the phase 1 loop:  
[![](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/2-after-phase-1-after-i-3-500x76.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/2-after-phase-1-after-i-3.png)

After the 4th iteration of the phase 1 loop (and the same after the entire phase 1 loop):  
[![](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/3-after-phase-1-after-i-4-500x107.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/3-after-phase-1-after-i-4.png)  
During the 2nd iteration of the phase 2 loop, after the ROL operation:  
[![4 after phase 2 after rol](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/4-after-phase-2-after-rol-500x107.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/4-after-phase-2-after-rol.png)  
During the 2nd iteration of the phase 2 loop, after the XOR operation:  
[![](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/5-after-phase-2-after-xor-500x138.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/5-after-phase-2-after-xor.png)  
The order of the bytes is then swapped, and this concludes these bytes' encryption.

Recovering the "stream key"  
If we take the encrypted bytes 4:8 from the last diagram, unswap their order, and ROR them back 5 bits, we get:  
[![](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/6-enc-after-ror-500x138.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/6-enc-after-ror.png)  
This operation (unswapping the byte order and ROR-ing back 5 bits) has basically "normalized" the encryption to a typical stream cipher. If we then XOR these bytes with known plain bytes, we'll be left with the function of the key bits that we mentioned in conclusion 4 above:  
[![](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/7-enc-after-xor-with-plain-500x107.png)](https://unit42.paloaltonetworks.com/wp-content/uploads/2018/07/7-enc-after-xor-with-plain.png)  
We call this stream (whose each bit is actually three XOR-ed "original key" bits) the "stream key". Since it is cyclic with a cycle length of 25000 bytes, it seems enough to have 25000 bytes of both plain and encrypted bytes in order to recover it and then decrypt the file!  
The following python function can be used to recover the stream key using some given plaintext and encrypted data pairs. The index parameter specifies the index of the 25000 known plaintext bytes in the given string (minus 4, since the first 4 bytes are never encrypted), i.e. only bytes idx+4:idx+4+25000 in the plain string will be used.  
key\_len = 25000 def ror(val, n, width): return ((val \& ((1\<\<width)-1)) \>\> n) | \\ (val \<\< (width-n) \& ((1\<\<width)-1)) def recover\_stream\_key(plain, enc, idx): assert len(plain) == len(enc) assert len(plain) \>= 4 + idx + key\_len plain = io.BytesIO(plain) enc = io.BytesIO(enc) assert plain.read(4) == enc.read(4) plain.seek(idx \& (~3), os.SEEK\_CUR) enc.seek(idx \& (~3), os.SEEK\_CUR) stream\_key = io.BytesIO() for i in xrange(0, key\_len + (idx % 4), 4): # read the next plain dword p = plain.read(4) p = struct.unpack("\<I", p)\[0\] # read the next encrypted dword and undo the bit shifts on it e = enc.read(4) # unswap the byte order e = struct.unpack("\>I", e)\[0\] # ROR back 5 bits e = ror(e, 5, 32) # XOR the plain and normalized encrypted dwords k = p^e k = struct.pack("\<I", k) # write the stream key dword to the recovered stream key stream if i == 0: stream\_key.write(k\[idx % 4:\]) elif i \< key\_len: stream\_key.write(k) else: # i == key\_len stream\_key.write(k\[:-idx % 4\]) stream\_key = stream\_key.getvalue() assert len(stream\_key) == key\_len return stream\_key

|----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | key\_len = 25000 def ror(val, n, width): return ((val \& ((1\<\<width)-1)) \>\> n) | \\ (val \<\< (width-n) \& ((1\<\<width)-1)) def recover\_stream\_key(plain, enc, idx): assert len(plain) == len(enc) assert len(plain) \>= 4 + idx + key\_len plain = io.BytesIO(plain) enc = io.BytesIO(enc) assert plain.read(4) == enc.read(4) plain.seek(idx \& (~3), os.SEEK\_CUR) enc.seek(idx \& (~3), os.SEEK\_CUR) stream\_key = io.BytesIO() for i in xrange(0, key\_len + (idx % 4), 4): # read the next plain dword p = plain.read(4) p = struct.unpack("\<I", p)\[0\] # read the next encrypted dword and undo the bit shifts on it e = enc.read(4) # unswap the byte order e = struct.unpack("\>I", e)\[0\] # ROR back 5 bits e = ror(e, 5, 32) # XOR the plain and normalized encrypted dwords k = p^e k = struct.pack("\<I", k) # write the stream key dword to the recovered stream key stream if i == 0: stream\_key.write(k\[idx % 4:\]) elif i \< key\_len: stream\_key.write(k) else: # i == key\_len stream\_key.write(k\[:-idx % 4\]) stream\_key = stream\_key.getvalue() assert len(stream\_key) == key\_len return stream\_key |

We should now (not really) be able to decrypt files with the following function:  
def decrypt(stream\_key, enc): size = len(enc) - 2 plain = io.BytesIO(enc) stream\_key\_cyclic = grouper(itertools.cycle(stream\_key), 4) for i in xrange(0, size\&(~3), 4): # read the next stream key dword sk = "".join(stream\_key\_cyclic.next()) sk = struct.unpack("\<I", sk)\[0\] # read the next encrypted dword and undo the bit shifts on it e = plain.read(4) # unswap the byte order e = struct.unpack("\>I", e)\[0\] # ROR back 5 bits e = ror(e, 5, 32) # XOR the normalized encrypted dword with the stream key dword to recover # the plain dword p = e^sk p = struct.pack("\<I", p) plain.seek(-4, os.SEEK\_CUR) plain.write(p) return plain.getvalue() def decrypt\_file(stream\_key, file): assert len(stream\_key) == key\_len # leave the first 4 bytes as-is file.seek(4, os.SEEK\_SET) # decrypt the rest of the first 1MB of the file enc\_data = file.read(0x100000 - 4) plain\_data = decrypt(stream\_key, enc\_data) file.seek(4, os.SEEK\_SET) file.write(plain\_data) # leave the rest of the file as is

|-------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | def decrypt(stream\_key, enc): size = len(enc) - 2 plain = io.BytesIO(enc) stream\_key\_cyclic = grouper(itertools.cycle(stream\_key), 4) for i in xrange(0, size\&(~3), 4): # read the next stream key dword sk = "".join(stream\_key\_cyclic.next()) sk = struct.unpack("\<I", sk)\[0\] # read the next encrypted dword and undo the bit shifts on it e = plain.read(4) # unswap the byte order e = struct.unpack("\>I", e)\[0\] # ROR back 5 bits e = ror(e, 5, 32) # XOR the normalized encrypted dword with the stream key dword to recover # the plain dword p = e^sk p = struct.pack("\<I", p) plain.seek(-4, os.SEEK\_CUR) plain.write(p) return plain.getvalue() def decrypt\_file(stream\_key, file): assert len(stream\_key) == key\_len # leave the first 4 bytes as-is file.seek(4, os.SEEK\_SET) # decrypt the rest of the first 1MB of the file enc\_data = file.read(0x100000 - 4) plain\_data = decrypt(stream\_key, enc\_data) file.seek(4, os.SEEK\_SET) file.write(plain\_data) # leave the rest of the file as is |

Unfortunately, the above solution excludes the handling a few edge cases:

1. The first 2 encrypted bytes (bytes 4:6 of the original file) are only XOR-ed with 2 key bits, and therefore:
   1. In order to decrypt the rest of the bytes we must use a stream key of idx \>= 2
   2. In order to decrypt the first two bytes we must use the stream key of idx == 0
2. If the length of the original file != 2 (mod 4), we will have len(plain) != 0 (mod 4) in encrypt(), and therefore we will have 1-2 bytes in the end of the file encrypted only by the last iteration of phase 1. Each plain bit of those bytes will be XOR-ed with only 1 key bit (which we don't have) and so we cannot decrypt them.

Moreover, as indicated by [Malwarebytes](https://blog.malwarebytes.com/threat-analysis/2018/04/lockcrypt-ransomware/) the filenames are encrypted by XOR-ing directly with a subset of the **original** key, and we won't be able to decrypt them as well. A filename of length m **can** be decrypted if we have a known filename of length n \>= m, as @demonslay335 uses in their decryptor.  
To conclude, in order to recover any arbitrary length file and filename, it looks like we actually have to recover the original encryption key and not just the stream key we described.

Recovering the original key  
The analysis we showed above in fact gave us linear equation system (over [GF(2)](<https://en.wikipedia.org/wiki/GF(2)>), in which XOR is the addition operation) which tie the stream key with the original key. If we try to generalize the analysis, the following python function can give us the original key bit indices which XOR-ed together result in each stream key bit (indexed by i):  
key\_bitlen = 25000\*8 def k\_for\_i(i): i\_dword = i \>\> 5 # the index of the dword for bit i i\_offset = i % 32 # the index of bit i in its dword i = i + key\_bitlen k = \[\] k.append((i\_dword \<\< 6) + i\_offset) if i\_offset \< 16: k.append(k\[0\] - 16) else: k.append(k\[0\] + 16) k.append((i\_dword \<\< 5) + ((i\_offset + 5) % 32)) return \[x % key\_bitlen for x in k if x \>= 0\]

|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | key\_bitlen = 25000\*8 def k\_for\_i(i): i\_dword = i \>\> 5 # the index of the dword for bit i i\_offset = i % 32 # the index of bit i in its dword i = i + key\_bitlen k = \[\] k.append((i\_dword \<\< 6) + i\_offset) if i\_offset \< 16: k.append(k\[0\] - 16) else: k.append(k\[0\] + 16) k.append((i\_dword \<\< 5) + ((i\_offset + 5) % 32)) return \[x % key\_bitlen for x in k if x \>= 0\] |

That is, stream\_key\[i\] == reduce(xor, (key\[k\] for k in k\_for\_i(i)))  
With this function, we can generate a very sparse 200000x200000 matrix over GF(2) which will describe the transformation between the original key and the stream key:  
def gen\_equations(idx): A\_i\_j\_s = \[\] for i in xrange(idx \<\< 3, (idx \<\< 3) + key\_bitlen): A\_i\_j\_s.append(k\_for\_i(i)) return A\_i\_j\_s

|-----------|------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 | def gen\_equations(idx): A\_i\_j\_s = \[\] for i in xrange(idx \<\< 3, (idx \<\< 3) + key\_bitlen): A\_i\_j\_s.append(k\_for\_i(i)) return A\_i\_j\_s |

That is, row i of the matrix A is all 0s except for the columns in A\_i\_j\_s\[i\] (which are 1).  
This is a VERY sparse matrix (each row contains only 3 values) and so it is feasible to solve it on a regular PC using linear algebra methods. For example, using the mathematics software [SageMath](https://www.sagemath.org/), we can do the following:

# fix the following parameters to those of your own stream\_key\_idx = 25000 \# the stream key idx you recovered stream\_key\_path = "key-stream-idx-25000.bin" \# the path to the stream key you recovered original\_key\_path = "key.bin" \# the path to write the original key to import itertools import struct def grouper(iterable, n, fillvalue=None): args = \[iter(iterable)\] \* n return itertools.izip\_longest(fillvalue=fillvalue, \*args) def str2bits(s): bits = \[\] for dword in grouper(s, 4): dword = "".join(dword) dword = struct.unpack("\<I", dword)\[0\] bits += \[(dword \>\> i) \& 1 for i in xrange(32)\] return bits def bits2str(bits): s = \[\] for dword\_bits in grouper(bits, 32): dword = 0 for i, bit in enumerate(dword\_bits): dword = dword | (bit \<\< i) s.append(struct.pack("\<I", dword)) return "".join(s) with open(stream\_key\_path) as f: stream\_key = f.read() assert len(stream\_key) == 25000 stream\_key\_bits = str2bits(stream\_key) Y = vector(GF(2), 200000, stream\_key\_bits) \# create a matrix with the equations for the stream key bit A\_i\_j\_s = gen\_equations(stream\_key\_idx) A = matrix(GF(2), 200000, sparse=True) for i, A\_i\_j in enumerate(A\_i\_j\_s): for j in A\_i\_j: A\[i,j\] = 1 \# recover the original key bits X = A.solve\_right(Y) key\_bits = \[int(x) for x in X.list()\] key = bits2str(key\_bits) with open(original\_key\_path, 'wb') as f: f.write(key)

|-------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | # fix the following parameters to those of your own stream\_key\_idx = 25000 # the stream key idx you recovered stream\_key\_path = "key-stream-idx-25000.bin" # the path to the stream key you recovered original\_key\_path = "key.bin" # the path to write the original key to import itertools import struct def grouper(iterable, n, fillvalue=None): args = \[iter(iterable)\] \* n return itertools.izip\_longest(fillvalue=fillvalue, \*args) def str2bits(s): bits = \[\] for dword in grouper(s, 4): dword = "".join(dword) dword = struct.unpack("\<I", dword)\[0\] bits += \[(dword \>\> i) \& 1 for i in xrange(32)\] return bits def bits2str(bits): s = \[\] for dword\_bits in grouper(bits, 32): dword = 0 for i, bit in enumerate(dword\_bits): dword = dword | (bit \<\< i) s.append(struct.pack("\<I", dword)) return "".join(s) with open(stream\_key\_path) as f: stream\_key = f.read() assert len(stream\_key) == 25000 stream\_key\_bits = str2bits(stream\_key) Y = vector(GF(2), 200000, stream\_key\_bits) # create a matrix with the equations for the stream key bit A\_i\_j\_s = gen\_equations(stream\_key\_idx) A = matrix(GF(2), 200000, sparse=True) for i, A\_i\_j in enumerate(A\_i\_j\_s): for j in A\_i\_j: A\[i,j\] = 1 # recover the original key bits X = A.solve\_right(Y) key\_bits = \[int(x) for x in X.list()\] key = bits2str(key\_bits) with open(original\_key\_path, 'wb') as f: f.write(key) |

Note that the above function assume for simplicity that idx \>= 2 and that the last bytes that were used to recover the stream key were encrypted by both phase 1 (two rounds) and phase 2, that is: len(plain) \>= 4 + ((idx + key\_len + 3) \& ~3) . The code can be fixed to address these special cases, but we did not think it was interesting.

Decrypting your files  
To summarize, the steps to recover the encryption key used by the malware:

1. Recover a plaintext (unencrypted) version of a file which was encrypted by the ransomware and is at least 25010 bytes of size
   1. We recommend doing that by installing the same software version of some software that was encrypted by the ransomware on a different computer, and try to match an encrypted DLL file to its original using their file sizes
2. Install [Python 2.7](https://www.python.org/downloads/release/python-2715/) if not already installed
3. Use the recover\_stream\_key.py script from the link below to recover the stream key for a certain idx \>= 2 from a given pair of encrypted and plaintext files.
   1. As stated above, we recommend using idx = 25000 if you have a large enough known plaintext file
4. Install [SageMath](https://www.sagemath.org/download-windows.html)
5. Open a SageMath Jupyter Notebook, paste the code snippet above, and execute it to recover the original encryption key
   1. IMPORTANT: Make sure to fix the 3 parameters on the top of the snippet to those of your own before running
   2. From our experiments, this step may take between 20 minutes to a few hours
6. Use the decryptor.py script from the link below to decrypt the encrypted files

We truly hope that any victims of this ransomware will find this analysis and scripts useful in recovering their lost files.  
You can find both scripts [here](https://github.com/pan-unit42/public_tools/tree/master/lockcrypt) within Unit 42's GitHub.
Back to top

### Tags

* [Decryptor](https://unit42.paloaltonetworks.com/tag/decryptor/ "decryptor")
* [LockCrypt](https://unit42.paloaltonetworks.com/tag/lockcrypt/ "LockCrypt")
* [Tool](https://unit42.paloaltonetworks.com/tag/tool/ "Tool")  
  [Threat Research Center](https://unit42.paloaltonetworks.com "Threat Research") [Next: OilRig Targets Technology Service Provider and Government Agency with QUADAGENT](https://unit42.paloaltonetworks.com/unit42-oilrig-targets-technology-service-provider-government-agency-quadagent/ "OilRig Targets Technology Service Provider and Government Agency with QUADAGENT")

### Related Articles

* [Open Source Tool Release: Gaining Novel AWS Access With EBS Direct APIs](https://unit42.paloaltonetworks.com/aws-ebs-direct-apis/ "article - table of contents")
* [NetWire and MITRE ChopShop](https://unit42.paloaltonetworks.com/netwire-mitre-chopshop/ "article - table of contents")
* [New Release: Decrypting NetWire C2 Traffic](https://unit42.paloaltonetworks.com/new-release-decrypting-netwire-c2-traffic/ "article - table of contents")

## Related Ransomware Resources

![Pictorial representation of Iran cyber attacks. Close-up of a person wearing glasses, with computer code reflected in the lenses.](https://unit42.paloaltonetworks.com/wp-content/uploads/2026/03/12_Security-Technology_Category_1920x900-786x368.jpg)  
[![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/07/top-threats.svg)High Profile Threats](https://unit42.paloaltonetworks.com/category/top-cyberthreats/) April 17, 2026 [#### Threat Brief: Escalation of Cyber Risk Related to Iran (Updated April 17)](https://unit42.paloaltonetworks.com/iranian-cyberattacks-2026/)

* [APK](https://unit42.paloaltonetworks.com/tag/apk/ "APK")

* [DDoS attacks](https://unit42.paloaltonetworks.com/tag/ddos-attacks/ "DDoS attacks")

* [GenAI](https://unit42.paloaltonetworks.com/tag/genai/ "GenAI")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/iranian-cyberattacks-2026/ "Threat Brief: Escalation of Cyber Risk Related to Iran (Updated April 17)")  
  ![Pictorial representation of RaaS RansomHouse. Digital representation of cybersecurity concept with a padlock superimposed over computer circuit boards, symbolizing data protection and encryption technologies.](https://unit42.paloaltonetworks.com/wp-content/uploads/2025/12/06_Ransomware_Category_1920x900-786x368.jpg)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/06/icon-threat-research.svg)Threat Research](https://unit42.paloaltonetworks.com/category/threat-research/) December 17, 2025 [#### From Linear to Complex: An Upgrade in RansomHouse Encryption](https://unit42.paloaltonetworks.com/ransomhouse-encryption-upgrade/)

* [ESXi](https://unit42.paloaltonetworks.com/tag/esxi/ "ESXi")

* [Jolly Scorpius](https://unit42.paloaltonetworks.com/tag/jolly-scorpius/ "Jolly Scorpius")

* [RansomHouse](https://unit42.paloaltonetworks.com/tag/ransomhouse/ "RansomHouse")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/ransomhouse-encryption-upgrade/ "From Linear to Complex: An Upgrade in RansomHouse Encryption")  
  ![Pictorial representation of 01flip ransomware written in Rust. Digital artwork of a pixelated U.S. dollar bill disintegrating into small blocks against a blue data matrix background.](https://unit42.paloaltonetworks.com/wp-content/uploads/2025/12/05_Ransomware_Category_1920x900-786x368.jpg)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/06/icon-threat-research.svg)Threat Research](https://unit42.paloaltonetworks.com/category/threat-research/) December 10, 2025 [#### 01flip: Multi-Platform Ransomware Written in Rust](https://unit42.paloaltonetworks.com/new-ransomware-01flip-written-in-rust/)

* [Bitcoin](https://unit42.paloaltonetworks.com/tag/bitcoin/ "Bitcoin")

* [CL-CRI-103](https://unit42.paloaltonetworks.com/tag/cl-cri-103/ "CL-CRI-103")

* [Cryptocurrency](https://unit42.paloaltonetworks.com/tag/cryptocurrency/ "Cryptocurrency")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/new-ransomware-01flip-written-in-rust/ "01flip: Multi-Platform Ransomware Written in Rust")  
  ![Pictorial representation of malicious LLMs. Close-up view of a digital wall displaying various glowing icons, representing a high-tech network interface.](https://unit42.paloaltonetworks.com/wp-content/uploads/2025/11/AdobeStock_1270203474-786x440.jpeg)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/06/icon-threat-research.svg)Threat Research](https://unit42.paloaltonetworks.com/category/threat-research/) November 25, 2025 [#### The Dual-Use Dilemma of AI: Malicious LLMs](https://unit42.paloaltonetworks.com/dilemma-of-ai-malicious-llms/)

* [Credential Harvesting](https://unit42.paloaltonetworks.com/tag/credential-harvesting/ "Credential Harvesting")

* [Data exfiltration](https://unit42.paloaltonetworks.com/tag/data-exfiltration/ "data exfiltration")

* [LLM](https://unit42.paloaltonetworks.com/tag/llm/ "LLM")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/dilemma-of-ai-malicious-llms/ "The Dual-Use Dilemma of AI: Malicious LLMs")  
  ![Constellation image representing the constellation schema used by Palo Alto Networks Unit 42 to track nation-state and cybercrime threat actor groups](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/06/Generic-B-1920x900-1-786x368.png)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/07/threat-actor-groups.svg)Threat Actor Groups](https://unit42.paloaltonetworks.com/category/threat-actor-groups/) August 1, 2025 [#### Threat Actor Groups Tracked by Palo Alto Networks Unit 42 (Updated Aug. 1, 2025)](https://unit42.paloaltonetworks.com/threat-actor-groups-tracked-by-palo-alto-networks-unit-42/)

* [Academic Serpens](https://unit42.paloaltonetworks.com/tag/academic-serpens/ "Academic Serpens")

* [Agent Serpens](https://unit42.paloaltonetworks.com/tag/agent-serpens/ "Agent Serpens")

* [Agonizing Serpens](https://unit42.paloaltonetworks.com/tag/agonizing-serpens/ "Agonizing Serpens")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/threat-actor-groups-tracked-by-palo-alto-networks-unit-42/ "Threat Actor Groups Tracked by Palo Alto Networks Unit 42 (Updated Aug. 1, 2025)")  
  ![Pictorial representation of Unit 42 threat attribution system. Illustration featuring a white triangle centered within an abstract cosmic background of purple and blue swirls and stars.](https://unit42.paloaltonetworks.com/wp-content/uploads/2025/07/Generic-A-1920x900-1-786x368.png)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/07/threat-actor-groups.svg)Threat Actor Groups](https://unit42.paloaltonetworks.com/category/threat-actor-groups/) July 31, 2025 [#### Introducing Unit 42's Attribution Framework](https://unit42.paloaltonetworks.com/unit-42-attribution-framework/)

* [Advanced Persistent Threat](https://unit42.paloaltonetworks.com/tag/advanced-persistent-threat/ "Advanced Persistent Threat")

* [Bookworm](https://unit42.paloaltonetworks.com/tag/bookworm/ "Bookworm")

* [Nomenclature](https://unit42.paloaltonetworks.com/tag/nomenclature/ "nomenclature")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/unit-42-attribution-framework/ "Introducing Unit 42’s Attribution Framework")  
  ![Pictorial representation of the ransomware landscape. Digital artwork of a disintegrating U.S. dollar bill with pixelated effects on a cyber-inspired background.](https://unit42.paloaltonetworks.com/wp-content/uploads/2025/04/05_Ransomware_Category_1920x900-786x368.jpg)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/06/icon-report-white-1.svg)Trend Reports](https://unit42.paloaltonetworks.com/category/trend-reports/) April 23, 2025 [#### Extortion and Ransomware Trends January-March 2025](https://unit42.paloaltonetworks.com/2025-ransomware-extortion-trends/)

* [BianLian](https://unit42.paloaltonetworks.com/tag/bianlian/ "BianLian")

* [Akira ransomware](https://unit42.paloaltonetworks.com/tag/akira-ransomware/ "Akira ransomware")

* [Muddled Libra](https://unit42.paloaltonetworks.com/tag/muddled-libra/ "Muddled Libra")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/2025-ransomware-extortion-trends/ "Extortion and Ransomware Trends January-March 2025")  
  ![A pictorial representation of Akira ransomware, distributed by Howling Scorpius. A person's hand typing on a keyboard with a digital screen displaying the word "password" highlighted in blue, set against a backdrop of various cybersecurity interface graphics.](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/11/09_Cybercrime_Category_1920x900-786x368.jpg)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/07/top-threats.svg)High Profile Threats](https://unit42.paloaltonetworks.com/category/top-cyberthreats/) December 2, 2024 [#### Threat Assessment: Howling Scorpius (Akira Ransomware)](https://unit42.paloaltonetworks.com/threat-assessment-howling-scorpius-akira-ransomware/)

* [Howling Scorpius](https://unit42.paloaltonetworks.com/tag/howling-scorpius/ "Howling Scorpius")

* [Leak site](https://unit42.paloaltonetworks.com/tag/leak-site/ "Leak site")

* [Torrenting](https://unit42.paloaltonetworks.com/tag/torrenting/ "torrenting")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/threat-assessment-howling-scorpius-akira-ransomware/ "Threat Assessment: Howling Scorpius (Akira Ransomware)")  
  ![Pictorial representation of a threat like BlackSuit ransomware. An illustration of a modern workspace with a laptop displaying cybersecurity icons, surrounded by stacks of coins and a credit card, all depicted in a neon, digital art style.](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/11/04_Ransomware_Category_1920x900-786x368.jpg)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/07/top-threats.svg)High Profile Threats](https://unit42.paloaltonetworks.com/category/top-cyberthreats/) November 20, 2024 [#### Threat Assessment: Ignoble Scorpius, Distributors of BlackSuit Ransomware](https://unit42.paloaltonetworks.com/threat-assessment-blacksuit-ransomware-ignoble-scorpius/)

* [BlackSuit ransomware](https://unit42.paloaltonetworks.com/tag/blacksuit-ransomware/ "BlackSuit ransomware")

* [Construction](https://unit42.paloaltonetworks.com/tag/construction/ "construction")

* [Education](https://unit42.paloaltonetworks.com/tag/education/ "Education")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/threat-assessment-blacksuit-ransomware-ignoble-scorpius/ "Threat Assessment: Ignoble Scorpius, Distributors of BlackSuit Ransomware")  
  ![A representation of a threat group like Jumpy Pisces. Illustrative image featuring two fish and the Pisces constellation superimposed on a stylized, abstract background with flowing purple waves and a starry night sky.](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/10/Pisces-NK-A-1920x900-1-786x368.png)  
  [![category icon](https://unit42.paloaltonetworks.com/wp-content/uploads/2024/07/threat-actor-groups.svg)Threat Actor Groups](https://unit42.paloaltonetworks.com/category/threat-actor-groups/) October 30, 2024 [#### Jumpy Pisces Engages in Play Ransomware](https://unit42.paloaltonetworks.com/north-korean-threat-group-play-ransomware/)

* [North Korea](https://unit42.paloaltonetworks.com/tag/north-korea/ "North Korea")

* [Jumpy Pisces](https://unit42.paloaltonetworks.com/tag/jumpy-pisces/ "Jumpy Pisces")

* [Fiddling Scorpius](https://unit42.paloaltonetworks.com/tag/fiddling-scorpius/ "Fiddling Scorpius")  
  [Read now ![Right arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-right-arrow-withtail.svg)](https://unit42.paloaltonetworks.com/north-korean-threat-group-play-ransomware/ "Jumpy Pisces Engages in Play Ransomware")

* ![Slider arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/slider-arrow-left.svg)

* ![Slider arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/slider-arrow-left.svg)  
  ![Close button](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/close-modal.svg) ![Enlarged Image]()  
  ![Newsletter](https://unit42.paloaltonetworks.com/wp-content/uploads/2026/03/unit42-footer-subscribe-desktop.png)  
  ![UNIT 42 Small Logo](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/palo-alto-logo-small.svg) Get updates from Unit 42

## Peace of mind comes from staying ahead of threats. Subscribe today.

Your Email

Subscribe for email updates to all Unit 42 threat research.  
By submitting this form, you agree to our [Terms of Use](https://www.paloaltonetworks.com/legal-notices/terms-of-use "Terms of Use") and acknowledge our [Privacy Statement.](https://www.paloaltonetworks.com/legal-notices/privacy "Privacy Statement")

This site is protected by reCAPTCHA and the Google [Privacy Policy](https://policies.google.com/privacy) and [Terms of Service](https://policies.google.com/terms) apply.

Invalid captcha!
Subscribe ![Right Arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/right-arrow.svg) ![loader](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-loader.svg)  
{#footer} Products and Services

* [AI-Powered Network Security Platform](https://www.paloaltonetworks.com/network-security)

* [Secure AI by Design](https://www.paloaltonetworks.com/ai-security)

* [Prisma AIRS](https://www.paloaltonetworks.com/ai-security/prisma-airs)

* [AI Access Security](https://www.paloaltonetworks.com/sase/ai-access-security)

* [Cloud Delivered Security Services](https://www.paloaltonetworks.com/network-security/security-subscriptions)

* [Advanced Threat Prevention](https://www.paloaltonetworks.com/network-security/advanced-threat-prevention)

* [Advanced URL Filtering](https://www.paloaltonetworks.com/network-security/advanced-url-filtering)

* [Advanced WildFire](https://www.paloaltonetworks.com/network-security/advanced-wildfire)

* [Advanced DNS Security](https://www.paloaltonetworks.com/network-security/advanced-dns-security)

* [Enterprise Data Loss Prevention](https://www.paloaltonetworks.com/sase/enterprise-data-loss-prevention)

* [Enterprise IoT Security](https://www.paloaltonetworks.com/network-security/enterprise-device-security)

* [Medical IoT Security](https://www.paloaltonetworks.com/network-security/medical-device-security)

* [Industrial OT Security](https://www.paloaltonetworks.com/network-security/ot-security-solution)

* [SaaS Security](https://www.paloaltonetworks.com/sase/saas-security)

* [Next-Generation Firewalls](https://www.paloaltonetworks.com/network-security/next-generation-firewall)

* [Hardware Firewalls](https://www.paloaltonetworks.com/network-security/hardware-firewall-innovations)

* [Software Firewalls](https://www.paloaltonetworks.com/network-security/software-firewalls)

* [Strata Cloud Manager](https://www.paloaltonetworks.com/network-security/strata-cloud-manager)

* [SD-WAN for NGFW](https://www.paloaltonetworks.com/network-security/sd-wan-subscription)

* [PAN-OS](https://www.paloaltonetworks.com/network-security/pan-os)

* [Panorama](https://www.paloaltonetworks.com/network-security/panorama)

* [Secure Access Service Edge](https://www.paloaltonetworks.com/sase)

* [Prisma SASE](https://www.paloaltonetworks.com/sase)

* [Application Acceleration](https://www.paloaltonetworks.com/sase/app-acceleration)

* [Autonomous Digital Experience Management](https://www.paloaltonetworks.com/sase/adem)

* [Enterprise DLP](https://www.paloaltonetworks.com/sase/enterprise-data-loss-prevention)

* [Prisma Access](https://www.paloaltonetworks.com/sase/access)

* [Prisma Browser](https://www.paloaltonetworks.com/sase/prisma-browser)

* [Prisma SD-WAN](https://www.paloaltonetworks.com/sase/sd-wan)

* [Remote Browser Isolation](https://www.paloaltonetworks.com/sase/remote-browser-isolation)

* [SaaS Security](https://www.paloaltonetworks.com/sase/saas-security)

* [AI-Driven Security Operations Platform](https://www.paloaltonetworks.com/cortex)

* [Cloud Security](https://www.paloaltonetworks.com/cortex/cloud)

* [Cortex Cloud](https://www.paloaltonetworks.com/cortex/cloud)

* [Application Security](https://www.paloaltonetworks.com/cortex/cloud/application-security)

* [Cloud Posture Security](https://www.paloaltonetworks.com/cortex/cloud/cloud-posture-security)

* [Cloud Runtime Security](https://www.paloaltonetworks.com/cortex/cloud/runtime-security)

* [Prisma Cloud](https://www.paloaltonetworks.com/prisma/cloud)

* [AI-Driven SOC](https://www.paloaltonetworks.com/cortex)

* [Cortex XSIAM](https://www.paloaltonetworks.com/cortex/cortex-xsiam)

* [Cortex XDR](https://www.paloaltonetworks.com/cortex/cortex-xdr)

* [Cortex XSOAR](https://www.paloaltonetworks.com/cortex/cortex-xsoar)

* [Cortex Xpanse](https://www.paloaltonetworks.com/cortex/cortex-xpanse)

* [Unit 42 Managed Detection \& Response](https://www.paloaltonetworks.com/cortex/managed-detection-and-response)

* [Managed XSIAM](https://www.paloaltonetworks.com/cortex/managed-xsiam)

* [Next-Generation Identity Security](https://www.paloaltonetworks.com/idira)

* [Privileged Access Management](https://www.paloaltonetworks.com/idira/human/privileged-access-management)

* [Identity and Access Management](https://www.paloaltonetworks.com/idira/human/identity-and-access-management)

* [Endpoint Privilege Manager](https://www.paloaltonetworks.com/idira/human/endpoint-privilege-manager)

* [Identity Governance](https://www.paloaltonetworks.com/idira/human/identity-governance)

* [Workforce Password Management](https://www.paloaltonetworks.com/idira/human/workforce-password-management)

* [Agentic Identities](https://www.paloaltonetworks.com/idira/agentic)

* [Secrets Management](https://www.paloaltonetworks.com/idira/machine/secrets-management)

* [Unified Secrets Governance](https://www.paloaltonetworks.com/idira/machine/unified-secrets-governance)

* [Application Credentials Delivery](https://www.paloaltonetworks.com/idira/machine/application-credentials-delivery)

* [Vendor Privileged Access](https://www.paloaltonetworks.com/idira/human/vendor-privileged-access)

* [Threat Intel and Incident Response Services](https://www.paloaltonetworks.com/unit42)

* [Prepare for Emerging Risks](https://www.paloaltonetworks.com/unit42/frontier-ai-defense)

* [Strengthen Your Defenses](https://www.paloaltonetworks.com/unit42/strengthen-your-defenses)

* [Build Your Security Strategy](https://www.paloaltonetworks.com/unit42/build-your-security-strategy)

* [Understand the Adversary](https://www.paloaltonetworks.com/unit42/threat-intelligence)

* [Respond to a Cyber Attack](https://www.paloaltonetworks.com/unit42/respond)  
  Company

* [About Us](https://www.paloaltonetworks.com/about-us)

* [Careers](https://jobs.paloaltonetworks.com/en/)

* [Contact Us](https://www.paloaltonetworks.com/company/contact-sales)

* [Corporate Responsibility](https://www.paloaltonetworks.com/about-us/corporate-responsibility)

* [Customers](https://www.paloaltonetworks.com/customers)

* [Investor Relations](https://investors.paloaltonetworks.com/)

* [Location](https://www.paloaltonetworks.com/about-us/locations)

* [Newsroom](https://www.paloaltonetworks.com/company/newsroom)  
  Popular Links

* [Blog](https://www.paloaltonetworks.com/blog/)

* [Communities](https://www.paloaltonetworks.com/communities)

* [Content Library](https://www.paloaltonetworks.com/resources)

* [Cyberpedia](https://www.paloaltonetworks.com/cyberpedia)

* [Event Center](https://events.paloaltonetworks.com/)

* [Manage Email Preferences](https://start.paloaltonetworks.com/preference-center)

* [Products A-Z](https://www.paloaltonetworks.com/products/products-a-z)

* [Product Certifications](https://www.paloaltonetworks.com/legal-notices/trust-center/certifications)

* [Report a Vulnerability](https://www.paloaltonetworks.com/security-disclosure)

* [Sitemap](https://www.paloaltonetworks.com/sitemap)

* [Tech Docs](https://docs.paloaltonetworks.com/)

* [Unit 42](https://unit42.paloaltonetworks.com/)

* [Do Not Sell or Share My Personal Information](https://panwedd.exterro.net/portal/dsar.htm?target=panwedd)
  ![Palo Alto Networks Logo](https://www.paloaltonetworks.com/etc/clientlibs/clean/imgs/pan-logo-dark.svg)

* [Privacy](https://www.paloaltonetworks.com/legal-notices/privacy)

* [Trust Center](https://www.paloaltonetworks.com/legal-notices/trust-center)

* [Terms of Use](https://www.paloaltonetworks.com/legal-notices/terms-of-use)

* [Documents](https://www.paloaltonetworks.com/legal)

Copyright © 2026 Palo Alto Networks. All Rights Reserved

* [![Youtube](https://www.paloaltonetworks.com/etc/clientlibs/clean/imgs/social/youtube-black.svg)](https://www.youtube.com/user/paloaltonetworks)
* [![Podcast](https://www.paloaltonetworks.com/content/dam/pan/en_US/images/icons/podcast.svg)](https://www.paloaltonetworks.com/podcasts/threat-vector)
* [![Facebook](https://www.paloaltonetworks.com/etc/clientlibs/clean/imgs/social/facebook-black.svg)](https://www.facebook.com/PaloAltoNetworks/)
* [![LinkedIn](https://www.paloaltonetworks.com/etc/clientlibs/clean/imgs/social/linkedin-black.svg)](https://www.linkedin.com/company/palo-alto-networks)
* [![Twitter](https://www.paloaltonetworks.com/etc/clientlibs/clean/imgs/social/twitter-x-black.svg)](https://twitter.com/PaloAltoNtwks)
* EN  
  Select your language  
  ![Play](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/player-play-icon.svg) ![Pause](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/player-pause-icon1.svg) ![Minimize](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-minimize.svg) ![Close button](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/close-modal.svg)

### Default Heading

Read the article ![Right Arrow](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/right-arrow.svg)  
Seekbar

![Play](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/player-play-icon.svg) ![Pause](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/player-pause-icon1.svg)  
![Volume](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-volume.svg)  
Volume
![Minimize](https://unit42.paloaltonetworks.com/wp-content/themes/unit42-v6/dist/images/icons/icon-minimize.svg)
