Reverse Engineering "Free VPN" Applications

Reverse Engineering "Free VPN" Applications

October 27, 2025·fyx(me)
fyx(me)

While cleaning up old notes, I came across this tweeter post I had saved about “Free VPN” and how they are malicious:

https://x.com/UK_Daniel_Card/status/1869808826180133222

Free VPNs have again made rounds on Twitter and Reddit, its been know for more than 10 years that these VPNs basically sell your data, use your traffic and/or show you ads to pay for the free service. If you’re not paying, you’re the product.

Reddit technology post Hola VPN botnet

The article also mentions that the internet traffic harvested from its users was resold via a website called Luminati (yes for real… and they still use this domain as we’ll see later).

I thought it would be funny to look under the hood of one of these VPN services and see how they work. What better one to pick then the first one showcased in the twitter post above: BrightVPN

Reversing BrightVPN

Since the post was released, BrightVPN seems to have done a redesign of their website and to their credit, made it look very professional (but no less malicious):

BrightVPN Website

At least they tell you they are going to use your internet connection.

Static analysis of the BrightVPN Android Application

They offer a number of applications to use their VPN service, we’ll start looking at the Android Application to begin as it’s easy to extract source code and look for quick wins.

To download their Android Application, you need to scan a QR code to a 3rd party Android App Store. They’re not even available on the Google Play Store which already says a lot…

