A hálózat csapdájában
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
- a kliens küld egy
SYN
csomagot
(a sorszám 0, mivel ez az első csomagja a kliensnek) - 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) - a kliens válaszol egy
ACK
csomaggal
(a sorszám 1, a nyugta szám 1)
Adatok cseréje
- a kliens küld 10 byte-nyi adatot
(a sorszám 1) - 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) - a szerver küld 100 byte-nyi adatot
(a sorszám 1) - a kliens válaszol egy
ACK
csomaggal
(a sorszám 11, a nyugta szám 101)
Kapcsolat lezárása
- a kliens küld egy
FIN
csomagot
(a sorszám 11) - a szerver válaszol egy
FIN
,ACK
csomaggal
(a sorszám 101, a nyugta szám 12) - 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ímeX.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?