CLI tool
mandiant/stringsifter avatar
mandiant/stringsifter

StringSifter: ranking strings output for malware triage

A machine learning tool that ranks strings based on their relevance for malware analysis.

762 stars126 forksPythonApache-2.0

At a glance

What is it?
StringSifter is a Python tool from Mandiant that scores extracted strings by their relevance to malware analysis, using a learning-to-rank model trained on EMBER-derived samples. It is a triage filter for analysts who already know how to run strings, not a replacement for it.
Who is it for?
Adopt StringSifter if your workflow already produces large strings dumps from binaries, memory captures, or sandbox runs and you need a first-pass ordering before manual review. Do not adopt it if you need to retrain the model on your own corpus, since the README states that neither labeled data nor training code is currently available, or if you cannot run Python 3.9 or newer.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 54 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The triage problem StringSifter addresses

A strings dump from a real malware binary is mostly noise. Compiler artifacts, library boilerplate, and unrelated format strings dominate the output, and the analyst has to find the handful of entries that hint at C2 addresses, persistence paths, or API usage. Reading the whole list is slow, and truncating it by length or frequency throws away the entries that matter. StringSifter takes the full list and returns it ordered by a relevance score, so the analyst can start at the top. The README frames the tool as ranking strings "based on their relevance for malware analysis", and the intended user is a reverse engineer or malware analyst who already extracts strings and wants a cheaper way to decide what to read first. It is not a detector and it does not classify a sample as malicious; it only reorders text that another tool produced.

How the ranking model was built and what it consumes

The Discussion section is the only place the README describes the model. It states that this version was trained on strings output from sampled malware binaries associated with the first EMBER dataset, that ordinal labels were generated through weak supervision, and that supervised learning uses Gradient Boosted Decision Trees with a learning-to-rank objective, implemented with LightGBM. That is the whole published description. The README also states plainly that neither labeled data nor training code is currently available, with a note that the approach may be reconsidered in future releases. The practical consequence is that the model is a fixed artifact shipped with the package: you can run it, you cannot inspect its training set, and you cannot fine-tune it on your own samples. The tool accepts arbitrary string lists, not just output from its own extractor, which is why the README suggests applying it to FLOSS output, live memory dumps, and sandbox runs. Input is line-oriented text; output is the same strings with an optional score, either to standard output or to files in batch mode.

Installing and running flarestrings and rank_strings

The README requires Python 3.9 or newer. Installation from PyPI is a single command:

pip install stringsifter

That installs two runnable scripts, flarestrings and rank_strings. The canonical pipeline is:

flarestrings <my_sample> | rank_strings

flarestrings is described as mimicking features of GNU binutils strings, and it exists so that output is predictable across platforms. It accepts -n or --min-len to set the minimum string length, defaulting to 4; flarestrings -n 8 <my_sample> prints only strings of length 8 or greater. rank_strings takes a positional input_strings argument plus four options: --scores or -s to include rank scores, --limit or -l to cap the number of returned strings, --min-score or -m to drop anything below a threshold, and --batch or -b to point at a folder of strings outputs. Without --batch, ranked strings go to standard output; with --batch, each result is written to <input_file>.ranked_strings. For source development the README uses poetry: poetry install --with dev, then poetry run tests -v for the unit tests. A Docker path also exists: build with docker build -t stringsifter -f docker/Dockerfile ., then either pass flarestrings or rank_strings as the container argument, or start a shell with docker run -v <my_malware>:/samples -it stringsifter and run the pipeline inside.

FLOSS output and the Python 2 to Python 3 boundary

