https://lecromee.github.io/posts/swing_vpn_ddosing_sites/ Greek geek POSTS Swing VPN app is a DDOS botnet June 4, 2023 tldr: Swing VPN is using its user base to DDOS sites using its users as a an attack botnet. new: Some people wrote me saying that the DDOS is not happening on ios devices. Just did a quick check and you guys are right. iOS app is using different way to do VPN and also does not do anything suspicious. I should appologize to you and to Appstore team for my lazy extrapolation without actually checking it. Unfortunately I don't have much time to fix the article right now, so please just ignore anything ios related below this line. Introduction It all started with a friend of mine complaining that his phone was doing a request to a specific app every few seconds. Initial assumption was that the phone was infected with a virus but a 2 minute investigation showed that all requests went from 'Swing VPN' app which were legitimately installed on the phone as VPN service. It was making requests to specific website that my friend never used and had specific data inside request payload indicating its intent send requests to an endpoint that would be heavily demanding on resources of that site. The site that was targeted on my friends phone and later on my phones was https://turkmenistanairlines.tm. Request was sent about every 10 seconds and was sent specifically to this search endpoint: https://turkmenistanairlines.tm/tm/flights/search?_token=J8SxUX2Qwzltw4LiHsRHTCtfthgBYxf4hyI8oNly&search_type=internal&departPort=TAZ&arrivalPort=CRZ&tripType=rt&departDate=4%2F22%2F2023&arrivalDate=5%2F4%2F2023&adult=1&child=0&infant=0&is_cship=on The specificity of this URL clearly indicates that this is not a mistake nor is it method to ping a site to check for errors with internet connection. Later in this document I will show and hopefully prove malicious intent of the creator of this application by inspecting how it all works and infrastructure behind it. Requests Let's start with examining requests and see what exactly is happening when we run Swing VPN on our phone. I am using a physical phone connected to a computer using a usb wire and a program named 'scrcpy' to mirror screen of my phone to the screen of my computer. This is done just simplify screenshot taking and is not required for the analysis. First let's start with simple inspection and verification that the request made to airlines website is done by the 'Swing VPN' app. For this I will use an android app pcapdroid to capture all requests by the apps and see who is responsible for which request. There is no need for additional plugins or apps to see the details about the requests as I will use different tools for those tasks. Current goal is to link each request with specific app. I want to mention that this phone have only standard android apps and swing. In this video pcapdroid has been just installed and I waited some time for google play to finish with all of its statics and other request so that they will be less non related requests in the log. Your browser does not support the video tag. From the video we can clearly see that this 'Swing VPN' app does some type of request to the site https://turkmenairlines.tm. From it we cannot clearly conclude that the app does something malicious but this is left for later analysis. For now the proof that the request are originated from the app that I am inspecting is good enough. Now we can safely proceed to a deeper dive into functionality of the app. --------------------------------------------------------------------- Your browser does not support the video tag. The next step is to figure out what exactly Swing VPN is trying to do by sending these requests. For these I will use mitmproxy to capture all data sent and see what the purpose of those requests. In this video 'Swing VPN' is just freshly installed from the Play Store and being monitored by mitmproxy. After app startup, language selection and acceptance of privacy policy the app starts to figure out 'real IP address' by doing a request to both google and bing with query "what+is+my+ip". My guess is that the app just parses the returned HTML and figures IP from those responses. These ip request needed, as we will see later, to figure out which config files to load. The app loads different configs and does different actions based on not only country or region of the user but also on the internet provider within the region. After the required config type is identified in this video the Swing VPN does a couple of requests to 2 different config files stored in personal google drive account of the app creator. The config files are requested from specific personal servers, a few github repositories or a couple google drive accounts. My guess is that config file location could be determined by daytime but I have not spent any time to verify that as it is not important. As soon as configs are retrieved the app connects to ad network to load ads. This concludes the app initialization process. After this app stores data into a local cache and proceeds to DDOS a site returned from the config. Android install base of Swing VPN And this is how the app behaves over time after being close. Hint it still tries to do it DDOS even though it is not being used. Requests over some time for Swing VPN From this log we can see that the app is requesting a specific endpoint of 'tm/flights/search'. Since flight search is quite intensive tasks that requires a lot of databases and server resources then it is clear that that the goal is to stress server out of resources so that normal users won't be able to acess it when needed. And even though 1 request per 10 seconds might seem that it does not doing DDOS the problem is in amount of install base. Currently in the beginning of June 2023 it has over 5 million install base on android and even if you split it by 10 it has a potention of 500k RPS. Which is quite impressive to be able to handle for a small site written probably in PHP. Sidenote: The app does not respect privacy Your browser does not support the video tag. While doing this little investigation I found out that the app does not care about privacy. It probably added the button 'I Accept the privacy policy' just to make appstore and playstore accept the app but in reality it is just a button that does not do anything. In the video above I installed a fresh version of Swing VPN from playstore and then instead of pressing 'I Accept the privacy policy' button I pressed which leads to 'Privacy Policy' screen. And while I was skimming though the policy the app already started sending my data to ad network. At the same time it was downloading configurations with information about which site to DDOS and started executing the DDOS routine while I as reading the 'Privacy Policy'. After I was done reading I just pressed back a couple time thus informing the app that I am not agreeing to the term but it is already late. The act of opening the app is enough for it start it's DDOS actions. The functionality of the configurations So we just went through outer look of how the app app does it actions related to DDOS'ing other sites. But I could have installed some other app in the background maybe with similar icon which did all the nasty stuff just to fool you. So now let's dive deeper inside the app and the actual configurations stored in the app which you can do yourself to verify that it is indeed the 'Swing VPN - Fast VPN Proxy' that is responsible for all this actions. Some general information about android apk: VERSION USED: swing-vpn-1-8-4.apk APK SIZE: 32.5 MiB INSTALL BASE ON PLAY STORE: 5+ million users LINK TO PLAY STORE: https://play.google.com/store/apps/details?id=com.switchvpn.app&hl=en_US ANDROID APP CREATOR: Limestone Software Solutions LAUNCH DATE: 2020-10-06 The app uses 2 custom native libraries to just obfuscate it's function and complicate the reverse engineering process. This files are libnativelib.so and libbony.so. We will use libnativelib.so as it will be enough to decrypt and deobfuscate the data. Configuration is downloaded from github, google drive or a custom host. In my research I checked only github and google drive since it was enough to check the hypothesis. Github Let's start with github. First of all there are at least 2 different github accounts used to store the configurations for the app. I cloned both the repositories just in case somebody needs the historical data if they are modified or deleted. It looks like both repositories are about 6 month old so it won't be something unexpected if new repositories are created soon. These repositories are: https://github.com/Javaidakhtar576/swinglite_new https://github.com/githubfunc/cocomo The general format of the message is some encoded string surrounded by curly braces. You could have seen one example of these in the second video. Here is how it looks like. Android install base of Swing VPN And here is a the text version of one of the configs requested during startup. {{{30766d755274445150474d656e6770435276786e6f15631c6056373b4f673c5935726c225378450752474d666d65704850707a6a6e48634f6355373d483b3a5631736e23527b425b54434b606a6a761955257f3a6f48624d6006303e4c6f3a5536716b715774475451444c616865754954767e3a6947664f6454306c4c693f5630756d25562f420754424e616d36724d507f7b3d6f46631a650132394c673a55367f6b7756784157554c4a34686b754e55747d6e6d13634b6051353f486e395334736e7556294257544248346837764255777e3b6b1667186656336c4c6e3a0232276f245328465751134c346c66744955277d6d6912644d6403306b4c3c3c0030746f75547a425554164a326862764c54257f366b4567496006363648683f5832276e7953284352554d48656865744855207c3d6a1166486407303a4e3b3d5630276f78577d4601514d4e616d62724d51727b3d6b47674e645530384c3a3a55362268795778435b554149676c6670185177783a6d13624d6051303a4d3f3c0030746f24567d410754174a376862754c55737f3e6b46674b6006363648683f5933226e77537d4754554d49306960754855227d3d6a1166486407303a4e6e3d5630766b735328460151444f636c65724351277e3d6b16661c6454323e4d6e3e0333746e775229475451444c616862751d54247d3c6911651b6456313d4d6a 3d0331736b735328460151464e616c6b724d54277f3a6a11671f6452323e48683f5232276f755328475a511349606961751955237d38681367486051356e486e380434736b22527b425b54434b606b63764a54247e3b6f48624d6006363c48683e5432276b76572b4254554d4866686a771955277c3b6813664e655130364c3d3d5834736a20527b475b50454e346c65761855717e3b6a49661c6454323848683f5232276f745328475a511348626860744e57717d69681466406451306c4c693d0032776f78577b430255434e616d36724d507e7b3d6f46631a640333384d69380536776b7657294354554448666860764d55707c3b6947664e6457343d4966390235716b735275460154434b616866774855227e3d69496748640433394e3a3b0536226a785574430155164861683175485571793b6c4462186153343d483b390230756f78577a420254114a326832764a55247e3b6915674e640333694c3c3a0434226a2057294354554448666860714f507f793b6c1562186004343d4d6f3c0330276f2556784301551749606864774d55227f3a6b44651b6455333d4c683a5436226a715328460551134d636c66711a51777c3b69126741645c313d4d683e0530726e73567c4200541648666832764e55277f6c6b1667186006363648683f5832276e7953284200554749666960744855777d3c6b1466496452306a486 e385534736b735275460155434a66686b764a55207e3d6b12674a6601336d4c6b3a5632276f245328475451444c616831751d54737d3c691466496452313b486e385534736f79577d430755474b606d36721951717f3b6b16674a645433694c3a3b0736226a7657794357544c4c3268677542552379366d13624d6051303a4c383d0730236f22527b475050454e616c6b724d54257f3a6b46671b645c323e4c6b3a5532276f245328475451444c616963751e55747c3d681466406553313f486e385534736b735275460154414a626864764a55277f696b45674c6006363648683e0332726e7757784301541049676865754a54777d38694362186106343d486e395334736e75577e425355434b676862774d51277a6e6f48631a6057373e4d6e3a5136756b71577e430651134d696c66714f5120793b694866406401303a4f6f3d0331736e77567c430156104a336964774b51277a6e6f48621f6006376948683a07367f6a2757744055554d49616962754854777f6e6947671b6557333f486e385534736a70527b465650454a656866761d55227c3c6b16661a655d33394d68385436776b765675405656114c616d37714f5074793b6d4262186455303e4c673d0333726f74577b430554424b616a6b761f54247e3b6811641c6701373e493b3e03337e6e77537d4754551748626830754856767d3c6813671c6456313d4e 3b3d5631726e75557f405357474e616d36724d507e7b3d6f46631a645d333d4c383a0235246a7056284250554d49616a31751d54767c3d6b4965416051356e486e380434736b22527b4206544349606862774d54237f3a6a48661d6006363648683f5935206a72522b4306564248656966754f55777e6c6846644f6506353c4e6e3e5735706f7756784150514649666b667748567e7c3d6e49654e6101323f4f6c380032276e7953284351554c4b606861744f54737d3c6813671b6757333f486e385534736a7054784250554448326a65764954777f6e6e16671f6501316a4c673a593620697752284105574c49646a30774956277f3f6c47664b645731394f683d5832276e74527b465650454a646863754c55227e3d6a13671b6506323f4f6a395832276f2453284657504c4d616860771e56747e3f6914654e6651306b4d68380532726e72552f405b574d4f306960751e50777a3f6b16621b610430384c6e3950357f6876567b425451134c346c66754a55717e3a691267186555303a4d6e3c0533716c71557a4601514d4e616d66734b57227e376840621a670430364e663b5236756a73557d415356424b606961751951237e3b694565496551306c493f3d5132726c77557a4006544c4a666a31724d51727b3d6b146718670133394d683b0736226b77562b405057414b636c667018517778386a16664a650631384 f6e3c5533776d77567c4153541148346d667749567e7c6e6a4267496107333b4c673f0535716973542e4157544048626b37754f5624783a6d13624d605130384c683e0530746e73577f420055454b606a60754f51277a6e6f48621f6701326d4c6c3a5137236a23542b400055144a616963771d55277f6e68476518665735374c6e3e0235716d23567f425055174f616861724255727d3c6a4866406006376948683a043676697657744254541748666966744e5177786c6d136418615133694d6f390234246b73567e425257444a666965774955227e3d6a4964186700373e493b3e0334276f77547e425554114c616c31714f55727d6a6a14661d655131394c693c0231726c7155794601514d4e616a65734d56717e3c6a11661c6006376948683a043676697657744254541748666966744e56757e396a1462186106343d4e6e3802337e6e725779435a55444e616c6b724d55207f376849671b6506323a4c6f3b03372469735578405651134d696c66774f50777e366814671c6403313f486e395334736f76562e410254424b616961764a54277e3c6914641c600636364868380333276923562b435154114c616c31714f54707c39691166406557303a486e385531256b73567c425454464a61686b764a55257b3d6e40631a6100373e48663e0337226b7557754357544c48666b62754854777c3a6947664e645d326a 4c6d3d5230746b735328460150454e346c65774a547e7f3b6b1266186407313a4c3c3b0336246a7057754155554748676861714f507f793b6d13624d6051313a4d6c3d0030706e75567c405b54454a356962774b51277a6e6f48631a6057373e4d6f3b5836256a7456754353574548336966744f55257c3b6c1662186106343d486e395334736e745779420754464b676862744255777e3d6a4867486506363e48683f5232276e77567c475a511349666966751a5476793b6c44674a6051303a4c3d3d0730736f22567c420750454f696c65734e51277b6c6f48674a6452336b4f6e3a5136736a705328460551134d626d64714f5120793b694366406500333b4c3c3d5230746b735328460151464f636c65724351277e3d6b1267496404313f4c3a3e0333746e775229475451444c616961744f55207f6e6947671b6557343d49663e5431256b73577c430154104e616d36724d55767e3b6a15661c65013636483c3e5737256b705628430655454866686a751d54767c3d6916664f645230364d6e3d5330236f24567c430250164b67686a721954257f6b6f4567416457336e4c6d3a5037256b76537e4255554d48626966754e552478366a41671a645c306d4c693d5135276d20532e4102551449666b30734d567f7e386a40674b6504323b496e385436776822562b405457404b676a67744955737c3d6946661f6651333 64d3f3d5935756f795729405351144a356a32764354777b396a49671b6403323e4c693a5035716b7156784256554d4d376836754354717d3c6813664f6452306c486a3d0030746e7156784301554349636831774d54257a6b6815651f6751373a4c6b3b0337276a25562c435755444b63686b744f5471786d6b146518670634394d683c0230236e71557d435355474a666d6a774d54257b396b15671b6500333d4d683b0534256a745675435350454d676c61704f5773783b6c13621d61513239496e380435736a72527f425b55454b6168377749557f7f6c6915671f6504333949673f0232226f77552c4652514d4d616a62704f5075783b6c14621c6452303b4d693d5331756a255378460554444a306837764355257a6b6e1163416452336b4c6c3a5936706b71527c4656511748336967764255767c3a694666486553356b4c3a3d5134736b22527b425754424b676830761955257b3d6e40631a665c31394f6e3e0337736e79567a4754544d49616831714f507f793b6946671a6557313f4d6f3855347e6b27577d430055454a68686a764a55707f366a4966186403336b4c6b3a5137276a7957784301554d49606c6a7449552379366811664c605c30394c3b3d5630766f79577d430250114b606862764e54277f3c6b426248675432384c3c3a5336226a20527c410550404b60693576485624783b6a16671f650630 6c4d683c5835756d2256294052554449616a30754b57247e3b6b1366186453333b4e68395137766a735275430754414a336d35754257227d6e6847621c6550303a4c6d3c0230726f79552f430755174b636862734255777f6a6a15671b6506336b4c6b3a5432236a7157744256551649616960764d55257c3b6811634c6757323e4f66395930706e73577b425355414a62686b754f55717e3d6a15624a6601303e4f3b3e0737256b7757784256564c49336964754850237c3b6811621c6457303a4d6c3d0731736e75547d425b55434a666d6a734b51227a3d6913621a610637394968380733276f7552284655511748626966744f55277c3f6916664d6657303e4d683d0335276a74527c470156414f676c62734d57237a3d6e11621a6101373a4c6b3a0537226a795675465b50164c656867751e55277d6e6911634c615334394c3c3d5130776f70562c430751134f636c61761f54247c696b49661d6453336e4d6a3f5536716a205328475a511348376861744955247d36691162186106343d4e6b3f0333756b7357744656554c4e616962774d55727b3d6e40631a645332384d6e3b5837246f24537e4702544c49666966751f55237d3c694066486550313b4c6d3d5130706f78577b425654174a326862774c51707e3b6b4763486504336a483c3a0736726a25572f4307544c49606c6b744e55707d386813661b64003 5694f3a3c00307e6f23567c4254511348696d30754c54767c3a6842621a6703323b4d3b3a5437256b725275415a55414a306967764f57247e3d6b14671a6455313b4c3f3d5832736c78572e420551434a306937741f50767f696944674b6552373a4d693a02367f6b77572b4307564749676936744d5570786d6947664f6557303a4d6e3d5130706f22527f420754424b636866774d54257c3f6b45661a6504366a4f6e385935746e7357294254541348336962754c55207e39694167186557356b4e6f3e0233226b77577d430154174b636b63771f547e7f3a6e47661a6504373a4c6e3a02377e6a7456284252574c4862696075485023783d6d1263186655353d496e390335736d77537b475a51454f606c61764e54277e3d6b436641640333694e6e3a5937256a70527c4653514d4d616a6270495170783b6b4863186153353d496f395930706f75577c425655434f376d66724955247f376b43674b6404366a496a3e0736776a20572c4357554649676d32704d51737d696814654e6450313c4c3f3d5631716a25562f425450454e346c65764255227e3b6b4267486404373e493b3e03342068705475475454454b376c31714f54707d386b4266486550313b486e385533256b7354744255554d4a33686b7643557f7b696e1663496100373c483d390436776a2057754302541449606c647743567179396c166341605d 353f4967390433766f78562c470551434f686c67771850237a3b6f43631c6603323c4d6a3a54362269725774435457434833696071425070783a6c49624f61503539486c395732256d79557d405756104e346c67764355777f6d6b16631c665c33394c693a5336716e25532e415555404961686b7519557079366c1663406152346a496c395135716b245379465a57444a626861764e54277f366f45621b6101363b483f3f0033236e77537d4754574548356937751d55207d6e6916624e6156346a496c390434206c765629425454434a356960774c517e7d6a6815631c6103363c483f3f5833756e75542f410256144d656d60711d51757f38681567416404303a4f6b3d0330736d235629430750114f666d64734851707a3c6e13631c605331374e3d390534736879537d4756554448336830754851757f3e6912661b64013069483c390432726f79577b425554134a666c31734e507f7a3e6f44621c6055363c483f3f58327e69765729435055164961683671425070783a6c49624f61503539486e395334736d25562f 435054174a34686b764e51717a3a6f44621c6000376d4f6d3a5136706a71577e425154104c636a6a7649517578386c15624f61533436486c3f0731716e715675420057404a666865741d55777e3b6f45621b6101363b483f3f0033236e755379410657404b676a32771a51207939694266486401303a486c3f5830746f72562b425550174e636a64761854277f696b47671b6054363d496b3f5932706f75537f465651464d636c64764e55747d3f69166718640334694969380535766b24537a470550454e346c65744255717e6e6b43674b6457333d483c3f0232706f75532e470757444833686a74485424786b6d156440645d303b4d6e3d5030236f755279475b51474e336c67744e547e7e3f6b46671b675c33394c68385336776b71537e465350104d646c6a704e507379396d46644a6600333b4e383f5334246b7156754253544c4a666c67744855227f3c6b4167486052373c4e693a5037276a23577c435351474d626d65704c512278396d40631d61563539496f395135706a70537f465a56134a356865761f55727f3a6f11641d6403333a4c6b3b0336776e235274465550144c326d67704b51777e6d6843674c}}} The decoding code is located in the native libs directory with the name libnativelib.so. I reverse engineered the decoding algorithm and wrote is the python code that does the reversing. You can download it here: decode.py In order to decode that message store it into a file, let's say 'data.txt' and just run that file on it like this: python decode.py data.txt The decoding string string will be put into stdout of the terminal and you if you want to save it to a file just redirect the output to the output file. For example: python decode.py data.txt > data.decoded.txt If we run this decoder on the encoded message provided above the output of it will be: { "adsMode": "Remote", "adsSingleIdMode": "1", "summaryAdLocal": "0", "timeLimitedMode": "1", "timeLimitedConnection": "0", "defaultTimeLimit": "5", "minTimeLimit": "3", "extendTimeSmall": "15", "extendTimeBig": "30", "report": "1", "fixedServer": "1", "repair": "0", "summary": "0", "adsTest": "0", "screenMirroring": "1", "hotspot": "1", "adsDisabledFirst": "0", "adsDisabledPeriod": "0", "drawerCodeItemEnabled": "0", "disconnectDialogEnabled": "0", "summaryScreenEnabled": "0", "reportScreenEnabled": "0", "youtubeChan": "", "telegramChan": "", "livechat": "https://demolivechat.com/", "email": "", "telegram": "", "whatsapp": "", "facebook": "", "instagram": "", "twitter": "", "tiktok": "", "fakeServerList": "1", "fakeServerListP": "1", "fakeServerListPP": "1", "fakeServerListPPS": "0", "fakeServerListVIP": "0", "fakeServerListGP": "0", "gdServers": "1Wg3kZfrbbZxNz3BX1faZ1UQwPR3I3sVC", "gdServersTP": "1AjsNBfyj5asMmagR2JDwKDYF9jdvTgMu", "gdServersPP": "142dHQVc_Bmt3Cs_AZ8wZ90e54TdXQCzr", "gdServersPPS": "14ExZ2TZLzkfLEZSum-RkXrl8nCVSGkeO", "gdServersVIP": "1QkzwRzVFeYoL1vPZxn5gm4_VPAxaZbX3", "gdServersGP": "1SxfivoSYgBwIiLyRD8bR0Kfjy2f-lCrw", "ghServers": "B2_s", "ghServersTP": "B2_sp", "ghServersPP": "B2_spp", "ghServersPPS": "B2_spps", "ghServersVIP": "B2_svip", "ghServersGP": "B2_sgp", "update": { "enabled": "0", "updateVersionName": "", "updateForcedCode": "", "updateAbout": "", "updateMirror1": "", "updateMirror2": "" }, "urls": { "enabled": "1", "minTime": "10", "maxTime": "10", "randCi": "1", "urlList": [ { "url": "https://turkmenistanairlines.tm/tm/flights/search?_token=J8SxUX2Qwzltw4LiHsRHTCtfthgBYxf4hyI8oNly&search_type=internal&departPort=TAZ&arrivalPort=CRZ&tripType=rt&departDate=4%2F22%2F2023&arrivalDate=5%2F4%2F2023&adult=1&child=0&infant=0&is_cship=on", "method": "GET" }, { "url": "https://turkmenistanairlines.tm/tm/flights/search?_token=J8SxUX2Qwzltw4LiHsRHTCtfthgBYxf4hyI8oNly&search_type=internal&departPort=TAZ&arrivalPort=CRZ&tripType=rt&departDate=4%2F22%2F2023&arrivalDate=5%2F4%2F2023&adult=1&child=0&infant=0&is_cship=on", "method": "GET" }, { "url": "https://turkmenistanairlines.tm/tm/flights/search?_token=J8SxUX2Qwzltw4LiHsRHTCtfthgBYxf4hyI8oNly&search_type=internal&departPort=TAZ&arrivalPort=CRZ&tripType=rt&departDate=4%2F22%2F2023&arrivalDate=5%2F4%2F2023&adult=1&child=0&infant=0&is_cship=on", "method": "GET" } ], "uaList": [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.116 Mobile Safari/537.36" ] } } If we scroll down to the 'urls' section we could easily find the link to the https://turkmenairlines.tm and the time required between requiests of 10 seconds. Which clearly lines up with our earlier observations. But there are quite a few files in the github repository and a lot of different configurations. Here are the files found in the repository: A1_c A1_spp A2_s A2_spps B1_sgp B1_svip B2_sp B3_c B3_spp GLOBAL_s GLOBAL_spps IRANMCI_sgp IRANMCI_svip IRANTELCOM_sp IRNCELL_c IRNCELL_spp RU_s RU_spps TEST_sgp TEST_svip A1_s A1_spps A2_sgp A2_svip B1_sp B2_c B2_spp B3_s B3_spps GLOBAL_sgp GLOBAL_svip IRANMCI_sp IRANTELCOM_c IRANTELCOM_spp IRNCELL_s IRNCELL_spps RU_sgp RU_svip TEST_sp backup A1_sgp A1_svip A2_sp B1_c B1_spp B2_s B2_spps B3_sgp B3_svip GLOBAL_sp IRANMCI_c IRANMCI_spp IRANTELCOM_s IRANTELCOM_spps IRNCELL_sgp IRNCELL_svip RU_sp TEST_c TEST_spp main A1_sp A2_c A2_spp B1_s B1_spps B2_sgp B2_svip B3_sp GLOBAL_c GLOBAL_spp IRANMCI_s IRANMCI_spps IRANTELCOM_sgp IRANTELCOM_svip IRNCELL_sp RU_c RU_spp TEST_s TEST_spps These filenames are constructed in specific order. First of all the a files has a prefix like A1, B1, ..., GLOBAL these is their way to split configurations into ISP related configurations. And here is how it is split: "B1" | "tm" | "State Company of Electro Communications Turkmentelecom" | "B2" | "tm" | "Telephone Network of Ashgabat CJSC;AGTS CDMA Mobile Department" | "B3" | "tm" | "Altyn Asyr CJSC" | "GLOBAL" | "default" | "" | "RU" | "ru" | "" | "IRANTELCOM" | "ir" | "" | "IRNCELL" | "ir" | "Iran Cell Service and Communication Company" | "A1" | "ae" | "" | "A2" | "ae" | "Emirates Integrated Telecommunications Company PJSC" | "IRANMCI" | "ir" | "Mobile Communication Company of Iran PLC" | with 'tm' -> Turkmenistan, 'ru' -> Russia, 'ir' -> Iran, 'ae' -> Unitaed Arab Emirates. We are interested in configurations that end with '_c' which is proably a way to identify 'configurations'. So if we walk over all the configuration files and collection all the urls the app is DDOS'ing then we will get a list of these urls: https://www.science.gov.tm/news/20230112news-2023-01-12/ https://www.science.gov.tm/organisations/classifier/reseach_institutes/ https://www.science.gov.tm/library/articles/article-asirow-25/ https://www.science.gov.tm/news/~Page34/ https://railway.gov.tm/ https://turkmenistanairlines.tm/tm/flights/search?_token=J8SxUX2Qwzltw4LiHsRHTCtfthgBYxf4hyI8oNly&search_type=internal&departPort=TAZ&arrivalPort=CRZ&tripType=rt&departDate=4%2F22%2F2023&arrivalDate=5%2F4%2F2023&adult=1&child=0&infant=0&is_cship=on https://www.science.gov.tm/news/~Page25/ https://www.science.gov.tm/news/~Page9/ https://www.science.gov.tm/news/~Page36/ https://www.science.gov.tm/sci_periodicals/ https://www.science.gov.tm/anounce/ https://www.science.gov.tm/projects/mietc1/ https://www.science.gov.tm/projects/APCICT1/ https://www.science.gov.tm/projects/caren/ https://www.science.gov.tm/events/ https://www.science.gov.tm/organisations/chemical_institute/ https://www.science.gov.tm/en/news/~Page11/ https://www.science.gov.tm/en/news/20220329news-2022-03-28-1/ https://www.science.gov.tm/en/news/20220310news-2022-03-09-1/ https://www.science.gov.tm/en/news/20220123news-2022-01-22-1/ https://www.science.gov.tm/news/20230112news-2023-01-12/ If we look in this list we can see already familiar link to turkmenistanairlines. But other urls are all look similar to each other and all end with '.gov.tm' which we probably can assume that this app is trying to attack some government sites of Turkmenistan. It is hard for me to imagine why would anybody do that but that is not what were are here for. My interest is in technical explorations. Configurations stored in the apk All those previous explorations could be easily removed and then there would be no way to prove that this app is actually doing that. So let's deep a bit more deeper and actually find evidence that is baked inside the apk and cryptographically signed. It turns out not that hard of a task. If you decompile the by unzipping it or with a tool like apktool, there would be a file at the location res/raw/rc_g.raw this file is also encrypted and could be decrypted with the 'decode.py' script but this files does not contain enclosing {{{ and }}} marks. So in order to decode that file we just need to add '-n' to end of our as second argument for 'decode.py' script. It is not the nicest solution but gets the job done for the this task: python decode.py cr_g.raw.txt -n So after you run this command you should get a file similar to this: { "configResources": [ { "type": "git", "purpose": "config", "url": "https://github.com/githubfunc/cocomo/blob/main/", "urlExt": "", "entry": "green" }, { "type": "git", "purpose": "config", "url": "https://github.com/javaidakhtar576/swinglite_new/blob/main/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://arpqpedacr.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://atrytgoi.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://bdefsr.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://cornchance.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://dreoapms.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://freekept.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://gquyidezfixp.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://haptpydligyh.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://hcvxm.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://hgvcp.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://jhgvu.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://mqurstd.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://mraznakgde.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://mwuth.com/", "urlExt": "", "entry": "main" }, { "type": "host", "purpose": "config", "url": "https://net-vm-games.com/", "urlExt": "", "entry": "main" }, { "type": "google", "purpose": "config", "url": "https://www.googleapis.com/drive/v3/files/", "urlExt": "?alt=media", "entry": "15_T7IYmov1A7Ar3jFe4SkZ4dKFpbomTf", "credentials": "..." }, { "type": "google", "purpose": "config", "url": "https://www.googleapis.com/drive/v3/files/", "urlExt": "?alt=media", "entry": "13R-GC8jtz4XB-xl_IQUeL8BiS32pXB03", "credentials": "..." }, { "type": "google", "purpose": "config", "url": "https://www.googleapis.com/drive/v3/files/", "urlExt": "?alt=media", "entry": "13B5sCioRZCGfBx13b9K2sRoo2XEEst0B", "credentials": "..." }, { "type": "google", "purpose": "pin", "url": "https://www.googleapis.com/drive/v3/files/", "urlExt": "?alt=media", "entry": "", "credentials": "..." } ] } I edited the output to remove 'credentials' value and replaced it with '...'. If you really want to get that data just run the script yourself on the file and you could get the original value. So if you look at the last output you will find familiar github and goodle drive links that the app used to download additional settings. That settings files apart from being a real settings configuration is used as C&C (Command and Control) mechanism to secretly deliver targets for the Swing VPN to do DDOS attacks. Related files * swinglite_new.zip - latest commit for swinglite_new repository. * cocomo.zip - latest commit for cocomo repository * google_drive.zip - decrypted config files from one of the google drive accounts * decode.py - file to decrypt encrypted config stored in github and google drive * swing-vpn-1-8-4.apk - a Swing VPN apk file version 1.8.4, downloaded from play store I provided only single commit for github repositories as they are quite large (over 100 MB). If for some reason you need whole repository you can contact me by email and I will send a link to download whole repositories with history of more than half a year of commits. Conclusion From the provided evidence I think it is undeniable that creator of the app has malicious intent in denying services to regular people by DDOS'ing those services. They use different techniques to obfuscate and hide their malicious actions in order to try to go undetected. That is main reason for why they send the request every few seconds as with the amount of install base they have it is enough to bring the services down but still not fire security alarms in appstore and playstore security teams. But if for some reason they decide that the pressure on the service is not enough they could easily send command to their apps and force the to storm those services with useless requests. Apart from malicious actions toward some innocent services I think it is really dishonest behavior toward regular users that download the app from stores. They do not respect their privacy and use users phones as a botnet. The reason it is very shady is that they already collect money from users by either show them ads or by selling monthly VIP services. It is from pure greed they also want use innocent users phones as a tool in their criminal actions. I have to give props for Swing VPN teams creativity to bypass security measure of Apple appstore and Google PlayStore but it is sad that Apple/Google security systems does not have some automated ways to detect these types of actions. If you have any questions about my methods, if you found any factual errors (don't send me typography corrections) or if I missed something important please contact me at via email. My login is lecromee and my mail hosting of choice is proton.me. I hope if you read these type of posts then you know how basic concatenation works. (c) Greek geek 2023