Claude
Skills
Sign in
Back

yourvpndead-vpn-detection

Included with Lifetime
$97 forever

Android app that detects VPN/proxy servers (VLESS/xray/sing-box) via local SOCKS5 vulnerability, exposing exit IPs and server configs without root

Security

What this skill does


# YourVPNDead — Android VPN Detection & SOCKS5 Vulnerability Scanner

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

Android app (Kotlin + Jetpack Compose) demonstrating that any app — without root or special permissions — can detect VPN usage, identify the VPN client, and retrieve the VPN server's exit IP through unauthenticated SOCKS5 proxies exposed on localhost by popular VPN clients (v2rayNG, NekoBox, Hiddify, etc.).

## Build & Install

```bash
git clone https://github.com/loop-uh/yourvpndead.git
cd yourvpndead
./gradlew assembleDebug
# Output: app/build/outputs/apk/debug/app-debug.apk
adb install app/build/outputs/apk/debug/app-debug.apk
```

Or download the pre-built APK from [Releases](https://github.com/loop-uh/yourvpndead/releases).

**Required permissions** (`AndroidManifest.xml`):
```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
```

## Architecture

```
ScanOrchestrator (14 phases)
├── ProfileDetector          — work profile, isolation, VPN status
├── ProcNetScanner           — /proc/net/tcp fingerprinting
├── DirectSignsChecker       — 6 direct VPN checks
├── IndirectSignsChecker     — 5 indirect checks (MTU, DNS, dumpsys)
├── DeviceInfoCollector      — device fingerprint
├── PortScanner              — TCP scan IPv4 + IPv6 localhost
├── Socks5Probe              — proxy type identification
├── XrayAPIDetector          — xray gRPC API detection
├── ClashAPIProbe            — Clash REST API probe
├── AuthProbe                — auth analysis + brute-force demo
├── ExitIPResolver           — exit IP via SOCKS5
└── GeoLocator               — IP geolocation
```

**Stack**: Kotlin, Jetpack Compose, Material 3, Coroutines, MVVM (ViewModel + StateFlow)

## Key Detection Modules

### 1. Direct VPN Signs — `DirectSignsChecker.kt`

Detects VPN via standard (and hidden) Android APIs:

```kotlin
// Check TRANSPORT_VPN capability
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork
val caps = connectivityManager.getNetworkCapabilities(network)

val hasVpnTransport = caps?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) == true

// Check hidden IS_VPN flag (not in public API)
val capsString = caps?.toString() ?: ""
val hasIsVpn = capsString.contains("IS_VPN")
val hasVpnTransportInfo = capsString.contains("VpnTransportInfo")

// Check system proxy properties
val httpProxyHost = System.getProperty("http.proxyHost")
val socksProxyHost = System.getProperty("socksProxyHost")

// Check NOT_VPN capability absence (inverse detection)
val notVpnCapability = caps?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) == true
// If false → network IS a VPN
```

### 2. VPN Interface Detection

```kotlin
import java.net.NetworkInterface

fun detectVpnInterfaces(): List<String> {
    val vpnPatterns = listOf(
        Regex("^tun\\d+$"),
        Regex("^tap\\d+$"),
        Regex("^wg\\d+$"),
        Regex("^ppp\\d+$"),
        Regex("^ipsec.*$")
    )
    return NetworkInterface.getNetworkInterfaces()
        .toList()
        .filter { iface -> vpnPatterns.any { it.matches(iface.name) } }
        .map { it.name }
}

// Check MTU anomaly (VPN lowers MTU due to encapsulation overhead)
fun checkMtuAnomaly(): Boolean {
    return NetworkInterface.getNetworkInterfaces()
        .toList()
        .filter { it.isUp && !it.isLoopback }
        .any { it.mtu in 1..1499 } // Standard Ethernet = 1500
}
// WireGuard: ~1420, OpenVPN: ~1400, VLESS/xray: ~1380-1400
```

### 3. /proc/net/tcp Scanner — `ProcNetScanner.kt`

Reads listening ports without root:

```kotlin
fun scanProcNetTcp(): List<Int> {
    val openPorts = mutableListOf<Int>()
    listOf("/proc/net/tcp", "/proc/net/tcp6").forEach { path ->
        try {
            File(path).forEachLine { line ->
                val parts = line.trim().split("\\s+".toRegex())
                if (parts.size >= 4) {
                    val state = parts[3]
                    if (state == "0A") { // 0A = LISTEN
                        val localAddress = parts[1]
                        val portHex = localAddress.split(":").lastOrNull()
                        portHex?.toIntOrNull(16)?.let { openPorts.add(it) }
                    }
                }
            }
        } catch (e: Exception) { /* May be restricted on newer Android */ }
    }
    return openPorts
}

// Fingerprint VPN client by port pattern
fun fingerprintClient(ports: List<Int>): String {
    return when {
        10808 in ports && 10809 in ports && 19085 in ports -> "v2rayNG / xray"
        2080 in ports -> "NekoBox / sing-box"
        7890 in ports && 7891 in ports && 9090 in ports -> "Clash / mihomo"
        3066 in ports && 3067 in ports -> "Karing"
        19090 in ports -> "sing-box (Clash API — IP leak via /connections!)"
        else -> "Unknown"
    }
}
```

### 4. Port Scanner — `PortScanner.kt`

TCP connect scan on 127.0.0.1 and ::1:

```kotlin
import kotlinx.coroutines.*
import java.net.InetSocketAddress
import java.net.Socket

suspend fun scanKnownPorts(
    timeout: Int = 300,
    parallelism: Int = 32
): List<Int> = coroutineScope {
    val knownVpnPorts = listOf(
        // xray / v2rayNG
        10808, 10809, 10810, 10085, 19085,
        // sing-box / NekoBox
        2080, 2081, 3066, 3067,
        // Clash / mihomo
        7890, 7891, 7892, 7893, 9090, 19090,
        // Common proxy
        1080, 8080, 8118, 9050, 3128,
        // Yandex.Metrica tracking
        29009, 29010, 30102, 30103,
        // Meta Pixel
        12387, 12388, 12389
    )

    val semaphore = kotlinx.coroutines.sync.Semaphore(parallelism)
    knownVpnPorts.map { port ->
        async(Dispatchers.IO) {
            semaphore.withPermit {
                try {
                    Socket().use { socket ->
                        socket.connect(InetSocketAddress("127.0.0.1", port), timeout)
                        port // Return port if connected
                    }
                } catch (e: Exception) { null }
            }
        }
    }.awaitAll().filterNotNull()
}

// Full scan 1-65535
suspend fun fullPortScan(timeout: Int = 200): List<Int> = coroutineScope {
    val semaphore = kotlinx.coroutines.sync.Semaphore(32)
    (1..65535).map { port ->
        async(Dispatchers.IO) {
            semaphore.withPermit {
                try {
                    Socket().use { socket ->
                        socket.connect(InetSocketAddress("127.0.0.1", port), timeout)
                        port
                    }
                } catch (e: Exception) { null }
            }
        }
    }.awaitAll().filterNotNull()
}
```

### 5. SOCKS5 Probe — `Socks5Probe.kt`

Identify proxy type and check for authentication:

```kotlin
import java.io.InputStream
import java.io.OutputStream
import java.net.Socket

enum class ProxyType { SOCKS5_NO_AUTH, SOCKS5_AUTH_REQUIRED, HTTP_CONNECT, GRPC, UNKNOWN }

fun probePort(port: Int, timeoutMs: Int = 2000): ProxyType {
    return try {
        Socket().use { socket ->
            socket.connect(InetSocketAddress("127.0.0.1", port), timeoutMs)
            socket.soTimeout = timeoutMs
            val out: OutputStream = socket.getOutputStream()
            val inp: InputStream = socket.getInputStream()

            // SOCKS5 handshake: VER=5, NMETHODS=1, METHOD=NO_AUTH(0x00)
            out.write(byteArrayOf(0x05, 0x01, 0x00))
            out.flush()

            val response = ByteArray(2)
            inp.read(response)

            when {
                response[0] == 0x05.toByte() && response[1] == 0x00.toByte() ->
                    ProxyType.SOCKS5_NO_AUTH         // Vulnerable!
                response[0] == 0x05.toByte() && response[1] == 0x02.toByte() ->
                    ProxyType.SOCKS5_AUTH_REQUIRED   // Protected
                else -> Pr

Related in Security