The README explicitly positions StringSifter for use on FLOSS output, noting that FLOSS reveals encoded, packed, or manually constructed strings that plain strings misses. The example command pipes floss -q into rank_strings, where -q suppresses headers so only extracted strings reach the ranker. Two caveats are spelled out. First, FLOSS requires Python 2 while StringSifter requires Python 3, so at least one side of the pipe must reference a virtual environment by relative path, or you use the standalone FLOSS executable, which does not depend on a Python interpreter. Second, the README warns that system strings implementations differ. On Linux, extracting both narrow and wide strings requires two invocations, strings <my_sample> > strs.txt followed by strings -el <my_sample> >> strs.txt. On macOS, some BSD strings versions do not support wide strings and the -a flag may be disabled by default, which can silently drop informative strings; the README recommends installing GNU Binutils via Homebrew or MacPorts. Windows ships no strings at all, so Sysinternals, Cygwin, or Malcode Analyst Pack is needed. This is the strongest argument for using flarestrings: it removes the platform-dependent extraction step from the equation.

Where the fixed model becomes a limitation

The ranking model is trained on one corpus and shipped as-is. If your samples come from a different family, a different compiler toolchain, or a different language than the EMBER-derived training set, the ordering may not reflect what you consider relevant, and there is no supported way to correct it, because the README states the training code and labels are not available. The weak supervision used to generate ordinal labels also means the ground truth behind the scores is approximate by construction, though the README does not quantify how noisy those labels are. A second limitation is the score itself: rank_strings returns an ordering and optional numeric scores, but the README gives no guidance on what a given score means or what threshold is sensible, so --min-score requires local calibration. Third, the tool operates on text only. It cannot see imports, sections, or control flow, so a sample whose malicious intent is not expressed in printable strings gains nothing from ranking. Finally, the pipeline is line-oriented, which means strings containing newlines or unusual encodings can be split or lost before ranking, depending on the extractor used upstream.

Compared with running plain strings and reading the output

The obvious alternative is GNU binutils strings piped through grep, sort, or a length filter. That approach is deterministic, transparent, and requires no model: you decide the rules and can see exactly why a line survived. StringSifter replaces those hand-written heuristics with a learned ordering, which is better when your rules are hard to articulate and worse when they are easy to articulate. A grep for http:// or a filter for strings longer than 20 characters is cheap, explainable, and reproducible across teams; a LightGBM ranking model is none of those things from the analyst's seat, because the feature set and weights are not published in the README. The Docker packaging is the other meaningful difference: plain strings is a local binary with platform quirks, while StringSifter ships a container that carries its own extraction and ranking steps. If your environment already standardises on a container image, that consistency may matter more than the model quality. If your environment forbids pulling model artifacts or requires every decision to be auditable, plain strings plus explicit filters is the safer choice.

Maintenance, licence, and what to check before adopting

The repository is Apache-2.0 and is not archived; the last push recorded is 2026-07-24, and no releases were retrieved from the material provided, so there is no release history to inspect here. The README notes that the model may be retrained in future releases, which means the ranking behaviour could change between versions without a documented migration path. Pin the version you validate against if you depend on stable ordering. Apache-2.0 permits commercial and internal use and requires preservation of notices; it also includes a patent grant and an explicit disclaimer of warranty, but this is not legal advice and you should read the licence text yourself if you plan to redistribute the package or bundle it into a product. On cost: the tool is a pip install with two scripts and a Dockerfile, so the operational surface is small. The real cost is validation. Because the model cannot be retrained on your data, every new sample class is a potential regression you have to catch by hand, and there is no published metric in the README to tell you what baseline accuracy to expect.

Editorial conclusion

Adopt StringSifter if your workflow already produces large strings dumps from binaries, memory captures, or sandbox runs and you need a first-pass ordering before manual review. Do not adopt it if you need to retrain the model on your own corpus, since the README states that neither labeled data nor training code is currently available, or if you cannot run Python 3.9 or newer. Before relying on it, verify the ranking quality on a sample set you understand, confirm that flarestrings output matches what your existing strings pipeline produces, and check the Apache-2.0 licence text if you plan to redistribute the model or the scripts.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. mandiant/stringsifter on GitHub
  4. README
Community notes

Community notes