A hálózat csapdájában

Utazás a csomagok fantasztikus világába

Azért van olyan sok ujja, hogy hatékonyabban tudjon hackelni.

Gondolkoztál már valaha azon, hogy mi történik a színfalak mögött, amikor elküldesz egy HTTP kérést? Honnan tudják a bitek, hogy merre kell menni, amikor megpingelsz valakit a helyi hálózaton? Ilyen és ehhez hasonló kérdések megválaszolását fogjuk most megkísérelni.

Nem volt egyértelmű, hogy melyik irányból lenne érdemes nekiesni a problémának. Fogjak egy HTTP kérést és ássak le a legaljára vagy induljunk a legaljáról, amíg el nem érünk egy HTTP kérésig? Végül az utóbbi mellett döntöttem. Az eleje így egy kicsit távoli lehet, de a végére szerintem könnyebben összeáll a kép. Az OSI modellben meghatározott rétegek mentén fogunk haladni, kezdjük is a hardverrel.

Fizikai réteg

Jó eséllyel mindenkinek van otthon egy Ethernet hálózata, például valami ehhez hasonló:

Előfordulhat az is, hogy a modem, a router és a switch ugyanabban a dobozban lakik.

Ez az úgynevezett fizikai réteg: egy rakás hálózati eszköz, réz madzagokkal összekötözgetve (vagy varázslattal hajtott vezeték nélküli eszközök). Minden hálózati eszköznek van egy egyedi MAC címe, amit még a gyárban megkap. Van sok érdekesség még itt, aminek utána lehet járni (például, hogy hogyan beszélik le egymás között az eszközök, hogy milyen sebességeket támogatnak), de én személy szerint sokkal több mindent nem tudnék elmondani róla, úgyhogy lépjünk is tovább a következő szintre, az adatkapcsolati rétegre.

Adatkapcsolati réteg

Ezen a szinten végre előkerülnek az általunk jól ismert nullák és egyesek. Van az úgynevezett Ethernet keret, ami egy egységnyi adat, amit egyszerre el tudunk küldeni. Valahogy így néz ki:

6 byte a címzett MAC címe
6 byte a feladó MAC címe
2 byte az adat típusa (0x0800 IP esetén, 0x0806 ARP esetén)
46-1500 byte az adat
4 byte ellenőrző összeg

Érdekes, hogy a feladó önbevallásos alapon megy. Mi történik, ha valaki más MAC címét adjuk ott meg? Lehet ezzel esetleg valami huncutságot csinálni?

Két érdekes dolog is van itt, amit érdemes megemlíteni. Az egyik a MAC spoofing, amikor nem vagyunk elégedettek a hardver gyártó által kiosztott MAC címmel és ezt meg szeretnénk változtatni. Mondjuk azért, mert az internet szolgáltatónk eszköze csak egy regisztrált MAC címmel működik. Vagy ott vannak az Android telefonok, amik alapból véletlenszerű MAC címmel csatlakoznak egy Wi-Fi hálózathoz, hogy ne lehessen nyomon követni a telefont a hálózatok között.

A másik érdekes dolog a MAC flooding, ilyenkor a támadó egy rakás véletlenszerű MAC címet állít be feladóként, ami feltölti a Switch teljes MAC cím tábláját, kiszorítva a valós MAC címeket. Ennek általában az a következménye, hogy egy beérkező valós csomag címzett MAC címét nem fogja megtalálni a MAC cím táblában, ami miatt mindenkinek kiküldi a csomagot. Így a támadó olyan csomagok tartalmába kukkanthat bele, amit nem neki szántak.

A szemfüles olvasó azt is rögtön kiszúrhatta, hogy sehol sem esik szó az úgynevezett IP címekről, amit mi általában használni szoktunk. Ehhez tovább kell lépnünk a következő szintre, ami a hálózati réteg.

Hálózati réteg