QR Code url: hxxps[://]qr2[.]mobi/BVPN_APK

This App store has to specify that its application come “with no viruses”. The fact that they have to specify this is hilarious in itself:

FREE WITH NO VIRUSES

From this marketplace, we can download Bright VPN directly onto our machine:

We now have an apk and can start doing some quick static analysis. We use apktool to unpack the apk (ie. the glorified zip archive) and retrieve its content:

$ apktool d --no-src bright-vpn-1-1.apk 
I: Using Apktool 2.7.0-dirty on bright-vpn-1-1.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/lo/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Copying raw classes.dex file...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
I: Copying META-INF/services directory

$ ls -al
total 10728
drwxrwxr-x   9 lo lo      240 Oct 27 14:20 .
drwxrwxr-x   3 lo lo       80 Oct 27 14:20 ..
-rw-rw-r--   1 lo lo     6050 Oct 27 14:20 AndroidManifest.xml
-rw-rw-r--   1 lo lo      565 Oct 27 14:20 apktool.yml
drwxrwxr-x   2 lo lo       60 Oct 27 14:20 assets
-rw-rw-r--   1 lo lo 10972348 Oct 27 14:20 classes.dex
drwxrwxr-x   8 lo lo      180 Oct 27 14:20 kotlin
drwxrwxr-x   6 lo lo      120 Oct 27 14:20 lib
drwxrwxr-x   3 lo lo       60 Oct 27 14:20 META-INF
drwxrwxr-x   3 lo lo       80 Oct 27 14:20 original
drwxrwxr-x 174 lo lo     3480 Oct 27 14:20 res
drwxrwxr-x   3 lo lo       80 Oct 27 14:20 unknown

The first thing that peaked my interest was this assets/dns_proxy_servers.json which appeared to contain a number of DNS entries:

$ cat assets/dns_proxy_servers.json | jq
[
  {
    "ip": "168.205.94.32",
    "country": "AR",
    "priority": 1,
    "location": "Argentina"
  },
  {
    "ip": "139.84.196.176",
    "country": "AU",
    "priority": 2,
    "location": "Australia"
  },
  
  [...]
  
  {
    "ip": "164.90.148.10",
    "country": "US",
    "priority": 4,
    "location": "United States"
  }
]

If we pick the first DNS server entry (168.205.94.32) and try to request DNS records for google.com, something interesting happens:

$ dig +short @168.205.94.32 google.com
168.205.94.32

The DNS proxy appears to be returning its own IP for google.com which is already weird. In contrast, this is what my local DNS server returns and what Google’s own DNS server (8.8.8.8) returns, which is nowhere close to the above:

$ dig +short google.com 
142.250.69.78

$ dig +short @8.8.8.8 google.com
142.250.69.78

Proxying DNS requests is not uncommon in VPNs as to prevent DNS leakage, however in this case they appear to be hijacking the records completely and always return the same IP address (ie. the IP of the proxy itself). This forces all traffic through the Proxies they have setup, probably to intercept and man-in-the-middle outgoing traffic.

Looking at some of the other files, we come across the res/values/strings.xml which contains a number of useless strings but also hints at something more interesting:

$ cat res/values/strings.xml | grep http

<string name="dns_list_url">https://hola.org/dns_proxy_servers</string>
<string name="dns_servers_url">https://hola.org/dns_proxy_servers</string>

[...]

<string name="my_ip_url">https://client.brightvpn.com/api/myip</string>

The file contains URLs for hola.org, which is also a very well know Free VPN which sells your data and traffic to the highest bidder. But this is probably just a coincidence.

If we try to query the URLs, the response appears very similar to the assets/dns_proxy_servers.json file we saw earlier:

$ curl https://hola.org/dns_proxy_servers | jq
[
  {
    "ip": "168.205.94.32",
    "country": "AR",
    "priority": 1,
    "location": "Argentina"
  },
  {
    "ip": "139.84.196.176",
    "country": "AU",
    "priority": 2,
    "location": "Australia"
  },
 
[...]

When we compare them, we can see they’re basically the same except for two (2) additional proxies in France and Japan:

$ cat ./assets/dns_proxy_servers.json | jq -r '.[].ip' | sort -u > a.out

$ curl https://hola.org/dns_proxy_servers | jq -r '.[].ip' | sort -u > b.out

$ diff a.out b.out
11d10
< 167.179.97.151
28d26
< 95.179.216.213

$ curl https://hola.org/dns_proxy_servers | jq '.[] | select(.ip == "167.179.97.151" or .ip == "95.179.216.213")'
{
  "ip": "95.179.216.213",
  "country": "FR",
  "priority": 3,
  "location": "France"
}
{
  "ip": "167.179.97.151",
  "country": "JP",
  "priority": 0,
  "location": "Japan"
}

Once again, this is probably just a coincidence… There’s no way BrigthVPN would be a wrapper for Hola VPN right?

Note: I looked at the decompiled dex files and the Java code gives off “My first Android project” kinda vibes. You can see the main service (MyVpnService) in appendix. // TODO

Dynamic analysis of the BrightVPN Android Application

Instead of speculating on what it might do, how about we run the application and observe.

A quick note on the testing setup I used

I decided to deploy a new Android Emulator (VM), I used BlissOS(an android VM) as it comes pre-installed with KernelSU (kernel root) which basically means we can load arbitrary modules (similar to Magisk) which would otherwise require us to root the machine. I used KernelSU to load AlwaysTrustUserCerts as this makes it much easier to install trusted certificates which is harder to do manually in newer android versions. You can use this nice script if you want to deploy a new BlissOS VM quickly and launch it with Qemu: https://github.com/maximilionus/android-qemu-launcher.

Alternatively, you can launch it with the following qemu command to launch your VM:

qemu-system-x86_64 \
  -enable-kvm \
  -M q35 \
  -m 4096 \
  -smp 9 \
  -cpu host \
  -drive file=./drives/android-image.qcow2.img,if=virtio \
  -usb \
  -device virtio-tablet \
  -device virtio-keyboard \
  -device qemu-xhci,id=xhci \
  -device virtio-vga-gl \
  -display sdl,gl=on \
  -machine vmport=off \
  -net nic,model=virtio-net-pci \
  -net user,hostfwd=tcp::4444-:5555 \
  -name "Android VM" \
  -audio pa,model=ac97

We start our VM and install the BrightVPN apk we downloaded previously:

# installing brightVPN
> adb install bright-vpn-1-1.apk
Performing Streamed Install
Success

We’re smarter than Google Play Protect obviously so we just dis-reguard their popup telling us the app is harmful and install it anyways:

We also setup our mitm proxy proxy to catch the HTTP requests from the app. I’m using mitmproxy instead of Burpsuite since I don’t plan on modifying requests, and I also love their TUI but that’s another story.

> adb reverse tcp:8080 tcp:8080
8080

> adb shell settings put global http_proxy 127.0.0.1:8080    

We start the app and try to connect to an arbitrary server they have listed:

We can see it make requests to the hola.org endpoint we say earlier as well as their own client.brightvpn.com/api/myip which basically returns your current IP (ie. testing if you’re routed or not):

And unfortunately this is pretty much where it stops for this application… Pressing the VPN button did nothing except send error reports to some website (which we’ll see again later):

I also couldn’t start the VPN itself, regardless of whether I was forcing the proxy through mitmproxy or not. The application was simply not working… I thought maybe it checks tries to fingerprint the device and see if its running in an emulator but saw no reference to that in the decompiled java code. While writing this post, I had another quick look and it turns out the IP checking endpoint no longer works? So maybe they just killed the application entirely… Who knows…

Who’s hiding behind BrightVPN?

A little disappointed that it didn’t work properly but such is life. I can only hypothesis how amazing it was and how unrelated to Hola VPN it is… Except if we can maybe find more proof linking the two together?

Funnily enough, BrightVPN’s Chrome Extension has amazing reviews and they keep on boasting about their App Certification:

If we have a look at their beautiful certification (which is definitely not a marketing gimmick), we can view all the applications certified by the same vendor… Oh, and what do we have here? Hola VPN is also certified by that same vendor and the Company is the same as the Bright VPN company, namely Bright Data

I’m not one to point fingers but all in all, this is the best conclusion I can come up with:

It was Hola VPN all along!

Moving on to reviewing Hola VPN

A quick look into the Hola VPN Android Application

Now that we know what to look for, I’ll spare you the details and speedrun the reversing part and go straight to dynamic analysis.

We install Hola VPN and start our proxy setup again to catch the traffic:

> adb install Hola_VPN_vARM7A_1.184.486.apk
Performing Streamed Install
Success

Identifying the full VPN setup workflow proved to be a little harder than expected since the application sets up a new interface which restarts the adb server and requires us to reset our proxy setup, however we can see:

  • Random IPs its trying to reach directly
  • Some IP tests its doing using a website of its own (brdtest.com and brdtnet.com) as well as a public IP check api64.ipify.org API
  • An error reporting host (perr.hola.org) with the exact same endpoint format and paremeters as the one found in the BrightVPN application (Another coincidence?)

We can also perform a quick tcpdump when the VPN is running and we see it connect to another seemingly random AWS IP address:

We can try to connect to the EC2 IP in a browser and get a certificate error issue, which reveals the certificate was issued for *.luminatinet.com:

luminatinet sounds very familiar! Oh yeah… We just did a full circle to the article released 11 years ago where they mention Hola VPN is selling traffic via a company named Luminati.

Nothing fishy, its just good business.

I was able to catch a few more endpoints while testing but why even bother reversing an Android Application and fighting with re-routing and what not when you can simply install their browser extension and route the traffic directly to Burp.

A quick look at the Hola VPN Google Chrome Extension

It is well known that every good VPN also provides a chrome extension alongside it:

So installed the extension, started the proxy, and looked at what we caught. We find the usual bunch, and endpoints related to what appears to be the proxies used by the application:

At this point I wanted to try and reverse the full connection flow and attempt to see if I could connect to a proxy directly with custom code (the proxies are non-standard so you won’t be able to send a simple HTTP CONNECT to it). And then I remembered, there’s probably someone who’s already tried to this…

A quick search revealed there was a Github repository which basically used Hola VPN proxies to route traffic: https://github.com/Snawoot/hola-proxy without relying on the application (and without having to give your traffic away). So I rushed to download and execute this random person’s code without checking if it was malicious or not because who does that anyways and it worked.

The tool sets up a local HTTP proxy which routes traffic through the Hola VPN proxy. We can start the local proxy as such:

> ./bin/hola-proxy -country AU -bind-address "127.0.0.1:8888"
MAIN    : 2025/10/26 21:39:06 main.go:247: INFO     hola-proxy client version  is starting...
MAIN    : 2025/10/26 21:39:06 main.go:347: INFO     Attempting action "get latest version of Chrome browser", attempt #1...
MAIN    : 2025/10/26 21:39:07 main.go:258: INFO     latest Chrome version is "142.0.7444.52"
MAIN    : 2025/10/26 21:39:07 main.go:263: INFO     discovered latest Chrome User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
MAIN    : 2025/10/26 21:39:07 main.go:350: INFO     Action "get latest version of Chrome browser" succeeded on attempt #1
MAIN    : 2025/10/26 21:39:07 main.go:347: INFO     Attempting action "get latest version of browser extension", attempt #1...
MAIN    : 2025/10/26 21:39:07 main.go:283: INFO     discovered latest browser extension version: 1.247.39
MAIN    : 2025/10/26 21:39:07 main.go:350: INFO     Action "get latest version of browser extension" succeeded on attempt #1
MAIN    : 2025/10/26 21:39:07 main.go:292: WARNING  Detected latest extension version: "1.247.39". Pass -ext-ver parameter to skip resolve and speedup startup
MAIN    : 2025/10/26 21:39:07 main.go:299: INFO     Constructing fallback DNS upstream...
MAIN    : 2025/10/26 21:39:07 main.go:347: INFO     Attempting action "run credentials service", attempt #1...
MAIN    : 2025/10/26 21:39:07 main.go:350: INFO     Action "run credentials service" succeeded on attempt #1
MAIN    : 2025/10/26 21:39:07 main.go:325: INFO     Endpoint: https://zagent2715.hola.org:22225
MAIN    : 2025/10/26 21:39:07 main.go:326: INFO     Starting proxy server...
MAIN    : 2025/10/26 21:39:07 main.go:328: INFO     Init complete.
PROXY   : 2025/10/26 21:39:33 handler.go:104: INFO     Request: 127.0.0.1:35536 HTTP/1.1 GET http://ip-api.com/
PROXY   : 2025/10/26 21:39:34 handler.go:95: INFO     127.0.0.1:35536 GET http://ip-api.com/ 200 OK

From there we can check that the routing work properly by checking our IP which shows we’re routing properly through a DigitalOcean server in New Jersey:

~ > curl -x http://localhost:8888 ip-api.com
{
  "status"       : "success",
  "continent"    : "North America",
  "continentCode": "NA",
  "country"      : "United States",
  "countryCode"  : "US",
  "region"       : "NJ",
  "regionName"   : "New Jersey",
  "city"         : "North Bergen",
  "district"     : "",
  "zip"          : "07047",
  "lat"          : 40.7964,
  "lon"          : -74.0203,
  "timezone"     : "America/New_York",
  "offset"       : -14400,
  "currency"     : "USD",
  "isp"          : "DigitalOcean, LLC",
  "org"          : "DigitalOcean, LLC",
  "as"           : "AS14061 DigitalOcean, LLC",
  "asname"       : "DIGITALOCEAN-ASN",
  "mobile"       : false,
  "proxy"        : true,
  "hosting"      : true,
  "query"        : "137.184.194.230"
}

I’ll leave it as an exercise for the reader to see how it connects to the network and authenticates.

Receiving the Ban Hammer from Hola VPN

The hola-proxy tool introduced above boasts the ability to forward traffic directly via residential peers (basically demonstrating what the Article said):

However, while testing it my traffic still appeared to be routed via VPS servers hosted on Digital Ocean and Vultr. From what I saw in the app itself, there might be some per domain routing applied too for example routing via peers when requesting netflix.com, etc. Or it may be that you need to have a premium account to be able to route through the network of peers. I’m not going to investigate further now but may look into it in the future.

Anyways, while I was playing around trying to route via the peer-to-peer network, I hit their rate limits and got IP banned:

> ./bin/hola-proxy -country FR -bind-address "127.0.0.1:8888"
MAIN    : 2025/10/26 22:10:40 main.go:247: INFO     hola-proxy client version  is starting...
MAIN    : 2025/10/26 22:10:40 main.go:347: INFO     Attempting action "get latest version of Chrome browser", attempt #1...
MAIN    : 2025/10/26 22:10:41 main.go:258: INFO     latest Chrome version is "142.0.7444.52"
MAIN    : 2025/10/26 22:10:41 main.go:263: INFO     discovered latest Chrome User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
MAIN    : 2025/10/26 22:10:41 main.go:350: INFO     Action "get latest version of Chrome browser" succeeded on attempt #1
MAIN    : 2025/10/26 22:10:41 main.go:347: INFO     Attempting action "get latest version of browser extension", attempt #1...
MAIN    : 2025/10/26 22:10:41 main.go:283: INFO     discovered latest browser extension version: 1.247.39
MAIN    : 2025/10/26 22:10:41 main.go:350: INFO     Action "get latest version of browser extension" succeeded on attempt #1
MAIN    : 2025/10/26 22:10:41 main.go:292: WARNING  Detected latest extension version: "1.247.39". Pass -ext-ver parameter to skip resolve and speedup startup
MAIN    : 2025/10/26 22:10:41 main.go:299: INFO     Constructing fallback DNS upstream...
MAIN    : 2025/10/26 22:10:41 main.go:347: INFO     Attempting action "run credentials service", attempt #1...
CRED    : 2025/10/26 22:10:41 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:43 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:43 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:43 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:43 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:43 credservice.go:32: ERROR    Configuration bootstrap error: Post "https://client.hola.org/client_cgi/background_init?uuid=386655b1cf604f778cd5ebd04ff81042": can't prepare underlying connection for TLS session: dial tcp 159.223.106.80:22224: connect: connection refused. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:44 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:45 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:45 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...
CRED    : 2025/10/26 22:10:45 credservice.go:32: ERROR    Configuration bootstrap error: temporary ban detected. Retrying with the fallback mechanism...

I love Hola VPN so much and this is how they repay me…

My love for Hola is greater than the hammer! I will not let it defeat me!

Time to fight Fire with Fire! Snawoot, the developer that released hola-proxy also released a proxy for another Free VPN service. He reverse engineered the Opera Browser’s free VPN service and created a tool to proxy through it similarly to hola-proxy.

Using this tool (opera-proxy), we can setup a local proxy that uses Opera VPN’s proxy network:

> ./bin/opera-proxy
MAIN    : 2025/10/26 22:13:57 main.go:225: INFO     opera-proxy client version v1.12.0 is starting...
MAIN    : 2025/10/26 22:13:57 main.go:525: INFO     Attempting action "anonymous registration", attempt #1...
MAIN    : 2025/10/26 22:13:57 main.go:528: INFO     Action "anonymous registration" succeeded on attempt #1
MAIN    : 2025/10/26 22:13:57 main.go:525: INFO     Attempting action "device registration", attempt #1...
MAIN    : 2025/10/26 22:13:57 main.go:528: INFO     Action "device registration" succeeded on attempt #1
MAIN    : 2025/10/26 22:13:57 main.go:525: INFO     Attempting action "discover", attempt #1...
MAIN    : 2025/10/26 22:13:57 main.go:373: INFO     Discovered endpoints: [77.111.247.6:443 77.111.247.44:443 77.111.247.77:443 77.111.244.209:443]. Starting server selection routine "fastest".
MAIN    : 2025/10/26 22:13:58 main.go:403: INFO     Selected endpoint address: 77.111.247.77:443
MAIN    : 2025/10/26 22:13:58 main.go:528: INFO     Action "discover" succeeded on attempt #1
MAIN    : 2025/10/26 22:13:58 main.go:444: INFO     Starting proxy server...
MAIN    : 2025/10/26 22:13:58 main.go:455: INFO     Init complete.
Routing traffic through the Opera VPN network
> curl -x http://127.0.0.1:18080 ip-api.com
{
  "status"       : "success",
  "continent"    : "Europe",
  "continentCode": "EU",
  "country"      : "Sweden",
  "countryCode"  : "SE",
  "region"       : "E",
  "regionName"   : "Östergötland",
  "city"         : "Linköping",
  "district"     : "",
  "zip"          : "582 22",
  "lat"          : 58.4122,
  "lon"          : 15.6258,
  "timezone"     : "Europe/Stockholm",
  "offset"       : 3600,
  "currency"     : "SEK",
  "isp"          : "Opera Browser VPN",
  "org"          : "Opera Software LLC",
  "as"           : "AS205016 HERN Labs AB",
  "asname"       : "HERNLABS",
  "mobile"       : false,
  "proxy"        : true,
  "hosting"      : false,
  "query"        : "77.111.247.77"
}
PROXY   : 2025/10/26 22:14:22 handler.go:102: INFO     Request: 127.0.0.1:48188 HTTP/1.1 GET http://ip-api.com/
PROXY   : 2025/10/26 22:14:23 handler.go:93: INFO     127.0.0.1:48188 GET http://ip-api.com/ 200 OK

From here, we can use hola-proxy to request access to the hola VPN network while proxying these access requests through the opera proxy network which means you will have a different IP when requesting access to the hola network and might not be banned.

If this sounds like gibberish don’t worry its about to make a lot more sense…

Basically we’re using the Opera VPN to authenticate to the Hola VPN and use Hola VPN as an exit node. Here is a diagram explaining how the traffic is routed:

                                       
                          Our machine  
 ┌────────────────────────────────────┐
 │        ┌───────────────┐           │
 │        │curl ip-api.com│           │
 │        └───────┬───────┘           │
 │                │                   │
 │                │  localhost:8888   │
 │                │                   │
 │   ┌────────────┴──────────────┐    │
 │   │                           │    │
 │   │     Hola Local Proxy      │    │
 │   │                           │    │
 │   └────────────┬──────────────┘    │
 │                │                   │
 │                │                   │
 │                │ localhost:18080   │
 │                │                   │
 │                │                   │
 │   ┌────────────┴──────────────┐    │
 │   │                           │    │
 │   │     Opera Local Proxy     │    │
 │   │                           │    │
 │   └────────────┬──────────────┘    │
 └────────────────┼───────────────────┘
                  .                    
                 www                   
                  .                    
     ┌────────────┴──────────────┐     
     │                           │     
     │       Opera Server        │     
     │                           │     
     └────────────┬──────────────┘     
                  .                    
                 www                   
                  .                    
     ┌────────────┴──────────────┐     
     │                           │     
     │        Hola Server        │     
     │                           │     
     └────────────┬──────────────┘     
                  .                    
                 www                   
                  .                    
          ┌───────┴────────┐           
          │   ip-api.com   │           
          └────────────────┘           
                                   

By doing so we can bypass the IP ban we received earlier and access the Hola VPN network again:

> ./bin/hola-proxy -country FR -bind-address "127.0.0.1:8888" -proxy http://127.0.0.1:18080
MAIN    : 2025/10/26 22:15:26 main.go:247: INFO     hola-proxy client version  is starting...
MAIN    : 2025/10/26 22:15:26 main.go:347: INFO     Attempting action "get latest version of Chrome browser", attempt #1...
MAIN    : 2025/10/26 22:15:27 main.go:258: INFO     latest Chrome version is "142.0.7444.52"
MAIN    : 2025/10/26 22:15:27 main.go:263: INFO     discovered latest Chrome User-Agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"
MAIN    : 2025/10/26 22:15:27 main.go:350: INFO     Action "get latest version of Chrome browser" succeeded on attempt #1
MAIN    : 2025/10/26 22:15:27 main.go:347: INFO     Attempting action "get latest version of browser extension", attempt #1...
MAIN    : 2025/10/26 22:15:27 main.go:283: INFO     discovered latest browser extension version: 1.247.39
MAIN    : 2025/10/26 22:15:27 main.go:350: INFO     Action "get latest version of browser extension" succeeded on attempt #1
MAIN    : 2025/10/26 22:15:27 main.go:292: WARNING  Detected latest extension version: "1.247.39". Pass -ext-ver parameter to skip resolve and speedup startup
MAIN    : 2025/10/26 22:15:27 main.go:299: INFO     Constructing fallback DNS upstream...
MAIN    : 2025/10/26 22:15:27 main.go:347: INFO     Attempting action "run credentials service", attempt #1...
MAIN    : 2025/10/26 22:15:29 main.go:350: INFO     Action "run credentials service" succeeded on attempt #1
MAIN    : 2025/10/26 22:15:29 main.go:325: INFO     Endpoint: https://zagent2713.hola.org:22225
MAIN    : 2025/10/26 22:15:29 main.go:326: INFO     Starting proxy server...
MAIN    : 2025/10/26 22:15:29 main.go:328: INFO     Init complete.


> curl -x http://127.0.0.1:8888 ip-api.com
{
  "status"       : "success",
  "continent"    : "North America",
  "continentCode": "NA",
  "country"      : "United States",
  "countryCode"  : "US",
  "region"       : "NJ",
  "regionName"   : "New Jersey",
  "city"         : "North Bergen",
  "district"     : "",
  "zip"          : "07047",
  "lat"          : 40.7964,
  "lon"          : -74.0203,
  "timezone"     : "America/New_York",
  "offset"       : -14400,
  "currency"     : "USD",
  "isp"          : "DigitalOcean, LLC",
  "org"          : "DigitalOcean, LLC",
  "as"           : "AS14061 DigitalOcean, LLC",
  "asname"       : "DIGITALOCEAN-ASN",
  "mobile"       : false,
  "proxy"        : true,
  "hosting"      : true,
  "query"        : "159.89.52.126"
}

If you’re still lost, this is the TLDR:

Wrapping up

Its just business in the end, these Free VPNs are not going to fund themselves. Instead they use your data and your resources in exchange of providing their service. Very dodgy and can be easily abused by malicious actors and land you in big trouble if you’re not cautious. I would stay really far away from these unless you know what you’re getting yourself into.

One thing I hope is that they become more transparent and stop hiding the fact that they’re just wrappers on wrappers around other dodgy providers.

I’m happy that people have started catching on and that these Free VPN providers have had to transition from hiding the fact that they resell your traffic to basically telling people they’re a “community-powered free VPN”, which sounds so much better than “we’re selling your data through a company called Luminati”:

I picked on BrightVPN and HolaVPN at random but I’m sure that most of the Free VPNs on the play store will do the exact same thing… Stay safe out there!

Appendix

BrightVPN: MyFirstAndroidApplication

Setting up mitm proxies for dynamic application reversing using adb

Warning

This setup might not work for all applications. For example, Xamarin applications are not proxy aware and you need to force traffic re-routing using iptables. (see the writeup here for example).

adb connect 192.168.122.1:4444   
adb devices -l

# if Burp/mitmproxy listens on host tcp/8080
adb reverse tcp:8080 tcp:8080

# set the global proxy
adb shell settings put global http_proxy 127.0.0.1:8080

# verify:
adb shell settings get global http_proxy

# clear later:
adb shell settings put global http_proxy :0

# for non-proxy aware applications, for re-route traffic (may require invisible proxy passthrough enabled in Burpsuite)
iptables -t nat -A OUTPUT -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:8080
iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:8080

# you can also use tcpdump on the device itself to look at traffic or take a packet capture
tcdump

Loading AlwaysTrustUserCerts Module using KernelSU

You may need to install the application first (the kernel is patched but the application is not installed). The apk is available on KernelSU’s github releases repository.

Once KernelSU is installed, we can load modules exactly the same way we would load them with Magisk.

Push the module zip with adb and select from the KernelSU application:

$ adb push AlwaysTrustUserCerts_v1.3.zip /sdcard/Download/
AlwaysTrustUserCerts_v1.3.zip: 1 file pushed, 0 skipped. 0.5 MB/s (6808 bytes in 0.014s)

We then reboot and can see the module is installed successfully:

Note: Magisk and KernelSU are modules are very similar but not fully compatible. As such, you may need to modify or adjust them slightly depending on what you are using.

Once loaded, you can install your cert as a user trusted certificate in Android Settings:

> adb push ~/.mitmproxy/mitmproxy-ca-cert.cer /sdcard/Download/
/home/kingjulien/.mitmproxy/mitmproxy-ca-cert.cer: 1 file pushed, 0 skipped. 0.1 MB/s (1172 bytes in 0.016s)

And once you setup your proxy and configure it on the device, you can start seeing and intercepting traffic:

> adb reverse tcp:8080 tcp:8080
8080

> adb shell settings put global http_proxy 127.0.0.1:8080    

Wireshark capture of proxy tunnel request

Annotations:

  • 149.28.37.150 is the Hola VPN Proxy IP
  • 77.111.242.72 is the Opera VPN Proxy IP
  • Hola Local listener on localhost:8888
  • Opera Local listener on localhost:18080

As you can see, no direct connection is made to the Hola VPN Proxy IP but instead it routes via the Opera VPN IP first (note the proxy CONNECT request from the Hola local Proxy to the Opera local Proxy):

References

BlissOS, KernelSU and installed Trusted certs:

Android Emulator, QEMU, adb routing:

hola-proxy and opera-proxy:

Ascii graph editors: