Scapy для полегшення життя :-)

Posted on

Така історія.

Шукав якось генератор OSPF Hello пакетів для бомбардування «лабораторного макета». Трохи помучився із packEth (я ловив/ліпив/перевіряв пакети, він огинався і зависав) і закинув цю ідею — взяв дві циски на двох кінцях каналу (пакетів мало, але регулярно і чесні, мені цього було достатньо).

Вже кинувши цю ідею, погуглив і надибав Scapy.

Це дивовижний інструмент :-)

Подивіться кілька статей:

Отже, двійко рецептів. Генерування OSPF Hello пакетів та Графік часу відгуків на ping.

Генерування OSPF Hello пакетів

По-перше, треба взяти розширення OSPF для Scapy, а далі все просто.

Ми будемо «ліпити» пакет «з нуля», хоча можна було б обійтися лише рівнями IP та OSPF. Крім того, ми будемо будувати рівень за рівнем, хоча все можна було б написати лише одним рядком (створення і надсилання пакета).

Створюємо пакет Ethernet:

>>> packet = Ether(src='00:06:28:b9:85:31',dst='01:00:5e:00:00:05')

>>> packet.show()
###[ Ethernet ]###
  dst= 01:00:5e:00:00:05
  src= 00:06:28:b9:85:31
  type= 0x0

Бачимо, що у полі «тип» — нуль, ніякий тип. Ми створили лише «шаблон» заголовку Ethernet.

Користуючись оператором „/‘, «нарощуємо» мітку vlan:

>>> packet = packet/Dot1Q(vlan=33)

>>> packet.show()
###[ Ethernet ]###
  dst= 01:00:5e:00:00:05
  src= 00:06:28:b9:85:31
  type= 0x8100
###[ 802.1Q ]###
     prio= 0
     id= 0
     vlan= 33
     type= 0x0

Бачимо? — помінявся і тип у заголовку Ethernet.

Далі жужмом додаємо IP і далі (так, все це можна одним рядком — Ether()/Dot1Q()/IP()/OSPF_Hdr()/...):

>>> packet = packet/IP(src='172.17.2.2',dst='224.0.0.5')
>>> packet = packet/OSPF_Hdr(src='172.17.2.2')
>>> packet = packet/OSPF_Hello(router='172.17.2.2',backup='172.17.2.1',neighbor='172.17.2.1')

Ну, тепер подивимося на пакет:

>>> packet.show()
###[ Ethernet ]###
  dst= 01:00:5e:00:00:05
  src= 00:06:28:b9:85:31
  type= 0x8100
###[ 802.1Q ]###
     prio= 0
     id= 0
     vlan= 33
     type= 0x800
###[ IP ]###
        version= 4
        ihl= 0
        tos= 0x0
        len= 0
        id= 1
        flags=
        frag= 0
        ttl= 64
        proto= ospf
        chksum= 0x0
        src= 172.17.2.2
        dst= 224.0.0.5
        options= ''
###[ OSPF Header ]###
           version= 2
           type= Hello
           len= 0
           src= 172.17.2.2
           area= 0.0.0.0
           chksum= 0x0
           authtype= Null
           authdata= 0x0
           reserved= 0x0
           keyid= 1
           authdatalen= 0
           seq= 0x0
###[ OSPF Hello ]###
              mask= 255.255.255.0
              hellointerval= 10
              options=
              prio= 1
              deadinterval= 40
              router= 172.17.2.2
              backup= 172.17.2.1
              neighbor= 172.17.2.1

Як на мене — то просто здуріти можна nice smile

Залишилося лише запустити цей пакет у мережу та зловито його там якиймось аналізатором — для перевірки.

Запускаємо (у потрібний інтерфейс):

>>> sendp(packet,iface='dlink')
.
Sent 1 packets.

І все. Отак просто.

Для перевірки — аналіз пакета аналізатором tshark.

А щоб запустити таки генератор, треба якось так:

>>> sendp(packet,iface='dlink',loop=True,inter=0.1)
.......
[etc-etc-etc...]

А щоб не бачити усіх цих цяточок, до аргументів додайте verbose=1.

Графік часу відгуків на ping

Це ще простіше.

Спочатку пінгаємо:

>>> ans,unans = sr(IP(dst='209.85.139.9')/ICMP()*5000,inter=1,verbose=1)
Begin emission:
Finished to send 5000 packets.

Received 487002 packets, got 5000 answers, remaining 0 packets

Це означає: cпочатку створили пакет ICMP (IP(dst='209.85.139.9')/ICMP()), а точніше, одразу створили 5000 таких пакетів (*5000), і запустили (функція sr — send/receive) не дуже балакучий (verbose=1) пінг на один із серверів Google (dst='209.85.139.9') з інтервалом 1 секунда (inter=1).

Отже, ми маємо ans — список пар ping/pong, з усіма параметрами.

Наприклад, можемо глянути:

>>> ans[0][0].show()
###[ IP ]###
  version= 4
  ihl= 0
  tos= 0x0
  len= 0
  id= 1
  flags=
  frag= 0
  ttl= 64
  proto= icmp
  chksum= 0x0
  src= 10.0.10.70
  dst= 209.85.139.9
  options= ''
###[ ICMP ]###
     type= echo-request
     code= 0
     chksum= 0x0
     id= 0x0
     seq= 0x0

>>> ans[0][1].show()
###[ IP ]###
  version= 4L
  ihl= 5L
  tos= 0x0
  len= 28
  id= 1
  flags=
  frag= 0L
  ttl= 239
  proto= icmp
  chksum= 0x5b3b
  src= 209.85.139.9
  dst= 10.0.10.70
  options= ''
###[ ICMP ]###
     type= echo-reply
     code= 0
     chksum= 0xffff
     id= 0x0
     seq= 0x0
###[ Padding ]###
        load= '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

Для створення графіка використовується метод plot — інтерфейс до Gnuplot:

>>> ans.plot(lambda x:(x[1].time-x[0].sent_time))
<Gnuplot._Gnuplot.Gnuplot instance at 0xb74e146c>

При цьому вискакує десь приблизно такий графік:

Як бачимо, графік трохи сходинками — це може бути через те, що якісь пакети залишаються без відповіді, але ж всі пакети однакові, відповідь на наступний нічим не відрізняється від відповіді на пакет, що реально залишився без відповіді.

Тому треба всі пакети зробити унікальними, замість ICMP() використовувати:

ICMP(id=os.getpid(),seq=RandShort())

До речі, якщо вам не потрібні занадто великі відгуки (всяке в житті буває): їх можна «відсіяти» таким чином:

>>> ans.plot(lambda x:x[1].time-x[0].sent_time,lfilter=lambda x:x[1].time-x[0].sent_time<0.1)
<Gnuplot._Gnuplot.Gnuplot instance at 0xb79d808c>

Тобто, всі затримки, довші за 100 мілісекунд, малюватися не будуть.

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

Цей сайт використовує Akismet для зменшення спаму. Дізнайтеся, як обробляються ваші дані коментарів.