ARP Scanning a jeho implementace v C#

Zdroj: SOOM.cz [ISSN 1804-7270]
Autor: jech
Datum: 14.1.2014
Hodnocení/Hlasovalo: 1/3

ARP Scanning je jednou z technik, která slouží pro zjištění aktivních uzlů na síti. Nejprve se podíváme, jak ARP Scan funguje a následně uvidíte, jak je lze napsat v C# s pomocí knihovny SharpPcap. Na konci článku také najdete odkaz na zkompilovanou verzi ARP scanneru, kterou můžete otestovat.

V článku o Man in the middle útoku jste se mohli dočíst, jak pracuje protokol ARP, jehož pochopení je základem k pochopení ARP scanningu. Pokud nějaká stanice odešle ARP request, mohou teoreticky nastat dvě situace:

Pokud se stanice dočká odpovědi, zná tedy IP i MAC adresu druhé stanice. Ví také, že daná stanice je na síti aktivní. Pokud tak odešle ARP requesty s dotazem na všechny možné stanice ze subnetu, z odpovědí lze snadno získat seznam aktivních stanic na síti.

Výhodou např. oproti TCP SYN scanu je, že stanice musí na ARP request odpovědět (přesněji téměř vždy, nemá-li statickou ARP tabulku a zakázán ARP protokol, ale to obyčejné stanice běžně nemají) a na rozdíl od ping či tcp syn scanu jej běžný firewall na koncové stanici neblokuje. Nevýhodou pak samozřejmě je, že lze prozkoumat maximálně broadcastovou doménu, ve které je vaše PC.

Implementace ARP Scanneru v C#

Implementace využívá knihovny SharpPcap v C#. Je založena na dvou hlavních funkcionalitách – odesílání ARP requestů na broadcastovou MAC pro každou IP adresu ze subnetu a zachytávání všech příchozích ARP response paketů. Implementace využívá také pomocné knihovny NetworkHelper, která vrací např. seznam IP adres pro subnet a podobně. Zdrojový kód knihovny zde probírat nebudu, protože není v tomto příkladu stěžejní. Pouze usnadňuje některé operace. ARP Scanner je na rozdíl od předchozích příkladů napsán částečně objektově a kód je tak čitelnější.

Vše jako obvykle začíná ve tříde Main, které nejprve rozparsuje argumenty (přijímá adresu sítě a délku masky – 192.168.1.0/24) a pokud vše proběhne správně, pokusí se dle dodané adresy sítě zjistit, na kterém síťovém zařízení lze scanner spustit. Zařízení následně otevře a získá jeho MAC adresu, která se bude používat jako zdrojová adresa pro odeslané ARP pakety. Následně se vytvoří objekt třídy ARPscan a spustí se jeho metoda startScan, která odstartuje scanning. Ze třídy Main je to vše, dále se podíváme už na samotnou třídu ARPscan.

zdrojový kód fce main z program.cs

Třída má tři veřejné vlastnosti a tři privátní proměnné. V konstruktoru přijímá síťové zařízení na kterém se má skenovat, adresu sítě, délku masky a MAC adresu, která bude nastavená jako zdrojová pro odesílané requesty (konstruktor by se bez ní nicméně obešel, protože ji lze získat ze síťového zařízení).

class ARPscan
{
  private ICaptureDevice device;
  public IPAddress Network { get; set; }
  public PhysicalAddress SrcMac { get; set; }
  public uint NetMaskLenght { get; set; }
  private List liveHosts;
  private PhysicalAddress broadcastMac = PhysicalAddress.Parse("FF-FF-FF-FF-FF-FF");