Itt több érdekes protokollról is szót kell ejtenünk, amik mind az Ethernet keret adat részébe fognak kerülni. Először is tudnunk kell, hogy egy adott IP címhez milyen MAC cím tartozik.

Address Resolution Protocol

Az ARP ebben segít nekünk, küldhetünk egy kérést, amire az IP cím tulajdonosa válaszolhat. Az üzenet felépítése:

2 byte a hardver típusa (0x0001 az Ethernet esetén)
2 byte a protokoll típusa (0x0800 IP esetén)
1 byte a hardver cím mérete (0x06 Ethernet esetén, mert 6 byte-os egy MAC cím)
1 byte a protokoll cím mérete (0x04 IP esetén, mert 4 byte-os egy IP cím)
2 byte az üzenet típusa (0x0001 a kérés, 0x0002 a válasz)
6 byte feladó hardver címe
4 byte feladó protokoll címe
6 byte címzett hardver címe
4 byte címzett protokoll címe

A fenti ábrát egészítsük ki IP címekkel és nézzünk meg egy konkrét példát a kérésre és a válaszra.

Mondjuk Alice szeretné Bob-ot ping-elni, úgyhogy megkérdezi, hogy a 192.168.1.103-as IP cím melyik MAC címhez tartozik. A kérés (a zöld rész az Ethernet kerethez tartozik, a kék rész az ARP kéréshez):

0xFFFFFFFFFFFF címzett (mindenki)
0x0A0000000002 feladó (Alice)
0x0806 ARP típusú adat
0x0001 Ethernet hardver
0x0800 IP protokoll
0x06 6 byte-os MAC cím
0x04 4 byte-os IP cím
0x0001 kérés típusú csomag
0x0A0000000002 a feladó (Alice) MAC címe
0xC0A80166 a feladó (Alice) IP címe
0x000000000000 a címzett (Bob) MAC címe
0xC0A80167 a címzett (Bob) IP címe
0x???????? ellenőrző összeg

Az IP címek hexadecimális formátumban vannak, a CyberChef remekül használható a konvertálásra. Kérés típusú üzenetben a címzett MAC címe bármi lehet, figyelmen kívül lesz hagyva az értéke.

Bob megkapja ezt a kérést és mivel az ő IP címe a 192.168.1.103, küld is rá egy választ:

0x0A0000000002 címzett (Alice)
0x0A0000000003 feladó (Bob)
0x0806 ARP típusú adat
0x0001 Ethernet hardver
0x0800 IP protokoll
0x06 6 byte-os MAC cím
0x04 4 byte-os IP cím
0x0002 válasz típusú csomag
0x0A0000000003 a feladó (Bob) MAC címe
0xC0A80167 a feladó (Bob) IP címe
0x0A0000000002 a címzett (Alice) MAC címe
0xC0A80166 a címzett (Alice) IP címe
0x???????? ellenőrző összeg

Itt is felmerül a kérdés, hogy vajon mi történik, ha nem csak Bob válaszol az üzenetre, hanem a gonosz Mallory is? Előfordulhat, hogy Alice rossz helyre fogja küldeni Bob-nak szánt üzeneteit?

Az eszközök rendelkeznek egy ARP cache nevezetű dologgal, ami az IP cím - MAC cím összerendeléseket tartalmazza, hogy ne kelljen minden egyes alkalommal megkérdezni. Alice esetén ez valahogy így nézhet ki:

$ ip -br neigh
192.168.1.103                           eth0             0a:00:00:00:00:03
192.168.1.104                           eth0             0a:00:00:00:00:04
192.168.1.1                             eth0             0a:00:00:00:00:01

Ez a cache akkor is frissül, ha kérés nélkül kapunk egy ARP választ, ami azt jelenti, hogy ha Mallory elkezdi elárasztani hamis ARP válaszokkal a hálózatot (Alice-nak azt mondja, hogy ő a router, a router-nek azt mondja, hogy ő Alice), valamint továbbítja az eredeti címzetteknek a rajta keresztülmenő csomagokat, akkor anélkül tudja lehallgatni (vagy akár megváltoztatni) Alice Internet forgalmát, hogy Alice ebből bármit észrevenne.

