Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
slipstream — 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. | Kitploit
Outils/GitHubGitHub/samyk/slipstream
ReconnaissanceExploitationSécurité WebSécurité RéseauTests d'Intrusion
GitHubsamyk/slipstream

slipstream

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.

Voir le dépôt
2.0k2142il y a 3 ansVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Site web

NAT Slipstreaming

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

Architecture NAT Slipstreaming 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

Table des matières

  • Résumé
  • Les détails
    • Network Address Translation (NAT)
      • Suivi de connexion
      • Application Level Gateway
    • Investigation du routeur / Extraction du firmware
    • Rétro‑ingénierie du firmware
      • Recherche de fichiers intéressants
      • Exploration de fonctions intéressantes
      • Ports / Services à examiner
      • Rétro‑ingénierie de l’objet noyau
    • Investigation du suivi de connexion / Application Level Gateway
      • Linux Netfilter
    • Contrôle des limites de paquets / Fragmentation
    • Attaque temporelle TCP / Découverte du sous‑réseau et de l’IP internes
      • Attaque temporelle
    • Confusion de protocole dans le navigateur
      • Altération des paquets en direct dans le navigateur
  • Autres découvertes
  • Téléchargement
  • Contact

Résumé

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 :

  • la victime visite un site malveillant (ou un site avec une publicité malveillante)
  • l’IP interne de la victime doit d’abord être extraite par le navigateur et envoyée au serveur
    • tentative d’extraction de l’IP interne via le canal de données WebRTC en https
      • certains navigateurs (Chrome) ne divulguent l’IP locale que via WebRTC en HTTPS, mais certaines de nos attaques nécessitent HTTP ; on redirige donc d’abord vers la version HTTPS du logiciel d’attaque pour extraire l’IP locale
      • on redirige ensuite vers la version HTTP avec l’IP locale incluse dans l’URL si on a pu l’obtenir, afin de contourner d’autres mécanismes de protection inter‑origines (l’adresse .local mDNS/Bonjour présentée ne sera pas utile pour l’attaque)
    • si l’IP interne n’est pas divulguée par WebRTC (Safari) ou en l’absence de WebRTC (≤ IE11), attaque temporelle TCP basée sur le web
      • des balises img cachées vers toutes les passerelles courantes (ex. 192.168.0.1) sont chargées en arrière‑plan
      • des événements onerror/onsuccess sont attachés aux balises img
      • si un RST TCP est renvoyé par la passerelle (ou SYN + réponse HTTP), on a détecté un sous‑réseau valide
      • on ré‑exécute l’attaque temporelle sur toutes les IP des sous‑réseaux détectés (/24), en mesurant le temps de déclenchement de onerror/onsuccess
      • la réponse la plus rapide est probablement l’IP interne, mais toutes les réponses sont considérées comme candidates et attaquées

paquet réussi découpé en paquet SIP valide

Les détails

Network Address Translation (NAT)

Nous 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).

NAT

Suivi de connexion

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.

root@kitploit:~
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.

Investigation de routeur / Extraction de firmware

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

root@kitploit:~
![R7000-V1.0.9.64_10.2.64.chk](https://assets.kitploit.com/production/public/readmes/3946/ff37a83fee71d670f7d0298b6c22182a4e55d0e3ec949d82256b532820438723.png)

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

binwalk R7000-V1.0.9.64_10.2.64.chk

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

root@kitploit:~
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 ../..

root@kitploit:~
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 !

Rétro-ingénierie du Firmware

Trouver des fichiers intéressants

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

root@kitploit:~
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

root@kitploit:~
### 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.

root@kitploit:~
### 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.

diffbits

Rétro-conception de l'objet noyau

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...

Ghidra _decode

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

Ghidra tdts.ko

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 :)

Tentative d'envoi d'un paquet SIP dans une requête HTTP POST

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()

root@kitploit:~
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.

Poursuite du reverse engineering de l'objet noyau

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).

Ghidra sip_decode

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 ?

Investigation du Connection Tracking / Application Level Gateway

Linux Netfilter

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.

Linux ALG

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_init
  • nf_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 IPv6
  • sip_help_tcp(...) appelé lorsqu'un paquet TCP SIP correspondant arrive
    • process_sip_msg(...) si cela ressemble à un paquet SIP potentiel
      • si c'est une requête

Contrôle des limites de paquets

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é.

Segmentation TCP

À 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).

POST d'un gros formulaire pour mesurer la MTU et la taille des données TCP

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.

Maximum Segment Size personnalisé (img/sniff1.png)

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

root@kitploit:~
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):

![SIP REGISTER Via IP validation](https://assets.kitploit.com/production/public/readmes/3946/46b6a320ab0e8972a37c8e057cebb36d5fb32905ef2894ea6a06bfe508e7db59.png)

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. :)