  public ARPscan(ICaptureDevice device, IPAddress network, uint netMaskLenght, PhysicalAddress srcMac)
  {
    this.device = device;
    this.Network = network;
    this.SrcMac = srcMac;
    this.NetMaskLenght = netMaskLenght;
    liveHosts = new List();
  }
...


Nyní se již dostáváme k funkci startScan, která slouží pro samotné skenování. Nejprve opětovně otevře dané zařízení (pro případ, že by nebylo ještě otevřeno) a poté registruje handler catchArpPacketsEventHandler na událost PacketArrivalEventHandler, která je volána vždy, když dorazí nějaký paket.

Poté je vytvořen filtr na zachytávané pakety – chceme všechny ARP pakety (proto 0x0806), které mají destinaci lokální MAC adresu (jinak bychom zpracovávaly i ty, které odesíláme). Filtr je aplikován a poté je spuštěno zachytávání.

Nyní musíme zjistit všechny IP adresy sítě s danou maskou – k tomu nám slouží třída IPAddressesCalculation z knihovny NetworkHelper. Když máme tento seznam, můžeme v cyklu pro každou IP adresu odeslat ARP request. Je lepší po každém odeslání vlákno na chvíli uspat, abychom síť zbytečně nezahltili. Po skončení cyklu vlákno ještě chvíli čeká a následně je ukončeno zachytávání paketů, zavřeno zařízení a jsou zobrazeny zjištěné aktivní uzly.

public void startScan()
{
  device.Open();
  device.OnPacketArrival += new PacketArrivalEventHandler(catchArpPacketsEventHandler);

  string filter = "ether proto 0x0806 and ether dst host " + SrcMac.ToString(); ;
  device.Filter = filter;

  device.StartCapture();

  List ipInRange = IPAddressesCalculation.GetListIpInNetwork(Network, NetMaskLenght);
  SharpPcap.LibPcap.LibPcapLiveDevice deviceLib;
  deviceLib = (SharpPcap.LibPcap.LibPcapLiveDevice)device;
  IPAddress localIP = deviceLib.Addresses[1].Addr.ipAddress;

  for (int j = 0; j < 1; j++)
  {
    foreach (IPAddress dstIp in ipInRange)
    {
      Sender.SendArpRequest(device, localIP, dstIp, SrcMac, broadcastMac);
      Thread.Sleep(100); //neodesilat zbytecne rychle za seobu, aby mel router sanci odpovedet
    }
  }
  Console.WriteLine("waiting on ARP frames arrival");
  Thread.Sleep(2000);
  device.StopCapture();
  device.Close();
  printLiveHosts();
}


Nejdůležitější funkcí je samozřejmě SendArpRequest ze statické třídy Sender, která vytvoří a odesílá ARP paket. V této funkci dojde nejprve k vytvoření ARP request a ethernetového paketu. U obou paketů je důležitá především cílová MAC adresa. V případě ARP request paketu musí být cílová MAC definovaná jako "00-00-00-00-00-00" a v případě ethernetu jde o broadcastovou MAC, neboli "FF-FF-FF-FF-FF-FF". Ethernetovému packetu pak stačí přiřadit payload (náklad) ve formě ARP paketu a pomocí funkce SendPacket jej odeslat.

public static void SendArpRequest(ICaptureDevice device, IPAddress srcIp, IPAddress dstIp, PhysicalAddress srcMac, PhysicalAddress dstMac)
{
  PhysicalAddress targetHwAddres = PhysicalAddress.Parse("00-00-00-00-00-00");
  ARPPacket arp = new ARPPacket(ARPOperation.Request, targetHwAddres, dstIp, srcMac, srcIp);
  EthernetPacket eth = new EthernetPacket(srcMac, dstMac, EthernetPacketType.Arp);

  //je potřeba přidat i payload, jinak je paket pravděpodobně ignorován
  //arp.PayloadData = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

  eth.PayloadPacket = arp;

  device.SendPacket(eth);
}


Odesílání ARP requestů je tedy kompletní a už nám zbývá jen zpracování odpovědí. To se děje v handleru catchArpPacketsEventHandler, který jsme ve funkci startScan registrovali na událost příchodu paketu. Příchozí paket je rozparsován a je zjištěna zdrojová IP adresa. Pokud se ještě nenachází v našem seznamu „živých“ IP adres, je do něj přidána. K vytisknutí aktivních IP adres dojde po ukončení scanu ve funkci startScan. Scanner je tímto kompletní.

private void catchArpPacketsEventHandler(object sender, CaptureEventArgs e)
{
  //Console.WriteLine("ARP packet catched!");

  Packet packet = Packet.ParsePacket(e.Packet.LinkLayerType, e.Packet.Data);

  Type t = typeof(ARPPacket);
  ARPPacket arpPacket = (ARPPacket)packet.Extract(t);

  //Console.WriteLine("ARP paket prijat, IP je nejspise onine");

  IPAddress liveIP = arpPacket.SenderProtocolAddress;

  if (!liveHosts.Contains(liveIP))
  {
    liveHosts.Add(liveIP);
    Console.WriteLine("host {0} is online!", liveIP.ToString());
  }
}


Pro vlastní testování si můžete stáhnout zdrojové kódy ARPscanneru a také zdrojové projekty ve Visual Studiu (ARPscanner a také nutný projekt knihovny NetworkHelper). Spustitelný exe soubor včetně nápovědy na ovládání programu pak můžete nalézt na mém blogu.

Kompletní zdrojové kódy: ARPscanner_source_codes.rar
Projekty ve VS: ARPscannerPlusNetworkHelper.rar

Binárka ke stažení: ARP Scanner verze 0.3

Ke spuštění je potřeba mít nainstalovanou knihovnu Winpcap.