Internet Protocol

A kövektező protokoll az IP, aminek megjelenésével végre vannak IP címeink és tudunk két IP cím között adatot cserélni. Az IP csomag felépítése:

4 bit verzió (0b0100 IPv4 esetén)
4 bit fejléc mérete (általában 0b0101)
8 bit különböző beállítások, amikbe nem mentem komolyabban bele, küldhetünk 0b00000000-t, abból baj nem lehet :)
2 byte a csomag teljes mérete
2 byte azonosító (a több részre bontott üzenetek csoportosításához)
2 byte tördeléssel kapcsolatos adatok (elképzelhető, hogy az IP csomag, amit küldeni szeretnénk nem fér bele egy Ethernet keretbe, ezért több részre kell bontani, alapesetben 0x00 az értéke)
1 byte TTL (Time-to-live), mindig egyel csökken, ha a csomag áthalad egy hálózati eszközön, ha eléri a nullát, az eszköz eldobja a csomagot
1 byte az adatban használt protokoll típusa (0x01 az ICMP, 0x06 a TCP, 0x11 az UDP)
2 byte ellenőrző összeg
4 byte a feladó IP címe
4 byte a címzett IP címe
  adat

Internet Control Message Protocol

Ha már a ping-et emlegettük korábban, szót kell ejtenünk az ICMP-ről. Elég fura állatfaj, a hálózati réteghez sorolják, de kicsit az az érzésem, hogy a szállítási rétegben lenne a helye. Ugyanúgy egy IP csomagba rakjuk bele, mint az UDP-t vagy a TCP-t, csak nem adatszállítás a célja. A ping esetén valahogy így épül fel az üzenet:

1 byte típus (0x08 a ping kérés, 0x00 a ping válasz)
1 byte kód (ping kérés/válasz esetén nem használt adat)
2 byte ellenőrző összeg
2 byte azonosító (kérés és válasz összepárosítása)
2 byte sorszám (kérés és válasz összepárosítása)
  opcionális adat

Alice már tudja Bob MAC címét, úgyhogy végre elküldheti azt a ping-et, amit eredetileg szeretett volna, amire aztán Bob válaszolhat. Valahogy így néz ki a kérés (a zöld rész az Ethernet kerethez tartozik, a kék rész az IP csomag, a piros rész az ICMP):

0x0A0000000003 címzett MAC címe (Bob)
0x0A0000000002 feladó MAC címe (Alice)
0x0800 IP típusú adat
0b01000101 verzió és fejléc méret
0b00000000 beállítások, amikkel nem foglalkozunk
0x???? a csomag teljes mérete
0x???? azonosító
0x00 tördeléssel kapcsolatos adatok
0xFF TTL
0x01 ICMP csomag
0x???? ellenőrző összeg
0xC0A80166 feladó IP címe (Alice)
0xC0A80167 címzett IP címe (Bob)
0x08 ping kérés típusú üzenet
0x00 nem használt adat
0x???? ellenőrző összeg
0x???? azonosító
0x???? sorszám
0x???????? ellenőrző összeg

Amire Bob a következő választ küldi:

0x0A0000000002 címzett MAC címe (Alice)
0x0A0000000003 feladó MAC címe (Bob)
0x0800 IP típusú adat
0b01000101 verzió és fejléc méret
0b00000000 beállítások, amikkel nem foglalkozunk
0x???? a csomag teljes mérete
0x???? azonosító
0x00 tördeléssel kapcsolatos adatok
0xFF TTL
0x01 ICMP csomag
0x???? ellenőrző összeg
0xC0A80167 feladó IP címe (Bob)
0xC0A80166 címzett IP címe (Alice)
0x00 ping válasz típusú üzenet
0x00 nem használt adat
0x???? ellenőrző összeg
0x???? azonosító (amit Alice küldött)
0x???? sorszám (amit Alice küldött)
0x???????? ellenőrző összeg

