{
	"id": "3890768c-05b1-48b4-b11e-48d4a9a7efa1",
	"created_at": "2026-04-06T00:10:03.701142Z",
	"updated_at": "2026-04-10T13:12:18.522247Z",
	"deleted_at": null,
	"sha1_hash": "9ad7dd7e564a298c4334e0b8f958bb8f37070c33",
	"title": "The Domain Generation Algorithms of SharkBot",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 77378,
	"plain_text": "The Domain Generation Algorithms of SharkBot\r\nArchived: 2026-04-02 11:27:01 UTC\r\nSharkBot is an Android banking malware that steals credentials and banking details 1234. Apart from one or two\r\nhard-coded domains, it relies on a fallback domain generation algorithm (DGA) for communication — which is\r\nrather rare for Android malware. The DGA was changed several times during the further development of\r\nSharkBot. This blogpost shows four versions of this DGA, and how they differ.\r\nBoth jadx and jd-gui were able to decompile almost the entire SharkBot code to great precision, but failed when it\r\ncame to the DGA function. I therefore used dex2jar to translate the compiled Android application code to a jar\r\nfile, then ran the unmaintained jad decompiler on the class that contains the DGA. You can find\r\nreimplementations of all four DGAs in Python on my GitHub repo.\r\nVersion 0.0.0\r\nThe first sample that I found has the version set to 0.0.0 :\r\nMD5\r\n0356f17f28778da7c97dc8b661c0aeb0\r\nSHA1\r\n2c4828f926471ec4f3522fc28dd4d8fdec692c35\r\nSHA256\r\n76b4ee2da4e39677038ea033f25652fb02ed9e84ab829ce212fa1dfbc941df2c\r\nSize\r\n3 MB (4116240 Bytes)\r\nCompile Timestamp\r\nn/a\r\nLinks\r\nVirusTotal\r\nPackage Name\r\ncom.btfezxwhygk2dw0gaj.eguafyojiqcw7a\r\nDetections\r\nVirustotal: 21/73 as of 2022-04-25 06:01:16 - Android.BankBot.904.origin (DrWeb),\r\nAndroid.Sharkbot.GEN46543 (CAT-QuickHeal), Android.PUA.DebugKey (Trustlook), Trojan (\r\n0058a5511 ) (K7GW), AndroidOS/SpyAgent.F.gen!Eldorado (Cyren), AppRisk:Generisk\r\n(SymantecMobileInsight), a variant of Android/Spy.Agent.BWR (ESET-NOD32), HEUR:Trojan-Banker.AndroidOS.Sharkbot.d (Kaspersky), a.privacy.BankSharkBot (Tencent),\r\nTrojan.Agent.Android.340641 (Zillya), Artemis!Trojan (McAfee-GW-Edition), Andr/Banker-HAQ\r\n(Sophos), Android:Evo-gen [Trj] (Avast-Mobile), ANDROID/SpyAgent.FKIX.Gen (Avira), HEUR:Trojan-Banker.AndroidOS.Sharkbot.d (ZoneAlarm), Android.Trojan.SharkBot.B (BitDefenderFalx),\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 1 of 7\n\nTrojan/Android.Agent.1071402 (AhnLab-V3), Artemis!0356F17F2877 (McAfee), Trojan-Spy.AndroidOS.SharkBot (Ikarus), Android/Agent.BWR!tr (Fortinet)\r\nAfter some mild deobfuscation of the strings and renaming the variables, the DGA looks as follows:\r\nprivate String dga()\r\n{\r\n StringBuilder urls = new StringBuilder();\r\n Calendar calendar = Calendar.getInstance();\r\n String tlds[] = \".top,.xyz,.cc,.info,.com,.ru,.info,.net\".split(\",\");\r\n int nrTlds = tlds.length;\r\n int i = 0;\r\n while(i \u003c nrTlds)\r\n {\r\n String tld = tlds[i];\r\n try\r\n {\r\n urls.append(\",http://\");\r\n StringBuilder seed = new StringBuilder();\r\n urls.append(\r\n Base64.encodeToString(\r\n seed.append(\r\n calendar.get(Calendar.WEEK_OF_YEAR)\r\n )\r\n .append(\"pojBI9LHGFdfgegjjsJ99hvVGHVOjhksdf\")\r\n .toString()\r\n .getBytes()\r\n , Base64.NO_WRAP)\r\n .substring(0, 19)\r\n )\r\n .append(tld);\r\n }\r\n catch(Exception exception) { }\r\n i++;\r\n }\r\n return urls.toString().toLowerCase();\r\n}\r\nThe DGA performs these steps to generate domains:\r\n1. Take the current week number (e.g. 12)\r\n2. Append the hardcoded string pojBI9LHGFdfgegjjsJ99hvVGHVOjhksdf\r\n3. Base64 encode the result\r\n4. Take the first 19 characters\r\n5. Append a TLD from the list .top , .xyz , .cc , .info , .com , .ru , .info , .net in order.\r\n6. Convert the domain to lower-case\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 2 of 7\n\nThis results in domains like these:\r\nmnbvakjjouxir0zkzmd.xyz\r\nmnbvakjjouxir0zkzmd.live\r\nmnbvakjjouxir0zkzmd.com\r\nmnbvakjjouxir0zkzmd.store\r\nmnbvakjjouxir0zkzmd.info\r\nmnbvakjjouxir0zkzmd.top\r\nmnbvakjjouxir0zkzmd.net\r\nThe second level domain (SLD) is the same for all generated domains at a given time. And since the time-dependent portion only affects the first two characters (when the week number is ≤ 9), or the first three characters\r\n(when the week number is ≥ 10), all domains will follow either of the two following pattern:\r\n[n-o][awgq]bvakjjouxir0zkzmd\\.(top|xyz|cc|info|com|ru|info|net)\r\n[nm][dtjz][acegikmquy]wb2pcstlmsedgzgz\\.(top|xyz|cc|info|com|ru|info|net)\r\nVersion 1.63.3\r\nThe second version of the DGA appears in this sample:\r\nMD5\r\n48ad4e0478e4d742f51848604d06130e\r\nSHA1\r\na9ca49ef2201707b7bcf57798fc67e69d238c900\r\nSHA256\r\nc14f413d8ed944ba7e4364e6b17585019fd622feeb4b53f7002a742d7389e08a\r\nSize\r\n7 MB (7345268 Bytes)\r\nCompile Timestamp\r\n1970-02-21 06:07:33 UTC\r\nLinks\r\nVirusTotal\r\nFilename\r\n(VirusTotal)\r\nDetections\r\nVirustotal: 8/72 as of 2022-05-20 04:22:02 - Android.BankBot.958.origin (DrWeb), a variant of\r\nAndroid/Spy.Agent.BWR (ESET-NOD32), HEUR:Trojan-Banker.AndroidOS.Sharkbot.d (Kaspersky),\r\nANDROID/Bankbot.FKWN.Gen (Avira), HEUR:Trojan-Banker.AndroidOS.Sharkbot.d (ZoneAlarm),\r\nTrojan/Android.Agent.1111318 (AhnLab-V3)\r\nThis sample features a small change to the DGA: The seed is now also based on the current year, which prevents\r\nthe DGA from repeating after a year.\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 3 of 7\n\nprivate String dga()\r\n{\r\n StringBuilder urls = new StringBuilder();\r\n Calendar calendar = Calendar.getInstance();\r\n String tlds[] = \".top,.xyz,.cc,.info,.com,.ru,.info,.net\".split(\",\");\r\n int nrTlds = tlds.length;\r\n int i = 0;\r\n while(i \u003c nrTlds)\r\n {\r\n String tld = tlds[i];\r\n try\r\n {\r\n urls.append(\",http://\");\r\n StringBuilder seed = new StringBuilder();\r\n urls.append(\r\n Base64.encodeToString(\r\n seed.append(\r\n calendar.get(Calendar.WEEK_OF_YEAR) +\r\n calendar.get(Calendar.YEAR)\r\n )\r\n .append(\"pojBI9LHGFdfgegjjsJ99hvVGHVOjhksdf\")\r\n .toString()\r\n .getBytes()\r\n , Base64.NO_WRAP)\r\n .substring(0, 19)\r\n )\r\n .append(tld);\r\n }\r\n catch(Exception exception) { }\r\n i++;\r\n }\r\n return urls.toString().toLowerCase();\r\n}\r\nBy adding the year, the time dependent component always exactly affects the first three characters of the domains,\r\nwhich therefore always follow this pattern:\r\nmja[xyz0123][mno][hnx3]bvakjjouxir0z.\\.(top|xyz|cc|info|com|ru|net)\r\nVersion 2.1\r\nThe next version of the DGA is used in this sample:\r\nMD5\r\n92011ba743860567b85f46aedf360661\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 4 of 7\n\nSHA1\r\n506df2fd2e638ab614eeb4cbc416bd46fdbf6a19\r\nSHA256\r\n70b244a03a0eacd00cc52ea8863af2c459eb8dc2e6bf5887e657b401e0477485\r\nSize\r\n4 MB (4252173 Bytes)\r\nLinks\r\nVirusTotal\r\nDetections\r\nVirustotal: 19/73 as of 2022-05-18 07:59:42 - Trojan.AndroidOS.Sharkbot.C!c (Lionic),\r\nTrojanBanker:Android/Sharkbot.bc773aca (Alibaba), Spyware ( 00592d791 ) (K7GW), AppRisk:Generisk\r\n(SymantecMobileInsight), a variant of Android/Spy.Agent.BWR (ESET-NOD32), HEUR:Trojan-Banker.AndroidOS.Sharkbot.e (Kaspersky), Android.Trojan-spy.Agent.Hphh (Tencent),\r\nAndroid.BankBot.977.origin (DrWeb), Artemis!Trojan (McAfee-GW-Edition), Andr/ShrkBot-B (Sophos),\r\nTrojan.AndroidOS.Agent (Ikarus), ANDROID/Bankbot.FKWN.Gen (Avira),\r\nTrojanSpy:AndroidOS/Sharkbot.B!MTB (Microsoft), Android:Evo-gen [Trj] (Avast-Mobile),\r\nAndroid.Trojan.Banker.ZP (BitDefenderFalx), Artemis!92011BA74386 (McAfee),\r\nAndroid/Agent.BWR!tr.spy (Fortinet)\r\nFor version 2, SharkBot made some larger changes:\r\nSwitch to md5 instead of base64. This assures that the second level domains (SLDs) will no longer have a\r\nsubstantial portion that never changes.\r\nAdding the TLD to the seed, which leads to different SLDs for different TLDs.\r\nCutting the length of the SLD from 19 to 16 letters.\r\nUsing differnt TLDs\r\nprivate String dga() {\r\n StringBuilder urls = new StringBuilder();\r\n Calendar calendar = Calendar.getInstance();\r\n String tlds[] = \".xyz,.live,.com,.store,.info,.top,.net\".split(\",\");\r\n int nrTlds = tlds.length;\r\n int i = 0;\r\n while(i \u003c nrTlds)\r\n {\r\n String tld = tlds[i];\r\n try\r\n {\r\n urls.append(\",http://\");\r\n StringBuilder seed = new StringBuilder();\r\n urls.append(\r\n calc_md5(\r\n seed.insert(0, tld)\r\n .append(calendar.get(Calendar.WEEK_OF_YEAR))\r\n .toString()\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 5 of 7\n\n)\r\n .append(Calendar.YEAR)\r\n .toString()\r\n .substring(0,16)\r\n )\r\n .append(tld);\r\n }\r\n catch(Exception exception) { }\r\n i++;\r\n }\r\n return urls.toString().toLowerCase();\r\nStrangely, the year is concatenated to the MD5 string instead of also being hashed. Because the resulting string is\r\ncut to the first 16 characters, this means that the year once more has no influence on the generated domains.\r\nVersion 2.8\r\nThe most recent sample that I investigated is version 2.8:\r\nMD5\r\n2dfe83d4d7c0b5e0cfc0537efdbbbb01\r\nSHA1\r\nf1a820369f02a696e8bcaecee464eeaef0847c44\r\nSHA256\r\ndae193b7cac6d048dcc37916c92b0d2b11c56aa3f45e9995c16417e3b0587404\r\nSize\r\n7 MB (7735296 Bytes)\r\nCompile Timestamp\r\n1970-02-24 12:54:57 UTC\r\nLinks\r\nVirusTotal\r\nFilename\r\n(VirusTotal)\r\nDetections\r\nVirustotal: 4/73 as of 2022-05-12 15:24:09 - a variant of Android/Spy.Agent.CDR (ESET-NOD32),\r\nHEUR:Trojan-Banker.AndroidOS.Sharkbot.e (Kaspersky), ANDROID/Bankbot.FKWN.Gen (Avira)\r\nIn version 2.8 the DGA was fixed. Now the year is part of the input to MD5, so domains will be different for\r\ndistinct years.\r\nprivate String dga() {\r\n StringBuilder urls = new StringBuilder();\r\n Calendar calendar = Calendar.getInstance();\r\n String tlds[] = \".xyz,.live,.com,.store,.info,.top,.net\".split(\",\");\r\n int nrTlds = tlds.length;\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 6 of 7\n\nint i = 0;\r\n while(i \u003c nrTlds)\r\n {\r\n String tld = tlds[i];\r\n try\r\n {\r\n urls.append(\",http://\");\r\n StringBuilder seed = new StringBuilder();\r\n urls.append(\r\n calc_md5(\r\n seed.insert(0, tld)\r\n .append(calendar.get(Calendar.WEEK_OF_YEAR))\r\n .append(\"\")\r\n .append(calendar.get(Calendar.YEAR))\r\n .toString()\r\n )\r\n .substring(0,16)\r\n )\r\n .append(tld);\r\n }\r\n catch(Exception exception) { }\r\n i++;\r\n }\r\n return urls.toString().toLowerCase();\r\nSource: https://bin.re/blog/the-dgas-of-sharkbot/\r\nhttps://bin.re/blog/the-dgas-of-sharkbot/\r\nPage 7 of 7",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://bin.re/blog/the-dgas-of-sharkbot/"
	],
	"report_names": [
		"the-dgas-of-sharkbot"
	],
	"threat_actors": [
		{
			"id": "3fff98c9-ad02-401d-9d4b-f78b5b634f31",
			"created_at": "2023-01-06T13:46:38.376868Z",
			"updated_at": "2026-04-10T02:00:02.949077Z",
			"deleted_at": null,
			"main_name": "Cleaver",
			"aliases": [
				"G0003",
				"Operation Cleaver",
				"Op Cleaver",
				"Tarh Andishan",
				"Alibaba",
				"TG-2889",
				"Cobalt Gypsy"
			],
			"source_name": "MISPGALAXY:Cleaver",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775434203,
	"ts_updated_at": 1775826738,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/9ad7dd7e564a298c4334e0b8f958bb8f37070c33.pdf",
		"text": "https://archive.orkl.eu/9ad7dd7e564a298c4334e0b8f958bb8f37070c33.txt",
		"img": "https://archive.orkl.eu/9ad7dd7e564a298c4334e0b8f958bb8f37070c33.jpg"
	}
}