[![successful packet broken into valid SIP packet](https://assets.kitploit.com/production/public/readmes/3946/9d8133cafc497975e4bfc89034da0246e8bdd460f73b213e91d71bf14dd097e6.png)](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]>.
Télécharger l’outil
  • une grande sonde TCP est envoyée via un formulaire caché et un POST HTTP automatique vers le serveur HTTP de l’attaquant, lié à un port non standard, pour forcer la segmentation TCP et la découverte de la taille MTU maximale de la pile IP de la victime
    • le serveur TCP de l’attaquant envoie l’option TCP Maximum Segment Size pour ajuster la taille des paquets sortants de la victime (RFC 793 §3.1), permettant de contrôler la taille des paquets TCP du navigateur
  • une grande sonde UDP est envoyée depuis le navigateur via le mécanisme d’authentification TURN de WebRTC vers un port non standard du serveur de l’attaquant, pour forcer la fragmentation IP en bourrant le champ username de TURN
    • on effectue une attaque similaire à la segmentation TCP, mais en UDP, car la fragmentation IP produira des valeurs différentes de la segmentation TCP
    • la taille MTU de la victime, la taille de l’en‑tête IP, la taille du paquet IP, la taille de l’en‑tête TCP et les tailles des segments TCP sont détectées par le serveur et renvoyées au navigateur de la victime, utilisées ensuite pour le bourrage des paquets
  • (v1) un « paquet SIP » est généré dans un nouveau formulaire caché, contenant l’IP interne pour déclencher le suivi de connexion Application Level Gateway
    • un « POST HTTP » vers le serveur sur le port TCP 5060 (port SIP) est initié, en contournant les ports restreints du navigateur
    • les données POST sont « bourrées » pour correspondre exactement à la taille du segment TCP / à la limite du paquet, puis le « paquet SIP » est ajouté et posté via le formulaire web
    • la pile IP de la victime découpe le POST en plusieurs paquets TCP, laissant le « paquet SIP » (faisant partie des données POST) dans son propre paquet TCP, sans aucun en‑tête HTTP
    • si le navigateur modifie la taille de la limite multipart/form (Firefox) ou si la taille du paquet change pour une autre raison, le changement de taille est communiqué au client, qui renvoie automatiquement avec la nouvelle taille
    • lors de l’ouverture d’un port UDP, le paquet SIP est envoyé via le protocole TURN dans un champ username spécialement conçu pour forcer la fragmentation IP et un contrôle précis des limites
  • (v2) un « paquet H.323 » utilisant une connexion STUN basée sur TCP (contournant les correctifs de v1 et les restrictions de ports du navigateur) est généré, contenant l’IP interne pour déclencher le suivi de connexion ALG, mais en forçant une redirection vers n’importe quel autre hôte du réseau dans un paquet de « renvoi d’appel »
    • un « renvoi d’appel H.323 » vers le serveur sur le port TCP 1720 (port H.323) est initié, contournant les ports restreints du navigateur, même si le port est bloqué — l’évasion de port est effectuée en utilisant la fonctionnalité STUN de WebRTC, qui ne respecte pas la liste des ports restreints
    • le champ username 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 web
    • la pile IP de la victime découpe le POST en plusieurs paquets TCP, laissant le « paquet H.323 » (faisant partie des données STUN) dans son propre paquet TCP, sans aucun en‑tête HTTP
    • si le navigateur modifie la taille de la limite multipart/form (Firefox) ou si la taille du paquet change pour une autre raison, le changement de taille est communiqué au client, qui renvoie automatiquement avec la nouvelle taille
  • le NAT de la victime voit un paquet SIP REGISTER correct sur le port SIP, ou un paquet de renvoi d’appel H.323 correct (sans données HTTP), ce qui déclenche l’ALG pour ouvrir tout port TCP/UDP défini dans le paquet vers tout hôte de la victime sur le réseau
    • le NAT de la victime réécrit le paquet SIP ou H.323, remplaçant l’IP interne par l’IP publique, indiquant à l’attaquant que l’exploitation a réussi
    • (v2) comme le renvoi d’appel H.323 peut rediriger vers n’importe quelle autre IP, le paquet peut contenir n’importe quelle IP interne de n’importe quel autre hôte du réseau de la victime, forçant le NAT à rediriger le port vers n’importe quel système du réseau
    • même si le NAT de la victime réécrit normalement les ports source, l’ALG sera quand même obligé de rediriger le port vers le port choisi par l’attaquant, car il croit que la machine de la victime (ou une autre machine du réseau, entièrement déterminée par l’attaquant) a ouvert ce port ; l’attaquant voit le nouveau port source dans le paquet SIP/H.323 arrivant
    • l’attaquant peut désormais contourner le NAT de la victime et se connecter directement à n’importe quel port de n’importe quelle machine du réseau, exposant des services et systèmes auparavant protégés/cachés
  • à explorer… peut‑être par vous ?
    • usage non malveillant : cette technique donne essentiellement au navigateur une capacité de socket TCP et UDP complète pour communiquer avec n’importe quel protocole localement sur le système ; la connexion peut être abstraite via un serveur cloud qui se reconnecte, mais le navigateur parle simplement au serveur cloud comme s’il s’agissait du socket, rendant les navigateurs bien plus puissants pour communiquer sur des protocoles non web
    • si vous testez dans une machine virtuelle (VM) utilisant un réseau partagé (utilisé pour protéger l’hôte des attaques en le routant à travers lui, sans le laisser directement sur le réseau), si les paquets sortent, c’est la machine hôte parente qui voit les ports ouverts, pas la VM ;)
    • la fragmentation IP permet un contrôle complet de toutes les données de la section données IP, donc un contrôle complet de l’en‑tête UDP, y compris les ports source/dest dans le paquet débordé… qu’est‑ce que cela pourrait encore abuser ?
  • process_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 SIP
  • c'est un défi car si on utilise seulement un navigateur web, on ne peut pas produire une connexion TCP brute et faire commencer un paquet avec nos propres données, car elles seront remplies par les en-têtes HTTP/TLS... ou peut-on ?
  • process_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 encore
  • nf_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 correctement
  • sip_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