LockFile is a new ransomware family that emerged in July 2021 following the discovery in April 2021 of the ProxyShell vulnerabilities in Microsoft Exchange servers. LockFile ransomware appears to exploit the ProxyShell vulnerabilities to breach targets with unpatched, on premises Microsoft Exchange servers, followed by a PetitPotam NTLM relay attack to seize control of the domain.
In this detailed analysis of the LockFile ransomware, we reveal its novel approach to file encryption and how the ransomware tries to bypass behavior and statistics-based ransomware protection.
This article discusses the following key findings in depth:
LockFile ransomware encrypts every 16 bytes of a file. We call this “intermittent encryption,” and this is the first time Sophos researchers have seen this approach used. Intermittent encryption helps the ransomware to evade detection by some ransomware protection solutions because an encrypted document looks statistically very similar to the unencrypted original.
Like WastedLocker and Maze ransomware, LockFile ransomware uses memory mapped input/output (I/O) to encrypt a file. This technique allows the ransomware to transparently encrypt cached documents in memory and causes the operating system to write the encrypted documents, with minimal disk I/O that detection technologies would spot.
The ransomware doesn’t need to connect to a command-and-control center to communicate, which also helps to keep its activities under the detection radar.
Additionally, LockFile renames encrypted documents to lower case and adds a .lockfile file extension, and its HTA ransom note looks very similar to that of LockBit 2.0.
The Sophos research is based on a LockFile sample with the SHA-256 hash: bf315c9c064b887ee3276e1342d43637d8c0e067260946db45942f39b970d7ce. This file can be found on VirusTotal.
If you load this sample in Ghidra, you will notice it only has three functions and three sections.
Three functionsThree sectionsThe binary appears to be dual packed by UPX and malformed to throw off static analysis by endpoint protection software. Also, the original section names were altered from UPX0 and UPX1 into OPEN and CLSE .
The first section, named OPEN, has a size of 592 KB (0x94000) but contains no data – only zeroes.
The second section, CLSE, has a size of 286 KB (0x43000), and the three functions are in the last page of this section. The rest of the data is encoded code that is decoded later and placed in the ‘OPEN’ section.
The entry() function is simple and calls FUN_1400d71c0():
Simple entryThe FUN_1400d71c0() function decodes the data from the CLSE section and puts it in the OPEN section. It also resolves the necessary DLLs and functions. Then it manipulates the IMAGE_SCN_CNT_UNINITIALIZED_DATA values and jumps to the code placed in the OPEN section.
Analyzing the OPEN section
Because the rest of the code is unpacked in the OPEN section, i.e., it is runtime generated, we used WinDbg and .writemem to write the OPEN section to disk, so we can analyze the code statically in Ghidra, e.g.:
.writemem c:[redacted]LockFilesec_open.bin lockfileexe+1000 L94000
After loading the file into Ghidra for analysis, we find a main start function:
The main function is the C runtime libraryThis is CRT, the C runtime library, not the real main function we’re looking for. However, after digging around we find it:
Finding the real main functionWe rename it to main_000861() and keep the address on hand so we can use it for reference when debugging in WinDbg.
The first part initializes a crypto library:
Initializing the crypto libraryWe find strings in the code, such as ‘Cryptographic algorithms are disabled after’ that are also used in this freely available Crypto++ Library on GitHub, so it is safe to assume that LockFile ransomware leverages this library for its encryption functions.
It then creates a mutex, to prevent the ransomware from running twice at the same time:
Creating mutexTerminating critical business processes
Then a string is decoded, which is a parameter for the system() call at line 161.
Encoded string containing a list of business critical processes to terminateThe string is a parameter for the system() call at line 161. This terminates all processes with vmwp in their name. To do this, the Windows Management Interface (WMI) command-line tool WMIC.EXE, which is part of every Windows installation, is leveraged. This action is repeated for other business critical processes associated with virtualization software and databases:
Hyper-V virtual machines
wmic process where “name like ‘%vmwp%’” call terminate
Oracle VM Virtual Box manager
wmic process where “name like ‘%virtualbox%’” call terminate
Oracle VM Virtual Box services
wmic process where “name like ‘%vbox%’” call terminate
Microsoft SQL Server, also used by SharePoint, Exchange
wmic process where “name like ‘%sqlservr%’” call terminate
wmic process where “name like ‘%mysqld%’” call terminate
Oracle MTS Recovery Service
wmic process where “name like ‘%omtsreco%’” call terminate
Oracle RDBMS Kernel
wmic process where “name like ‘%oracle%’” call terminate
Oracle TNS Listener
wmic process where “name like ‘%tnslsnr%’” call terminate
VMware virtual machines
wmic process where “name like ‘%vmware%’” call terminate
By leveraging WMI, the ransomware itself is not directly associated with the abrupt termination of these typical business critical processes. Terminating these processes will ensure that any locks on associated files/databases are released, so that these objects are ready for malicious encryption.
The code continues to retrieve all drive letters with GetLogicalDriveString() at line 692 and iterates through them.
LockFile creates another thread for each driveIn the loop, it determines the drive type via GetDriveType(). When this is a fixed disk (type three = DRIVE_FIXED at line 703), it spawns a new thread (at lines 705, 706), with the function 0x7f00 as the start address.
Ransom note is an HTML application
The function at 0x7f00 first creates the HTA ransom note, e.g., ‘LOCKFILE-README-[hostname]-[id].hta’ in the root of the drive. Instead of dropping a note in TXT format, LockFile formats its ransom note as a HTML Application (HTA) file. Interestingly, the HTA ransom note used by LockFile closely resembles the one used by LockBit 2.0 ransomware:
The LockFile ransom note looks very much like…
…the LockBit ransom note
In its ransom note, the LockFile adversary asks victims to contact a specific e-mail address: firstname.lastname@example.org. The domain name used, ‘contipauper.com’ appears to be a derogatory reference to a competing ransomware group called Conti. The domain name seems to have been created on August 16, 2021.
Then EncryptDir_00007820() is called at line six. The first part of the encrypt directory function is not very noteworthy:
Function at 0x7f00But the second part is:
The ransomware uses FindFirstFile() at line 63 and FindNextFile() at line 129 to iterate through the directory in param_1.
In the first part (lines 66-91), it checks if the filename does not contain:
Then it runs through two lists of known file type extensions of documents it doesn’t attack (lines 92-102).
.a3l .a3m .a4l .a4p .a5l .abk .abs .acp .ada .adb .add .adf .adi .adm .adp .adr .ads .af2 .afm .aif .aifc .aiff .aim .ais .akw .alaw .tlog .vsix .pch .json .nupkg .pdb .ipdb .alb .all .ams .anc .ani .ans .api .aps .arc .ari .arj .art .asa .asc .asd .ase .asf .xaml .aso .asp .ast .asv .asx .ico .rll .ado .jsonlz4 .cat .gds .atw .avb .avi .avr .avs .awd .awr .axx .bas .bdf .bgl .bif .biff .bks .bmi .bmk .book .box .bpl .bqy .brx .bs1 .bsc .bsp .btm .bud .bun .bw .bwv .byu .c0l .cal .cam .cap .cas .cat .cca .ccb .cch .ccm .cco .cct .cda .cdf .cdi .cdm .cdt .cdx .cel .cfb .cfg .cfm .cgi .cgm .chk .chp .chr .cht .cif .cil .cim .cin .ck1 .ck2 .ck3 .ck4 .ck5 .ck6 .class .cll .clp .cls .cmd .cmf .cmg .cmp .cmv .cmx .cnf .cnm .cnq .cnt .cob .cpd .cpi .cpl .cpo .cpr .cpx .crd .crp .csc .csp .css .ctl .cue .cur .cut .cwk .cws .cxt .d64 .dbc .dbx .dc5 .dcm .dcr .dcs .dct .dcu .dcx .ddf .ddif .def .defi .dem .der .dewf .dib .dic .dif .dig .dir .diz .dlg .dll .dls .dmd .dmf .dpl .dpr .drv .drw .dsf .dsg .dsm .dsp .dsq .dst .dsw .dta .dtf .dtm .dun .dwd .dwg .dxf .dxr .eda .edd .ede .edk .edq .eds .edv .efa .efe .efk .efq .efs .efv .emd .emf .eml .enc .enff .ephtml .eps .epsf .epx .eri .err .esps .eui .evy .ewl .exc .exe .f2r .f3r .f77 .f90 .far .fav .fax .fbk .fcd .fdb .fdf .fft .fif .fig .fits .fla .flc .flf .flt .fmb .fml .fmt .fnd .fng .fnk .fog .fon .for .fot .fp1 .fp3 .fpt .frt .frx .fsf .fsl .fsm .ftg .fts .fw2 .fw3 .fw4 .fxp .fzb .fzf .fzv .gal .gdb .gdm .ged .gen .getright .gfc .gfi .gfx .gho .gid .gif .gim .gix .gkh .gks .gna .gnt .gnx .gra .grd .grf .grp .gsm .gt2 .gtk .gwx .gwz .hcm .hcom .hcr .hdf .hed .hel .hex .hgl .hlp .hog .hpj .hpp .hqx .hst .htt .htx .hxm .ica .icb .icc .icl .icm .idb .idd .idf .idq .idx .iff .igf .iif .ima .imz .inc .inf .ini .ins .int .iso .isp .ist .isu .its .ivd .ivp .ivt .ivx .iwc .j62 .java .jbf .jmp .jn1 .jtf .k25 .kar .kdc .key .kfx .kiz .kkw .kmp .kqp .kr1 .krz .ksf .lab .ldb .ldl .leg .les .lft .lgo .lha .lib .lin .lis .lnk .log .llx .lpd .lrc .lsl .lsp .lst .lwlo .lwob .lwp .lwsc .lyr .lzh .lzs .m1v .m3d .m3u .mac .magic .mak .mam .man .map .maq .mar .mas .mat .maud .maz .mb1 .mbox .mbx .mcc .mcp .mcr .mcw .mda .mdb .mde .mdl .mdn .mdw .mdz .med .mer .met .mfg .mgf .mic .mid .mif .miff .mim .mli .mmf .mmg .mmm .mmp .mn2 .mnd .mng .mnt .mnu .mod .mov .mp2 .mpa .mpe .mpp .mpr .mri .msa .msdl .msg .msn .msp .mst .mtm .mul .mus .mus10 .mvb .nan .nap .ncb .ncd .ncf .ndo .nff .nft .nil .nist .nlb .nlm .nls .nlu .nod .ns2 .nsf .nso .nst .ntf .ntx .nwc .nws .o01 .obd .obj .obz .ocx .ods .off .ofn .oft .okt .olb .ole .oogl .opl .opo .opt .opx .or2 .or3 .ora .orc .org .oss .ost .otl .out .p10 .p3 .p65 .p7c .pab .pac .pak .pal .part .pas .pat .pbd .pbf .pbk .pbl .pbm .pbr .pcd .pce .pcl .pcm .pcp .pcs .pct .pcx .pdb .pdd .pdp .pdq .pds .pf .pfa .pfb .pfc .pfm .pgd .pgl .pgm .pgp .pict .pif .pin .pix .pjx .pkg .pkr .plg .pli .plm .pls .plt .pm5 .pm6 .pog .pol .pop .pot .pov .pp4 .ppa .ppf .ppm .ppp .pqi .prc .pre .prf .prj .prn .prp .prs .prt .prv .psb .psi .psm .psp .ptd .ptm .pwl .pwp .pwz .qad .qbw .qd3d .qdt .qfl .qic .qif .qlb .qry .qst .qti .qtp .qts .qtx .qxd .ram .ras .rbh .rcc .rdf .rdl .rec .reg .rep .res .rft .rgb .rmd .rmf .rmi .rom .rov .rpm .rpt .rrs .rsl .rsm .rtk .rtm .rts .rul .rvp .s3i .s3m .sam .sav .sbk .sbl .sc2 .sc3 .scc .scd .scf .sci .scn .scp .scr .sct01 .scv .sd2 .sdf .sdk .sdl .sdr .sds .sdt .sdv .sdw .sdx .sea .sep .ses .sf .sf2 .sfd .sfi .sfr .sfw .shw .sig .sit .siz .ska .skl .slb .sld .slk .sm3 .smp .snd .sndr .sndt .sou .spd .spl .sqc .sqr .ssd .ssf .st .stl .stm .str .sty .svx .swa .swf .swp .sys .syw .t2t .t64 .taz .tbk .tcl .tdb .tex .tga .tgz .tig .tlb .tle .tmp .toc .tol .tos .tpl .tpp .trk .trm .trn .ttf .tz .uwf .v8 .vap .vbp .vbw .vbx .vce .vcf .vct .vda .vi .viff .vir .viv .vqe .vqf .vrf .vrml .vsd .vsl .vsn .vst .vsw .vxd .wcm .wdb .wdg .web .wfb .wfd .wfm .wfn .xml .acc .adt .adts .avi .bat .bmp .cab .cpl .dll .exe .flv .gif .ini .iso .jpeg .jpg .m4a .mov .mp3 .mp4 .mpeg .msi .mui .php .png .sys .wmv .xml
.acc .adt .adts .avi .bat .bmp .cab .cpl .dll .exe .flv .gif .ini .iso .jpeg .jpg .m4a .mov .mp3 .mp4 .mpeg .msi .mui .php
Note: Interestingly, this ransomware doesn’t attack JPG image files, like photos.
If the file extension of a found document is not on the list, the code concatenates the filename and path (line 103) and calls EncryptFile_00007360() to encrypt the document.
The EncryptFile_00007360() function encrypts the document via memory mapped I/O:
Encrypting a document via memory mapped I/OThe document is first opened at line 164 and at line 177 the function CreateFileMapping() maps the document into memory. At line 181, lVar17 points to the now memory mapped document.
The code continues by appending the decryption blob to the end of the document in memory. Here is an example of a test document comprising the character ‘a’ (0x61), 128 times:
Test document consisting of 128 times the character ‘a’ (0x61)After the decryption blob is added, the memory mapped document now looks like this:
Decryption blob is appended to the memory mapped test documentFurther on, the document gets encrypted, 16 bytes at the time, via function EncryptBuffer_0002cbf4() at line 271:
16 bytes intermittent encryptionEncryptBuffer_0002cbf4() encrypts 16 bytes in the received buffer lVar15. This is set to lVar7 at line 268, which points to the memory mapped document.
Interestingly, it then adds 0x20 (32 bytes) to lVar15, skipping 16 bytes. This makes the encryption intermittent:
The memory mapped test document after one passThe memory mapped test document after a second passThe memory mapped test document after all bytes were processedAn animated image comparing an original document to LockFile’s intermittently encrypted output.The notable feature of this ransomware is not the fact that it implements partial encryption. LockBit 2.0, DarkSide and BlackMatter ransomware, for example, are all known to encrypt only part of the documents they attack (in their case the first 4,096 bytes, 512 KB and 1 MB respectively,) just to finish the encryption stage of the attack faster.
What sets LockFile apart is that is doesn’t encrypt the first few blocks. Instead, LockFile encrypts every other 16 bytes of a document. This means that a text document, for instance, remains partially readable.
There is an intriguing advantage to taking this approach: intermittent encryption skews statistical analysis and that confuses some protection technologies.
Evading ransomware protection by skewing statistical analysis
The intermittent encryption approach adopted by LockFile skews analysis such as the chi-squared (chi^2) used by some ransomware protection software.
An unencrypted text file of 481 KB (say, a book) has a chi^2 score of 3850061. If the document was encrypted by DarkSide ransomware, it would have a chi^2 score of 334 – which is a clear indication that the document has been encrypted. If the same document is encrypted by LockFile ransomware, it would still have a significantly high chi^2 score of 1789811.
The following graphical representations (byte/character distribution) show the same text document encrypted by DarkSide and LockFile.
Original text document
Text document encrypted by DarkSide
Text document encrypted by LockFile
As you can see, the graphical representation of the text document encrypted by LockFile looks very similar to the original. This trick will be successful against ransomware protection software that performs content inspection with statistical analysis to detect encryption.
We haven’t seen intermittent encryption used before in ransomware attacks.
Persisting the encrypted document to disk
After the encryption, the document is closed (line 279-281) and the file is moved (renamed):
The string ‘%s.lockfile is decoded (in lines 284-298) and then passed to the sprintf() function at line 300 to append ‘.lockfile’ to the filename. I
In line 301 the original filename is changed to the new filename. Interestingly, the file is renamed to lower case and it is unlikely that a LockFile decrypter would be able to restore the filename to its original state, i.e., upper casing in the filename is lost forever.
Since the attack leverages CreateFileMapping(), the encrypted memory mapped document is written (persisted) to disk by the Windows System process, PID 4. This can be witnessed via Sysinternals Process Monitor. In the figure below we removed the Process Monitor filter that excludes activity by the System process (PID 4):
By leveraging memory mapped I/O, ransomware can more quickly access documents that were cached and let the Windows System process perform the write action. By letting the System process perform the WriteFile operation, the actual encrypted bytes are written by the operating system itself – disjoined from the actual malicious process.
In the example above, this happens six seconds after the ransomware encrypts the document, but on large systems this delay can extend to minutes. This trick alone can be successful in evading detection by some behavior-based anti-ransomware solutions.
The use of memory mapped I/O is not common among ransomware families, although it was used by the Maze ransomware and by the (less frequently seen) WastedLocker ransomware.
No ransomware to remove
Once it has encrypted all the documents on the machine, the ransomware deletes itself with the following command:
cmd /c ping 127.0.0.1 -n 5 && del “C:UsersMarkDesktopLockFile.exe” && exit
The PING command sends five ICMP messages to the localhost (i.e., itself), and this is simply intended as a five second sleep to allow the ransomware process to close itself before executing the DEL command to delete the ransomware binary.
This means that after the ransomware attack, there is no ransomware binary for incident responders or antivirus software to find or clean up.
Note: Like most human-operated ransomware nowadays, LockFile ransomware doesn’t need to contact a command-and-control (C2) server on the internet to operate. This means that it can encrypt data on machines that do not have internet access.
Sophos would also like to acknowledge SophosLabs researchers Alex Vermaning and Gabor Szappanos for their contributions to this report.