{
	"id": "46697087-0b51-42ea-acb1-0aaa56576cb6",
	"created_at": "2026-04-06T02:13:02.912485Z",
	"updated_at": "2026-04-10T03:21:14.691308Z",
	"deleted_at": null,
	"sha1_hash": "2f1e7753e001748e0e6a6a411ca777133c379ef3",
	"title": "Fortinet Reverses Flutter-based Android Malware “Fluhorse” | FortiGuard Labs",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1440770,
	"plain_text": "Fortinet Reverses Flutter-based Android Malware “Fluhorse” |\r\nFortiGuard Labs\r\nBy Axelle Apvrille\r\nPublished: 2023-06-21 · Archived: 2026-04-06 01:29:49 UTC\r\nAndroid/Fluhorse is a recently discovered malware family that emerged in May 2023. What sets this malware\r\napart is its utilization of Flutter, an open-source SDK (software development kit) renowned among developers for\r\nits ability to build applications compatible with Android, iOS, Linux, and Windows platforms using a single\r\ncodebase. While previous instances of threat actors using Flutter for malware exist, such as MoneyMonger, they\r\nactually used Flutter for its cross-platform UI elements without carrying the actual malicious payload. So, despite\r\nFlutter application reversing being notoriously difficult, MoneyMonger can actually be quite easily reversed with\r\nthe usual Android reversing techniques.\r\nThe Android/Fluhorse family represents a significant shift as it incorporates the malicious components directly\r\nwithin the Flutter code.\r\nThis blog post covers insights on two notable aspects:\r\n1.        The Fluhorse campaign of May had basic obfuscation and no packing. In June, however, the sample we\r\nanalyzed was packed. This is evidence that malware authors are moving to the next step of maturity.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 1 of 15\n\nFigure 1: Android/Fluhorse sample of May 2023 isn't packed, and directly uses Flutter\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 2 of 15\n\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 3 of 15\n\nFigure 2: Android/Fluhorse sample of June 2023 is packed and conceals its\r\n2.        Reverse engineering technique. Reversing Flutter applications is widely considered a challenging\r\nendeavor. Many researchers treat it as a black box, only analyzing those components that can be observed from the\r\noutside. Some employ dynamic instrumentation tools, like re-Flutter and re-flutter-demo, which come with their\r\nown complexities and risks associated with running malware in controlled environments. But for our analysis, we\r\nsuccessfully managed to fully reverse-engineer the Fluhorse malware in a static manner, without the need for\r\ndynamic execution.\r\nNote that Fortinet customers are fully protected against this family of malware. These samples are detected as\r\nAndroid/Fluhorse.A!tr.spy or Android/Packed.57103!tr, and all related URLs mentioned in this blog are detected\r\nand blocked.\r\nQuick description of Android/Fluhorse\r\nThe malware we analyzed posed as a legitimate app for an electronic toll system used in Southern Asia. It lures\r\nthe victim into entering his/her credentials, steals them, and also steals 2FA (two-factor authentication) codes by\r\nlistening to incoming SMS and forwarding them to a website controlled by the attackers.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 4 of 15\n\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 5 of 15\n\nFigure 3: Android/Fluhorse poses as an electronic toll collection application\r\nVictims typically install the malware via e-mail phishing campaigns, with previous  research claiming that\r\nFluhorse has been downloaded over 100,000 times.\r\nThe current version we analyzed (SHA256:\r\n2c05efa757744cb01346fe6b39e9ef8ea2582d27481a441eb885c5c4dcd2b65b) was first seen on June 11, 2023. It is\r\ndistributed from the malicious URL hxxps://fasd1[.]oss-ap-southeast-1.aliyuncs.com/ETC.apk. FortiGuard’s\r\ntelemetry shows it has been being accessed from Asia since June 12.\r\nPacker\r\nThe sample is packed, concealing the encrypted payload within the wrapping application’s Dalvik executable,\r\nclasses.dex. The packer adds a 4-byte integer at the end of the DEX that includes (1) the size of the encrypted\r\npayload and, just before that, (2) the encrypted payload.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 6 of 15\n\nFigure 4: The encrypted payload is hidden with classes.dex itself\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 7 of 15\n\nDecryption is performed at the native level (to harden reverse engineering) using OpenSSL’s EVP\r\ncryptographic API. The encryption algorithm is AES-128-CBC, and its implementation uses the same hard-coded\r\nstring for the key and initialization vector (IV). From a cryptographic point of view, this is a bad idea as the IV is\r\nnot meant to be confidential.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 8 of 15\n\nFigure 5: Native library libapksadfsalkwes.so decrypts the malicious payload using AES 128\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 9 of 15\n\nThe decrypted payload is a ZIP file that contains another DEX. The payload is then installed (the packer’s\r\nimplementation includes Android sources for MultiDex support). The “main” of the malware is found in the\r\npayload (com.dsfdgfd.sdfsdf.MainActivity). It simply loads the Flutter application, as in the unpacked version of\r\nMay 2023.\r\nFigure 6: Contents of the decrypted malicious payload\r\nOur in-depth static analysis of libapp.so revealed everything we needed to know, including the supposed directory\r\nstructure of the malicious source code. The malicious Dart package is named sms_fluter and has the following\r\nfiles:\r\nsms_flutter/main.dart. This file implements the main application. We can see below that the functionalities\r\nare basic.\r\nsms_flutter/api/login.dart. This file implements communications with the remote attacker’s website. Class\r\nLoginApi implements a method postSms.\r\nsms_flutter/views/webifrview.dart. This file implements several classes: WebIfrViewState and WebIfrView,\r\nwhich basically handle the UI, but also request necessary permissions to listen to incoming SMS messages.\r\nThe malware uses the Dart Telephony package. This package is non-malicious and its code is open-source\r\nand documented. In particular, the package offers the possibility of listening to incoming SMS in the\r\nbackground. The malware author closely followed the documentation and created the following code (code is my\r\nmanual re-construction from assembly).\r\nonBackgroundMessage(SmsMessage message) async {\r\n    // malicious tasks\r\n}\r\nvoid main() {\r\n  runApp(MyApp());\r\n}\r\nThe corresponding assembly lines are shown below. For the reverse engineer, finding the address of a given Dart\r\nfunction is difficult. Several researchers use reFlutter, or similar techniques where the application is instrumented\r\nand then run, causing each address to be dumped. Unfortunately, for malware analysts, this technique has two\r\nmajor drawbacks besides its complex setup: first, you only get the addresses of the functions you visit.\r\nSecond, you instrument and run a malware, which should be avoided.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 10 of 15\n\nFigure 7: Fluhorse’s main\r\nTo obtain the addresses I was looking for, I used JEB, a reverse-engineering tool that offers some limited (but at\r\nleast existing) support for Flutter. In particular, it finds the addresses of each referenced method and is usually able\r\nto correctly map the name in the assembly code. When it fails (more frequently on ARM32 and ARM64), I fall\r\nback to reversing the x86_64 library. Fortunately, the x86_64 version was provided in this Fluhorse sample,\r\nprobably by error, because the rest of the implementation (for example, the packing) is not implemented for\r\nx86_64. If that doesn’t work, then I manually get the address from JEB’s code information and re-base it with the\r\nrelocation base for zero-based objects.\r\nWe can see below that the malware listens to incoming SMS. First, it registers for change notifications on SMS\r\nand then calls listenIncomingSms in the Telephony package.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 11 of 15\n\nFigure 8: Dart Assembly code to listen to incoming SMS\r\nThe malware then posts the incoming SMS message to a remote website in an asynchronous task.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 12 of 15\n\nFigure 9: The malware builds the URL of the remote website to contact: hxxp://pmm122.com/addcontent3…\r\nLater, the assembly adds the intercepted SMS body in argument “c4” of the URL.\r\nReverse engineering the code reveals several specificities to Dart assembly. In Dart, all constants, literals, etc, are\r\nstored in an Object Pool, which is like an index table. Later, all access to strings is done indirectly: the code asks\r\nfor access to a given index. Dart assembly dedicates a custom register R15 for x86_64 to access the Object Pool.\r\nThe illustrated example above accesses R15+8FB7, which corresponds to index 0x8FB7 // 8 = 4598. JEB shows\r\nthe Object Pool indexes for us and lets us map the assembly line by fetching the domain name string.\r\nFigure 10: JEB shows Object Pool indexes\r\nThe SMS body is inserted in the URL as argument “c4” (e.g., hxxp://pmm122[.]com/addcontent?c4=hello).\r\nThe incoming phone number is not provided because the Telephony package does not provide this feature. In\r\nthe following code of the (non-malicious) Telephony package, see that only the SMS body is retrieved and\r\nprovided to the callback.\r\n      try {\r\n        await handlerFunction(SmsMessage.fromMap(\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 13 of 15\n\ncall.arguments['message'], INCOMING_SMS_COLUMNS));\r\n      } catch (e) {\r\nLater, Fluhorse’s code performs the HTTP POST of the SMS message and reads the response. This is handled\r\nby Dart’s standard HTTP package.\r\nFigure 11: Assembly lines where the malware actually posts the HTTP request and reads the response. This is how\r\nAndroid/Fluhorse steals 2FA codes sent by SMS.\r\nConclusion\r\nReversing Flutter applications statically is a breakthrough for anti-virus researchers, as, unfortunately, more\r\nmalicious Flutter apps are expected to be released in the future.\r\nBuilding expertise in dealing with these samples and improving tools is necessary. This blog post provides results\r\nfor Android/Fluhorse. More detailed explanations have been submitted to conferences in Q4 2023, where I hope to\r\nbe selected to explain this live. ;) Stay tuned!\r\nFortinet Protections\r\nFortinet customers are fully protected against this family of malware.\r\nSamples in this blog are detected by the FortiGuard AV engine as:\r\nAndroid/Fluhorse.A!tr.spy\r\nAndroid/Packed.57103!tr.\r\nThe FortiGuard AntiVirus service is supported by FortiGate, FortiMail, FortiClient, and FortiEDR. Fortinet EPP\r\ncustomers running current AntiVirus updates are also protected.\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 14 of 15\n\nFortinet Webfiltering blocks all URLs identified in this blog.\r\nIf you believe this or any other cybersecurity threat has impacted your organization, please contact our Global\r\nFortiGuard Incident Response Team.\r\nIOCs\r\nThe samples mentioned in this blog post are (sha256 hash):\r\n2c05efa757744cb01346fe6b39e9ef8ea2582d27481a441eb885c5c4dcd2b65b\r\nOther similar samples:\r\ne8cdf809a5655124fa9347e7a90f071bed74907d3098737fd1184148ab475e39\r\n6e7293564e7e2e051d42168b068535a7963974cdd6437a3242230b9593dc7f04\r\n7cee6677790c493dddf16ff610a174e6536208f8853816cd0d71fc6bde56e93c\r\n91f0a27ae5ca77930c21b19f33479e7abcb10dfcf2a92b690ccddea01434fe84\r\n7dff0f7987f956c948847ea3659730408e35e4513b6adcd92d60ba48a93f62f1\r\n6f0b3733f91a6af56bf5bc789b808475cb556f2d360131ef6a9082b98dfd0139\r\n0a577ee60ca676e49add6f266a1ee8ba5434290fa8954cc35f87546046008388\r\n25fee29a8cb3e6b71771897e34a58cf9c7c0be4805acabb36be886e93de03f62\r\n663033dce1688186d6111c8637dd3bc79483bdb8fc1b2ad4d5ead030f79f84b7\r\n6dbde61a3aa372e8af7aa049dd466a2892bbe0d1229866cb2ba46c8f61648a57\r\n94bb98d9955947f9e7c502961e4b2a7724289e80b566035c14ac9fa6cf36df1c\r\n852314984cbea056a782520654f84c828588b6a0163bdeb8f8d5016b05c205f9\r\nc55feac16e7ca084f47a899281b566faf41b5666376353efeb9010fe5d23b526\r\n0a106c851a267fb8590be1f033e995bfc559ffaf2be050b3f12f599e0c8c021c\r\n32c427581a0368b66dd50b381772fb0d6dab30d8316f4e4f0d0373d453091cd0\r\n7909593a310b245a1a92a78469be341b0849e6f1076af30f8266b1c5a861ead1\r\nc25d533487499204771fac87787d38df91f0971b693dffa9b17fa0d92c80bfac\r\n5af2ec81d09ecbf8c26a8887d96c948b3c61667b7ffc488fbd67239ea9ac2cd6\r\n5481348e4751a494bc76ab4908071124f12624369401b717c7766e7d0645754d\r\nSource: https://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nhttps://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse\r\nPage 15 of 15",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.fortinet.com/blog/threat-research/fortinet-reverses-flutter-based-android-malware-fluhorse"
	],
	"report_names": [
		"fortinet-reverses-flutter-based-android-malware-fluhorse"
	],
	"threat_actors": [],
	"ts_created_at": 1775441582,
	"ts_updated_at": 1775791274,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/2f1e7753e001748e0e6a6a411ca777133c379ef3.pdf",
		"text": "https://archive.orkl.eu/2f1e7753e001748e0e6a6a411ca777133c379ef3.txt",
		"img": "https://archive.orkl.eu/2f1e7753e001748e0e6a6a411ca777133c379ef3.jpg"
	}
}