Yourvpndead Vpn Detection
Android app that detects VPN/proxy servers (VLESS/xray/sing-box) via local SOCKS5 vulnerability, exposing exit IPs and server configs without root
- Skill ID
- reason-machines/trending-skills/yourvpndead-vpn-detection
- Publisher
- reason-machines
- Repository
- trending-skills
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
reason-machines/trending-skills/yourvpndead-vpn-detectionInstalls these files- SKILL.md
What this skill tells the agent
YourVPNDead — Android VPN Detection & SOCKS5 Vulnerability Scanner
Skill by 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
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.apkOr download the pre-built APK from Releases.
Required permissions (AndroidManifest.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 geolocationStack: 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:
// 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 VPN2. VPN Interface Detection
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-14003. /proc/net/tcp Scanner — ProcNetScanner.kt
Reads listening ports without root:
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:
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:
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 // ProtectedMore from reason-machines
Use this skill in a project
Open any RiverX project, search the Skills panel for reason-machines/trending-skills/yourvpndead-vpn-detection, and install it in one click.
