Blog

FLOSS Version 2.0

William Ballenthin, Moritz Raabe, Blaine Stancill
Jun 21, 2022
5 min read

The FLARE Obfuscated String Solver (FLOSS) has been supporting analysts to extract hidden strings from malware samples for many years now. Over the last few months, we’ve added new functionality and improved the tool’s performance. In this blog post we will share exciting new features and improvements including a new string deobfuscation technique, simplified tool usage, and much faster result output. We’ve also updated the FLOSS logo:

FLOSS LOGO

Reminder: FLOSS extracts strings from malware

FLOSS analyzes compiled programs, identifies functions that may decode data, and automatically deobfuscates hidden strings. Initially, FLOSS extracted three major string types: static strings included verbatim, encoded strings decoded by dedicated functions, and stack strings constructed piecemeal. For more background, or a refresher on FLOSS, please review our original blog post.

With this new release, we’ve taught FLOSS how to decode a whole new class of obfuscated strings...

New string deobfuscation: tight strings

With our newest update, FLOSS handles an additional string obfuscation technique that we call “tight strings”. Tight strings are a combination of stackstrings and encoded strings. Like stackstrings, a tight string consists of individual bytes that get constructed on the stack at runtime. And like an encoded string, these bytes are decoded before their usage. This decoding often happens in a tight loop, a short code sequence that repeats many times, hence the name tight strings. Kudos to Blaine Stancill for the tight name and the core algorithm.

We were motivated to add support for tight strings due to their prevalence in malware families such as KEGTAP, BEERBOT, and ANCHOR that we encountered dozens of times during 2021. We wonder if FLOSS’s success at decoding obfuscated strings encouraged the malware authors’ development of tight strings. It should also be noted that tight strings may be purposefully included in a binary via inlined string decoding routines, such as those provided by the ADVobfuscator project.

Figure 1 shows an example of a tight string in the malware sample with MD5 hash 9a16a348d3f4e7da3e8746667624115f and SHA256 hash 2065157b834e1116abdd5d67167c77c6348361e04a8085aa382909500f1bbe69.

After storing the obfuscated string bytes (0x6D, 0x28, 0x46, …) into consecutive stack offsets (var_630, var_62F, var_62E, …) in the top block, the malware executes the second block repeatedly to decode the data.

Figure 1: Example disassembly of a tight string decoding sequence
Figure 1: Example disassembly of a tight string decoding sequence

Figure 2 shows the decompilation of these two blocks. It’s debatable whether a visit to the dentist or reverse engineering this statically is more fun. Luckily, FLOSS can now save you from both! Like our existing algorithms, we emulate over identified program parts. FLOSS constructs the obfuscated data on a virtual stack and decodes the data by emulating the tight loop, extracting strings from the virtual stack once the loop ends.

Figure 2: Decompilation of the tight string decoding sequence
Figure 2: Decompilation of the tight string decoding sequence

In this example, FLOSS recovers the string InternetReadFile that the malware uses to dynamically resolve the corresponding Windows API function. Figure 3 shows FLOSS’ verbose output of the recovered string.

Figure 3: FLOSS tight string results excerpt
Figure 3: FLOSS tight string results excerpt

Additional improvements

Besides adding a new string deobfuscation algorithm, we’ve made the existing identification and emulation algorithms more reliable and performant. FLOSS results now include fewer false positive deobfuscated strings. This means that you’ll be distracted by junk output less often when running the new version of FLOSS. To facilitate this, we added library function recognition via FLIRT signatures and improved the string filtering and sanitization methods.

Faster emulation and shortcutting emulation of functions that don’t decode strings significantly boost FLOSS’ overall runtime. During testing we’ve noticed an average speed up of 47%! This means you can process malware more quickly, especially as part of a large-scale processing pipeline. Earlier versions of FLOSS only provided results after complete analysis. As shown in Figure 4 the updated tool now provides decoded strings as they are uncovered and indicates the analysis progress. We find this makes the tool feel more responsive: we get a good sense for if it’s working right away and tend to have more patience (or perhaps just an obsession watching progress bars complete).

Figure 4: FLOSS live results output as strings are decoded
Figure 4: FLOSS live results output as strings are decoded

Finally, we’ve added more API emulation hooks and updated the emulation engine as well as the underlying analysis framework. Combining all our improvements means FLOSS can now emulate more malware samples than before!

Improved FLOSS usage and results output

For FLOSS users, the new version significantly simplifies the command-line arguments. To extract all strings, you simply run FLOSS like before:

$ floss malware.exe

When you know you don’t care about a class of strings, such as if you want to ignore static strings and focus only on obfuscated strings, you can now use the --no argument, e.g., to not extract static strings:

$ floss malware.exe --no static

To focus on specific string type(s) you want to extract, use the --only option:

$ floss malware.exe --only tight decoded

Figure 5 shows an example of the new FLOSS results output. Prior FLOSS versions did not successfully decode any strings from this file.

Figure 5: FLOSS final summary results output

FLARE FLOSS RESULTS (version 2.0.0)

+------------------------+------------------------------------------------+

| file path              | 2065157b834e1116abdd5d67167c77c6348361e04a8... |

| extracted strings      |                                                |

|  static strings        | Disabled                                       |

|  stack strings         | Disabled                                       |

|  tight strings         | 55                                             |

|  decoded strings       | 53                                             |

+------------------------+------------------------------------------------+

 

----------------------------

| FLOSS TIGHT STRINGS (55) |

----------------------------

%d%02d%02d
bcrypt.dll
BCryptOpenAlgorithmProvider
BCryptImportKeyPair
BCryptVerifySignature
BCryptCloseAlgorithmProvider
ReadFile
kernel32.dll
GetTempPathW
kernel32.dll
~pkg%d%S
Date
HttpQueryInfoA
wininet.dll
Set-Cookie
.bazar
%i.%i.%i.%i
Host: %s
update: %s
XTag
InternetQueryDataAvailable
wininet.dll
InternetReadFile
CoInitialize
ole32.dll
CoInitializeSecurity
GetTempPathW
kernel32.dll
GetTempFileNameW
http://127.0.0.1/pics.html
[…]

------------------------------

| FLOSS DECODED STRINGS (53) |

------------------------------
CoInitialize
ole32.dll
CoInitializeSecurity
CoCreateInstance
CoTaskMemFree
HEAD
HttpQueryInfoA
Date
wininet.dll
bcrypt.dll
BCryptOpenAlgorithmProvider
BCryptImportKeyPair
BCryptVerifySignature
BCryptCloseAlgorithmProvider
kernel32.dll
ReadFile
%d%02d%02d
CoInitialize
ole32.dll
Font Service
CoInitializeSecurity
CoCreateInstance

[…]

Please consult the help via floss -h to see all available options.

FLOSS API and integration

We’ve migrated FLOSS from Python 2 to Python 3 (finally!). This brings FLOSS up to speed with most other modern Python projects and enables additional performance improvements.

If you use FLOSS as a module in a larger project, please be aware that many APIs have changed, and that you will likely need to update your tools accordingly. To ease the integration and interoperability with other tools, all FLOSS results can easily be exported to a JSON document via the -j argument. This way you can trivially run FLOSS as a subprocess from any programming language and programmatically manipulate the results.

Call to FLOSS and thank you

Thanks to the community for the continuous support, bug reports, and feature requests. Dozens of people have contributed to FLOSS over the years. We hope you find the updates useful and look forward to hearing from you at https://github.com/mandiant/flare-floss/issues. You can find all FLOSS releases at https://github.com/mandiant/flare-floss/releases and the source code at https://github.com/mandiant/flare-floss.