Kezd bonyolódni a dolog, pedig még messze vagyunk a végétől. Feltűnt, hogy portokról még nem is esett szó? Nem véletlenül. Ezen a ponton a port fogalma még nem létezik, ideje szintet lépni.

Szállítási réteg

Ha nyitottál már valaha socket-eket bármilyen programozási nyelven, akkor ismerősek lesznek az itt található protokollok. Kezdjük az egyszerűbbel.

User Datagram Protocol

Mint már említettem, az UDP egyszerű. Nincs garantálva, hogy megérkezik a csomag, nincs újraküldés elveszett csomagokra, csak belekiabálunk a cső egyik végébe és reménykedünk, hogy a másik végén hallani fogják. Egy csomag a következőképpen néz ki:

2 byte feladó port
2 byte címzett port
2 byte a csomag teljes mérete
2 byte ellenőrző összeg
  opcionális adat

A feladó port is opcionális, ha nem nulla az értéke, akkor azon a porton várjuk a válasz csomagokat.

Transmission Control Protocol

És ezzel elérkeztünk a méltán híres és közkedvelt TCP-hez. A modern, pakoljunk mindent a böngészőbe és szolgáljuk ki HTTP-n alapú Internet sarokköve. Egészen addig, amíg a HTTP/3 el nem terjed, ami vált UDP-re. Egy csomag a következőképpen néz ki:

2 byte feladó port
2 byte címzett port
4 byte sorszám
4 byte nyugta szám
4 bit header méret (hány darab 4 byte-os blokkból áll)
4 bit lefoglalt bitek, amik nincsenek használatban
8 bit beállítások (SYN, FIN, ACK, URG és a többiek)
2 byte ablak méret
2 byte ellenőrző összeg
2 byte a sürgős adat kezdőcíme (ha URG a csomag)
  opcionális beállítások
  opcionális adat

Nem csak a csomag felépítése a lényeg, hanem az a kis tánc, amit a kliens és a szerver eljár, hogy adatokat cserélhessen. Ki kell építeni és le kell zárni a kapcsolatot, valamint mindkét fél nyugtázza, hogy megkapta a másik által küldött adatokat.

Kapcsolat kiépítése
  1. a kliens küld egy SYN csomagot
    (a sorszám 0, mivel ez az első csomagja a kliensnek)
  2. a szerver válaszol egy SYN, ACK csomaggal
    (a sorszám 0, mivel ez az első csomagja a szervernek, a nyugta szám 1, mivel az előzőleg kapott sorszám az 0 volt és nem volt benne adat, így a következő csomagban az 1-es sorszámra számítunk)
  3. a kliens válaszol egy ACK csomaggal
    (a sorszám 1, a nyugta szám 1)
Adatok cseréje
  1. a kliens küld 10 byte-nyi adatot
    (a sorszám 1)
  2. a szerver válaszol egy ACK csomaggal
    (a sorszám 1, a nyugta szám 11, mivel az előző sorszám 1 volt és 10 byte-nyi adatot kaptunk)
  3. a szerver küld 100 byte-nyi adatot
    (a sorszám 1)
  4. a kliens válaszol egy ACK csomaggal
    (a sorszám 11, a nyugta szám 101)
Kapcsolat lezárása
  1. a kliens küld egy FIN csomagot
    (a sorszám 11)
  2. a szerver válaszol egy FIN, ACK csomaggal
    (a sorszám 101, a nyugta szám 12)
  3. a kliens válaszol egy ACK csomaggal
    (a sorszám 12, a nyugta szám 102)

Alkalmazási réteg

Elegánsan átugrunk két szintet, a viszonyréteget és a megjelenítési réteget. A viszonyrétegen van például a SOCKS protokoll, a megjelenítési réteg feladatköre pedig gyakran összeolvad az alkalmazási réteggel.

