Security false positives

Introduction

We occasionally receive false reports of security vulnerabilities in Dart and Flutter applications, generated by tools that were built for other kinds of applications (for example, those written with Java or C++). This document provides information on reports that we believe are incorrect and explains why the concerns are misplaced.

Common concerns

Shared objects should use fortified functions

The shared object does not have any fortified functions. Fortified functions provides buffer overflow checks against glibc’s commons insecure functions like strcpy, gets etc. Use the compiler option -D_FORTIFY_SOURCE=2 to fortify functions.

When this refers to compiled Dart code (such as the libapp.so file in Flutter applications), this advice is misguided because Dart code doesn’t directly invoke libc functions; all Dart code goes through the Dart standard library.

(In general, MobSF gets false positives here because it checks for any use of functions with a _chk suffix, but since Dart doesn’t use these functions at all, it doesn’t have calls with or without the suffix, and therefore MobSF treats the code as containing non-fortified calls.)

Shared objects should use RELRO

no RELRO found for libapp.so binaries

Dart doesn’t use the normal Procedure Linkage Table (PLT) or Global Offsets Table (GOT) mechanisms at all, so the Relocation Read-Only (RELRO) technique doesn’t really make much sense for Dart.

Dart’s equivalent of GOT is the pool pointer, which unlike GOT, is located in a randomized location and is therefore much harder to exploit.

In principle, you can create vulnerable code when using Dart FFI, but normal use of Dart FFI wouldn’t be prone to these issues either, assuming it’s used with C code that itself uses RELRO appropriately.

Shared objects should use stack canary values

no canary are found for libapp.so binaries

This shared object does not have a stack canary value added to the stack. Stack canaries are used to detect and prevent exploits from overwriting return address. Use the option -fstack-protector-all to enable stack canaries.

Dart doesn’t generate stack canaries because, unlike C++, Dart doesn’t have stack-allocated arrays (the primary source of stack smashing in C/C++).

When writing pure Dart (without using dart:ffi), you already have much stronger isolation guarantees than any C++ mitigation can provide, simply because pure Dart code is a managed language where things like buffer overruns don’t exist.

In principle, you can create vulnerable code when using Dart FFI, but normal use of Dart FFI would not be prone to these issues either, assuming it’s used with C code that itself uses stack canary values appropriately.

Code should avoid using the _sscanf, _strlen, and _fopen APIs

The binary may contain the following insecure API(s) _sscanf , _strlen , _fopen.

The tools that report these issues tend to be overly-simplistic in their scans; for example, finding custom functions with these names and assuming they refer to the standard library functions. Many of Flutter’s third-party dependencies have functions with similar names that trip these checks. It’s possible that some occurrences are valid concerns, but it’s impossible to tell from the output of these tools due to the sheer number of false positives.

Code should use calloc (instead of _malloc) for memory allocations

The binary may use _malloc function instead of calloc.

Memory allocation is a nuanced topic, where trade-offs have to be made between performance and resilience to vulnerabilities. Merely using malloc is not automatically indicative of a security vulnerability. While we welcome concrete reports (see below) for cases where using calloc would be preferable, in practice it would be inappropriate to uniformly replace all malloc calls with calloc.

The iOS binary has a Runpath Search Path (@rpath) set

The binary has Runpath Search Path (@rpath) set. In certain cases an attacker can abuse this feature to run arbitrary executable for code execution and privilege escalation. Remove the compiler option -rpath to remove @rpath.

When the app is being built, Runpath Search Path refers to the paths the linker searches to find dynamic libraries (dylibs) used by the app. By default, iOS apps have this set to @executable_path/Frameworks, which means the linker should search for dylibs in the Frameworks directory relative to the app binary inside the app bundle. The Flutter.framework engine, like most embedded frameworks or dylibs, is correctly copied into this directory. When the app runs, it loads the library binary.

Flutter apps use the default iOS build setting (LD_RUNPATH_SEARCH_PATHS=@executable_path/Frameworks).

Vulnerabilities involving @rpath don’t apply in mobile settings, as attackers don’t have access to the file system and can’t arbitrarily swap out these frameworks. Even if an attacker somehow could swap out the framework with a malicious one, the app would crash on launch due to codesigning violations.

CBC with PKCS5/PKCS7 padding vulnerability

We have received vague reports that there is a “CBC with PKCS5/PKCS7 padding vulnerability” in some Flutter packages.

As far as we can tell, this is triggered by the HLS implementation in ExoPlayer (the com.google.android.exoplayer2.source.hls.Aes128DataSource class). HLS is Apple’s streaming format, which defines the type of encryption that must be used for DRM; this isn’t a vulnerability, as DRM doesn’t protect the user’s machine or data but instead merely provides obfuscation to limit the user’s ability to fully use their software and hardware.

Apps can read and write to external storage

App can read/write to External Storage. Any App can read data written to External Storage.

As with data from any untrusted source, you should perform input validation when handling data from external storage. We strongly recommend that you not store executables or class files on external storage prior to dynamic loading. If your app does retrieve executable files from external storage, the files should be signed and cryptographically verified prior to dynamic loading.

We have received reports that some vulnerability scanning tools interpret the ability for image picker plugins to read and write to external storage as a threat.

Reading images from local storage is the purpose of these plugins; this is not a vulnerability.

Apps delete data using file.delete()

When you delete a file using file. delete, only the reference to the file is removed from the file system table. The file still exists on disk until other data overwrites it, leaving it vulnerable to recovery.

Some vulnerability scanning tools interpret the deletion of temporary files after a camera plugin records data from the device’s camera as a security vulnerability. Because the video is recorded by the user and is stored on the user’s hardware, there is no actual risk.

Obsolete concerns

This section contains valid messages that might be seen with older versions of Dart and Flutter, but should no longer be seen with more recent versions. If you see these messages with old versions of Dart or Flutter, upgrade to the latest stable version. If you see these with the current stable version, please report them (see the section at the end of this document).

The stack should have its NX bit set

The shared object does not have NX bit set. NX bit offer protection against exploitation of memory corruption vulnerabilities by marking memory page as non-executable. Use option --noexecstack or -z noexecstack to mark stack as non executable.

(The message from MobSF is misleading; it’s looking for whether the stack is marked as non-executable, not the shared object.)

In older versions of Dart and Flutter there was a bug where the ELF generator didn’t emit the gnustack segment with the ~X permission, but this is now fixed.

Reporting real concerns

While automated vulnerability scanning tools report false positives such as the examples above, we can’t rule out that there are real issues that deserve closer attention. Should you find an issue that you believe is a legitimate security vulnerability, we would greatly appreciate if you would report it: