البحث عن النطاقات الفرعية (Subdomains): برمجة أداة استكشاف باستخدام بايثون


البحث عن النطاقات الفرعية (Subdomains): برمجة أداة استكشاف باستخدام بايثون

ماذا سنبني اليوم؟

سنقوم ببناء أداة بسيطة وفعالة بلغة بايثون لاكتشاف النطاقات الفرعية (subdomains) لموقع ويب مستهدف، مما يعزز فهمنا لكيفية عمل استكشاف الشبكات وأهميته في الأمن السيبراني.

الخطوة 1: التحضير الأولي - استيراد المكتبات ومعالجة المدخلات

في هذه الخطوة، سنقوم بإعداد بيئة العمل لاستكشاف النطاقات الفرعية. سنحتاج إلى مكتبة requests لإجراء طلبات HTTP، ومكتبة sys للتعامل مع وسائط سطر الأوامر، ومكتبة os للتعامل مع الملفات. سنتأكد أيضًا من أن المستخدم يقدم النطاق المستهدف.

للتثبيت: تأكد من تثبيت مكتبة requests عن طريق الأمر التالي في سطر الأوامر:
pip install requests
import requests # لاستخدام مكتبة requests لعمل طلبات HTTP
import sys      # لاستخدام مكتبة sys للتعامل مع وسائط سطر الأوامر
import os       # لاستخدام مكتبة os للتعامل مع مسارات الملفات

def find_subdomains(target_domain, wordlist_file="subdomains.txt"):
    # دالة رئيسية للبحث عن النطاقات الفرعية
    # سنضيف الكود الخاص بالبحث هنا لاحقاً
    pass

if __name__ == "__main__":
    # التحقق من أن المستخدم قد قدم النطاق المستهدف كوسيطة لسطر الأوامر
    if len(sys.argv) != 2:
        print("الاستخدام: python subdomain_finder.py <النطاق_المستهدف>")
        sys.exit(1)
    
    target = sys.argv[1] # الحصول على النطاق المستهدف من وسائط سطر الأوامر
    print(f"بدء البحث عن النطاقات الفرعية للنطاق: {target}")
    # find_subdomains(target) # سنقوم باستدعاء الدالة هنا لاحقاً

الخطوة 2: بناء قائمة الكلمات (Wordlist) ومحاولة الاتصال

الآن، سنقوم بتجهيز قائمة الكلمات التي تحتوي على أسماء النطاقات الفرعية المحتملة. سنقرأ هذه الكلمات من ملف نصي، ثم نستخدم كل كلمة لبناء نطاق فرعي كامل ومحاولة الاتصال به عبر HTTP أو HTTPS. إذا استجاب النطاق الفرعي، فهذا يعني أنه موجود.

نصيحة تقنية: ملف قائمة الكلمات (wordlist) هو مفتاح فعالية هذه الأداة. يمكنك العثور على قوائم كلمات جاهزة وشاملة على GitHub، مثل تلك الموجودة في مستودع SecLists. كلما كانت قائمة الكلمات أكبر وأكثر شمولاً، زادت فرصتك في اكتشاف نطاقات فرعية جديدة.
مثال على محتوى ملف subdomains.txt:
www
mail
dev
test
blog
api
import requests
import sys
import os

# تعطيل تحذيرات الشهادات غير الموثوقة عند استخدام verify=False
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

def find_subdomains(target_domain, wordlist_file="subdomains.txt"):
    found_subdomains = [] # قائمة لتخزين النطاقات الفرعية المكتشفة
    
    # التحقق من وجود ملف قائمة الكلمات
    if not os.path.exists(wordlist_file):
        print(f"خطأ: ملف قائمة الكلمات '{wordlist_file}' غير موجود.")
        print("يرجى إنشاء ملف يحتوي على قائمة الكلمات أو تحديد مسار صحيح.")
        sys.exit(1)

    # قراءة الكلمات من ملف قائمة الكلمات
    with open(wordlist_file, "r") as f:
        wordlist = [line.strip() for line in f if line.strip()] # قراءة الكلمات وتجاهل الأسطر الفارغة

    print(f"جارٍ استخدام قائمة كلمات من '{wordlist_file}' تحتوي على {len(wordlist)} كلمة.")

    for word in wordlist:
        subdomain = f"{word}.{target_domain}" # بناء النطاق الفرعي المحتمل (مثال: www.example.com)
        try:
            # محاولة إرسال طلب HTTP إلى النطاق الفرعي عبر HTTPS أولاً
            # استخدام timeout=1 لتجنب التعليق على النطاقات غير الموجودة أو البطيئة
            # allow_redirects=False لتجنب تتبع التحويلات غير المرغوبة والتي قد تخفي وجود النطاق الفرعي الأصلي
            # verify=False لتجاهل مشاكل الشهادات الذاتية أو غير الصالحة عند استخدام HTTPS
            try:
                response = requests.get(f"https://{subdomain}", timeout=1, allow_redirects=False, verify=False)
                if response.status_code < 400: # التحقق من أن كود الحالة ليس خطأ (4xx أو 5xx)
                    print(f"[+] تم العثور على نطاق فرعي (HTTPS): {subdomain}")
                    found_subdomains.append(f"https://{subdomain}")
                    continue # الانتقال إلى الكلمة التالية إذا نجح الاتصال بـ HTTPS
            except requests.exceptions.SSLError:
                pass # تجاهل أخطاء SSL ومحاولة HTTP
            except requests.ConnectionError:
                pass # تجاهل أخطاء الاتصال لـ HTTPS ومحاولة HTTP
            
            # محاولة إرسال طلب HTTP إذا لم ينجح HTTPS
            response = requests.get(f"http://{subdomain}", timeout=1, allow_redirects=False)
            if response.status_code < 400: # التحقق من أن كود الحالة ليس خطأ
                print(f"[+] تم العثور على نطاق فرعي (HTTP): {subdomain}")
                found_subdomains.append(f"http://{subdomain}")

        except requests.exceptions.Timeout:
            # يتم تجاهل أخطاء المهلة، حيث يعني ذلك أن النطاق الفرعي لم يستجب في الوقت المحدد
            pass
        except requests.exceptions.RequestException as e:
            # معالجة أي استثناءات أخرى قد تحدث أثناء الطلب (مثل أخطاء DNS أو أخطاء أخرى في الاتصال)
            pass
    
    return found_subdomains

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("الاستخدام: python subdomain_finder.py <النطاق_المستهدف>")
        sys.exit(1)
    
    target = sys.argv[1]
    print(f"بدء البحث عن النطاقات الفرعية للنطاق: {target}")
    
    # استدعاء الدالة الرئيسية للبحث عن النطاقات الفرعية
    found = find_subdomains(target, "subdomains.txt") 
    print("\n--- النطاقات الفرعية المكتشفة ---")
    if found:
        for s in found:
            print(s)
    else:
        print("لم يتم العثور على أي نطاقات فرعية.")

الخطوة 3: تحسين الأداء والتخصيص (ملاحظات إضافية)

السكربت الحالي هو نقطة انطلاق ممتازة. لتحسينه بشكل أكبر، يمكننا التفكير في:

  • التوازي (Multithreading/Multiprocessing): استخدام عدة خيوط أو عمليات لإجراء طلبات HTTP بشكل متزامن، مما يسرع عملية البحث بشكل كبير.
  • مصادر قائمة الكلمات المتعددة: دمج نتائج من قوائم كلمات مختلفة أو حتى البحث عن النطاقات الفرعية الشائعة عبر الإنترنت.
  • تحليل الاستجابة: بدلاً من مجرد التحقق من كود الحالة، يمكن تحليل محتوى الصفحة (مثل علامات العنوان أو نص الجسم) لتحديد ما إذا كان النطاق الفرعي نشطًا حقًا أو مجرد إعادة توجيه.
  • تسجيل الأخطاء: إضافة تسجيل للأخطاء لتتبع المشاكل التي قد تحدث أثناء الفحص.
ملاحظة أخلاقية وقانونية:
يجب استخدام أدوات استكشاف النطاقات الفرعية بشكل أخلاقي وقانوني. لا تقم أبدًا بفحص نطاق فرعي دون الحصول على إذن صريح من مالك النطاق. الاستخدام غير المصرح به قد يؤدي إلى عواقب قانونية خطيرة. هذه الأداة مخصصة للأغراض التعليمية واختبار الاختراق المصرح به فقط.

الكود النهائي الكامل

إليكم السكربت الكامل الذي قمنا ببنائه خطوة بخطوة، جاهزاً للنسخ والتشغيل:

import requests # لاستخدام مكتبة requests لعمل طلبات HTTP
import sys      # لاستخدام مكتبة sys للتعامل مع وسائط سطر الأوامر
import os       # لاستخدام مكتبة os للتعامل مع مسارات الملفات

# تعطيل تحذيرات الشهادات غير الموثوقة عند استخدام verify=False
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

def find_subdomains(target_domain, wordlist_file="subdomains.txt"):
    """
    يقوم بالبحث عن النطاقات الفرعية لنطاق مستهدف باستخدام قائمة كلمات محددة.
    :param target_domain: النطاق الرئيسي المراد البحث عن نطاقاته الفرعية (مثال: example.com).
    :param wordlist_file: مسار ملف قائمة الكلمات الذي يحتوي على أسماء النطاقات الفرعية المحتملة.
    :return: قائمة بالنطاقات الفرعية المكتشفة.
    """
    found_subdomains = [] # قائمة لتخزين النطاقات الفرعية المكتشفة
    
    # التحقق من وجود ملف قائمة الكلمات
    if not os.path.exists(wordlist_file):
        print(f"خطأ: ملف قائمة الكلمات '{wordlist_file}' غير موجود.")
        print("يرجى إنشاء ملف يحتوي على قائمة الكلمات أو تحديد مسار صحيح.")
        sys.exit(1)

    # قراءة الكلمات من ملف قائمة الكلمات
    with open(wordlist_file, "r") as f:
        wordlist = [line.strip() for line in f if line.strip()] # قراءة الكلمات وتجاهل الأسطر الفارغة

    print(f"جارٍ استخدام قائمة كلمات من '{wordlist_file}' تحتوي على {len(wordlist)} كلمة.")

    for word in wordlist:
        subdomain = f"{word}.{target_domain}" # بناء النطاق الفرعي المحتمل (مثال: www.example.com)
        try:
            # محاولة إرسال طلب HTTP إلى النطاق الفرعي عبر HTTPS أولاً
            # استخدام timeout=1 لتجنب التعليق على النطاقات غير الموجودة أو البطيئة
            # allow_redirects=False لتجنب تتبع التحويلات غير المرغوبة والتي قد تخفي وجود النطاق الفرعي الأصلي
            # verify=False لتجاهل مشاكل الشهادات الذاتية أو غير الصالحة عند استخدام HTTPS
            try:
                response = requests.get(f"https://{subdomain}", timeout=1, allow_redirects=False, verify=False)
                if response.status_code < 400: # التحقق من أن كود الحالة ليس خطأ (4xx أو 5xx)
                    print(f"[+] تم العثور على نطاق فرعي (HTTPS): {subdomain}")
                    found_subdomains.append(f"https://{subdomain}")
                    continue # الانتقال إلى الكلمة التالية إذا نجح الاتصال بـ HTTPS
            except requests.exceptions.SSLError:
                pass # تجاهل أخطاء SSL ومحاولة HTTP
            except requests.ConnectionError:
                pass # تجاهل أخطاء الاتصال لـ HTTPS ومحاولة HTTP
            
            # محاولة إرسال طلب HTTP إذا لم ينجح HTTPS
            response = requests.get(f"http://{subdomain}", timeout=1, allow_redirects=False)
            if response.status_code < 400: # التحقق من أن كود الحالة ليس خطأ
                print(f"[+] تم العثور على نطاق فرعي (HTTP): {subdomain}")
                found_subdomains.append(f"http://{subdomain}")

        except requests.exceptions.Timeout:
            # يتم تجاهل أخطاء المهلة، حيث يعني ذلك أن النطاق الفرعي لم يستجب في الوقت المحدد
            pass
        except requests.exceptions.RequestException as e:
            # معالجة أي استثناءات أخرى قد تحدث أثناء الطلب (مثل أخطاء DNS أو أخطاء أخرى في الاتصال)
            pass
    
    return found_subdomains

if __name__ == "__main__":
    # التحقق من أن المستخدم قد قدم النطاق المستهدف كوسيطة لسطر الأوامر
    if len(sys.argv) != 2:
        print("الاستخدام: python subdomain_finder.py <النطاق_المستهدف>")
        sys.exit(1)
    
    target = sys.argv[1] # الحصول على النطاق المستهدف من وسائط سطر الأوامر
    print(f"بدء البحث عن النطاقات الفرعية للنطاق: {target}")
    
    # استدعاء الدالة الرئيسية للبحث عن النطاقات الفرعية
    # تأكد من وجود ملف 'subdomains.txt' في نفس مسار السكربت أو قم بتغيير المسار هنا.
    found = find_subdomains(target, "subdomains.txt") 
    print("\n--- النطاقات الفرعية المكتشفة ---")
    if found:
        for s in found:
            print(s)
    else:
        print("لم يتم العثور على أي نطاقات فرعية.")

النتيجة المتوقعة

عند تشغيل السكربت، ستحتاج إلى توفير النطاق المستهدف كوسيطة سطر أوامر. على سبيل المثال، إذا كان ملف السكربت الخاص بك هو subdomain_finder.py وتريد فحص example.com، فستقوم بتشغيله كالتالي:

python subdomain_finder.py example.com

ستكون النتيجة على النحو التالي (مع اختلاف النطاقات الفرعية المكتشفة بناءً على قائمة الكلمات والنطاق المستهدف الفعلي):

بدء البحث عن النطاقات الفرعية للنطاق: example.com
جارٍ استخدام قائمة كلمات من 'subdomains.txt' تحتوي على 6 كلمة.
[+] تم العثور على نطاق فرعي (HTTPS): www.example.com
[+] تم العثور على نطاق فرعي (HTTP): dev.example.com
[+] تم العثور على نطاق فرعي (HTTPS): api.example.com

--- النطاقات الفرعية المكتشفة ---
https://www.example.com
http://dev.example.com
https://api.example.com