Az alkalmazási rétegünk például a HTTP. A fentebb megszerzett tudás felhasználásával nézzük is meg, hogy mi történik, amikor Alice futtat egy egyszerű curl www.example.org parancsot.

Ahhoz, hogy ezt meg tudjuk mondani, tudnunk kell Alice hálózati beállításait. Tegyük fel, hogy valami ilyesmi van beállítva:

auto eth0
iface eth0 inet static
      address 192.168.1.102
      netmask 255.255.255.0
      gateway 192.168.1.1
      dns-nameservers 1.1.1.1
  • a www.example.org domain-nel nem tudunk sokat kezdeni, kell egy IP cím
  • a beállított DNS IP címe nem a helyi hálózaton van, úgyhogy a kérést a router (gateway) felé kell küldeni
  • ARP kérés küldése, hogy kiderítsük a router MAC címét
  • UDP csomag küldése a router MAC címére, az IP csomagban a DNS IP címével
  • a router látja, hogy nem ő a címzett, ezért továbbítja a csomagot az Internet felé (itt még közrejátszik a NAT és a kapcsolatkövetés is, hogy kifelé a router publikus IP címe látszódjon a csomagban és a visszaérkező válaszról tudja, hogy kinek kell továbbítani)
  • az Internet felől érkezik egy válasz UDP csomag, a router látja, hogy nem neki címezték
  • a kapcsolatkövetés miatt tudja a router, hogy merre kell továbbítania a csomagot
  • UDP csomag továbbítása Alice-nek
  • a www.example.org IP címe X.X.X.X
  • HTTP kérés összeállítása, TCP kapcsolat felépítése
  • az X.X.X.X cím nem a helyi hálózaton van, úgyhogy a TCP csomagokat a router MAC címére küldjük a megfelelő címzett IP címmel
  • a válasz csomagokat a router továbbítja Alice-nek
  • TCP kapcsolat lebontása
  • az ARP kérésekre ezen a ponton már valószínűleg nincs szükség, mert benne van a cache-ben

Felmerülhet még a kérdés, hogy miből derül ki, hogy egy IP cím nem a helyi hálózaton van. A fenti address/netmask/gateway beállításokból generálódik egy routing tábla, ami valahogy így nézhet ki:

$ sudo route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

Nem mondom, hogy tudom pontosan hogyan is működik, de azt tippelném, hogy a legszűkebb találatot fogja előszedni vagy a végéről indul visszafelé és az első találatot, de lényeg a lényeg, ha valami a 192.168.1.0-192.168.1.255 tartományban van, akkor azt csak egyszerűen kiküldi a megfelelő MAC címre, az összes többi IP cím esetén a 192.168.1.1 IP címhez tartozó MAC címre küldi a csomagot. A router-nek pedig van egy hasonló routing táblája, ami alapján el tudja dönteni, hogy mit kezdjen azzal a csomaggal.

Összegzés

Remélem sikerült egy csepp betekintést nyerni abba, hogy mi történik "alattunk", amikor használjuk az Internetet. Sok rétegen át rengeteg dolog történik és mi most csak a felszínt kapargattuk meg egy kicsit.

Nem is jutottunk túl messzire, csak a router-ig merészkedtünk. Ami azon túl van... az egy külön világ, ahol olyanok vannak, mint a DSL, SDH, PPP, MPLS, BGP, OSPF, meg még egy csomó rövidítés, aminek még a létezéséről se tudok. És mégis, az esetek többségében eljutnak a csomagjaink a megfelelő címzettekhez. Mi ez, ha nem varázslat?

This post is also available in english: Lost in the network

Hozzáfűznél valamit?

Dobj egy emailt a blog kukac deadlime pont hu címre.

Feliratkoznál?

Az RSS feed-et ajánljuk, ha kedveled a régi jó dolgokat.