
NAT Slipstreaming は、被害者のネットワーク上の誰かがウェブサイトを訪問するだけで、攻撃者が被害者マシンにバインドされた任意の TCP/UDP サービスにリモートアクセスし、被害者の NAT/ファイアウォールをバイパスすることを可能にします。
NAT Slipstreaming を使用すると、攻撃者は被害者のNAT/ファイアウォールをバイパスして(リモート任意ファイアウォールピンホール制御)、被害者がWebサイトを訪問するだけで、被害者のNATの背後にある任意のシステムにバインドされた任意のTCP/UDPサービスにリモートアクセスできます。
v1 開発者: @SamyKamkar // https://samy.pl
v2 開発者: Samy Kamkar && (Ben Seri && Gregory Vishnipolsky、Armis 所属)
詳細は Ben & Gregory による v2 の優れた技術解説 をお読みください。ここでは v2 の更新内容を深く掘り下げ、多くの追加詳細を提供しています。
v1 公開: 2020年10月31日 👻
v2 公開: 2021年1月26日
ソースコード: https://github.com/samyk/slipstream
アニメーション版はこちら 、私のフォーク の draw.io で生成。アニメーション内でエッジコンテキストフローと制御をエクスポート可能
NAT Slipstreaming は、タイミング攻撃またはWebRTCによる内部IP抽出、自動リモートMTUおよびIPフラグメンテーション発見、TCPパケットサイズの調整、TURN認証の悪用、正確なパケット境界制御、およびブラウザ悪用によるプロトコル混乱を連鎖させることで、NAT、ルータ、ファイアウォールに組み込まれたアプリケーションレベルゲートウェイ(ALG)コネクショントラッキング機構とユーザーのブラウザを悪用します。宛先ポートを開くのはNATまたはファイアウォールであるため、ブラウザベースのポート制限をすべて回避します。
この攻撃は、一部のTCPおよびUDPパケットのデータ部分を、HTTPやその他のヘッダーを含めずに任意に制御できることを利用します。この攻撃は、すべての主要な最新(および旧来の)ブラウザにわたってこの新しいパケットインジェクション技術を実行するものであり、私のオリジナルの2010年のNATピニング技術(DEFCON 18 + Black Hat 2010で発表)の最新版です。さらに、ローカルIPアドレス発見の新しい技術も含まれています。
この攻撃には、NAT/ファイアウォールがALG(アプリケーションレベルゲートウェイ)をサポートしている必要があります。ALGは、SIPやH323(VoIPプロトコル)、FTP、IRC DCCなど、複数のポート(制御チャネル+データチャネル)を使用するプロトコルに必須です。
高レベルでは、NAT Slipstreaming は次のように動作します:
.local mDNS/Bonjour アドレスは攻撃に有用ではない)192.168.0.1)への隠し img タグがバックグラウンドで読み込まれるonerror/onsuccess イベントが img タグにアタッチされるusername フィールドを詰め込んでIPフラグメンテーションを強制する
NAT(ネットワークアドレス変換)はいくつかの理由で使用されます。NATの最も有用な機能は、単一のパブリックIPアドレスを複数のシステムで共有できることです。これは、ローカルネットワークを作成し、接続するすべてのマシンにローカルIPアドレスを提供し、それらのシステムの1つがインターネットに接続するときに、送信パケットをパブリックIPを使用するように書き換えて応答がNATに戻るようにし、逆に宛先IPを特定のクライアントのIPに書き換えることで実現します。
同じアドレス/ポート(google.com:443)への接続を内部ホスト間で区別するのはNATの責任です。なぜなら、最終的にそれらの送信ポート、宛先IP、送信元IPはすべて同じになるからです。2つの異なる内部ピアが同じ送信元ポートから接続しようとした場合、最新のNATは一方の送信元ポートを変更します(一部のネットワークはすべてのTCP/UDP送信元ポートに対してこれを行います)。

Wikipedia(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.
NAT配下のマシンがパケットを送信し、ルーターがリモートホストからの応答を予想する場合、ルーターは情報(具体的には送信元と宛先のポート、送信元と宛先のIPアドレス、および内部IP)を追跡し、一致するパケットを内部IPに返送します。
LAN上の別のホストが同じ送信元・宛先ポートとIPで同じ接続を試みた場合、NATはそれを区別できません(LAN上では送信元IPが異なりますが、WAN側では同じパブリックIPに書き換えられます)。そのため、NATは送信元ポートを変更しますが、応答を返送する際には元に戻します。
### アプリケーションレベルゲートウェイ
ALGを使用すると、NATはFTPのような複数ポートを使用するプロトコルを追跡し、システムからFTPサーバーへの通信を可能にします。そして、特定のポート上の内部IPにファイルを送信するようリクエストすると、ALGはパケットを書き換えてパブリックIPを含め、FTPサーバーの接続をユーザーに転送します。もしIPが書き換えられていなければ、FTPサーバーは内部IPに接続しようとします(または、シグナリング接続と同じ送信元IPを期待する場合は接続を試みないこともあります)。
[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.
まず、一般的なゲートウェイが実際にパケットやFTP、SIPなどのマルチポートプロトコルをどのように扱うかを見てみたいと思います。そのために、一般的なルーターからファームウェアをリバースエンジニアリングします。物理的なルーターからフラッシュをダンプすることもできますが、メーカーから暗号化されていないファームウェアを入手できれば、より多くのルーターモデルをより迅速に調査できるようになります。
まずは一般的なルーター、Netgear Nighthawk R7000から始めます。簡単な検索で、Netgearの記事と最近のファームウェアが見つかります。ファームウェアをダウンロードして解凍すると、R7000-V1.0.9.64_10.2.64.chkという30MBのファイルが見つかります。```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

`file`コマンドは[マジック情報](https://www.wikiwand.com/en/Magic_number_(programming))を検出しないので、[`binwalk`](https://github.com/ReFirmLabs/binwalk)を使用してファイル内のネストされたデータをスキャンできます。```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

私はmacOSを使用しています。binwalkはデフォルトでいくつかのLinuxアプリに依存しているため、binwalk -e(ファイルを抽出するコマンド)が失敗します。そのため手動で抽出しています(そして私はPerlゴルフが<3です)。```sh
tigerblood:~c/ng$ perl -ne'$@.=$_}{print+substr$@,2221098' R7000-V1.0.9.64_10.2.64.chk > squash.fs
または、[`inout`](https://github.com/samyk/samytools/blob/master/inout) を使用します。例: `inout R7000-V1.0.9.64_10.2.64.chk 2221098`。
`dd` を使用することもできますが、出力を高速にするには大きな `bs` (ブロックサイズ) を指定する必要があります(例: 1024)。ただし、`skip` 属性(squashfs ブロブの位置から開始するように指示する)はブロックサイズを尊重するため、2221098 が 2 以外で明らかに割り切れる数ではないことに気づきました...今、気になっています。```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
それではsquashファイルシステムを展開しましょう。私はmacOSで動作し、lzoをサポートするsquashfs-toolsのフォークのフォークを作成しました。追加でxzとlzoをインストールする必要があるかもしれません。あるいは、Linux上でsasquatchを使用することもできます。```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 ../..
そして最後に、SquashFSを展開できます。```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
これで生のOSを探索できます!
それでは、FTP関連のファイルが見つかるかどうか見てみましょう。FTPは広く使われているプロトコルであり、ALGサポートがルーターで多く見られるからです。私はg toolを使っています。これは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
面白いものはないので、気にしないファイルを無視して、内容が `/ftp/` にマッチするバイナリファイルを `g` で探しましょう。```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 はデフォルトでカレントワーキングディレクトリを再帰的にスキャンします。-l はファイル名のみを表示するため(これらはほとんどがバイナリです)、-a はバイナリファイルをスキャンするため、ftp はマッチするテキストのため、-v '\.(html?|js|gif)$|www/|bin/' はWebファイルと実行可能ファイル((s)bin/ にあります)を無視するためです。lib/lib*.{a,so}{.*,}(bash形式)ファイルは面白くないので、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
### 潜在的に有用な関数の探索
さて、注目すべき2つのファイルがあります -- `lib/modules/tdts.ko` は関連している可能性があり、`lib/modules/2.6.36.4brcmarm+/kernel/lib/br_dns_hijack.ko` はおそらく関連していませんが、面白そうです!後で調査するかもしれません。```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
素晴らしい!ftp機能を持つカーネルオブジェクト(.ko)で、"port"のような単語があるため、FTP ALGに関連している可能性が高い。FTP RFC 959はPORTコマンドの意味を説明している:```
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.
### 調査すべきポート/サービス
いくつかのFTP機能は見つけましたが、使用可能なポートに重点を置いています。現代のブラウザは、FTPを含む多くの[制限ポート](https://github.com/samyk/chromium/blob/2d57e5b8afc6d01b344a8d95d3470d46b35845c5/net/base/port_util.cc#L20-L90)へのアウトバウンドHTTP(S)接続を防止するため、FTP ALGの悪用はおそらく不可能です。
2010年、私が[初めてNAT Pinningを実演した](https://samy.pl/natpin/)とき、DCC CHAT/FILEメッセージを介してポート6667(IRC)を使用しました。すぐにブラウザベンダーはポート6667をブロックしましたが、一部のブラウザはポートを保存するためにuint32(32ビット符号なし整数)を使用し、ポートがブロックされているか確認して、ブロックされていなければ接続していました。これを回避するには、TCPポートが16ビット長であることに注意することが重要です。そのため、選択した「制限」ポートに2**16(65536)を加算すると、この場合は65536+6667=72203となり、ブラウザは72203を保存し、ポート制限を通過し(72203 != 6667)、その後TCPスタックに送られ、そこで16ビットに切り詰められ、目的の制限ポートになります。
私の簡単な[`base calculator, 3`](https://github.com/samyk/samytools/blob/master/3)はこれを示しています(db = 10進数→2進数):```sh
tigerblood:/Users/samy/d$ 3 db 65536 6667 65536+6667
000000010000000000000000
000000000001101000001011
000000010001101000001011
私のdiffbitsツールを使うと、よりよくわかります。これはビット列間の類似点や相違点、さらに複数のビット列グループ間の比較を表示するシンプルなツールで、独自のバイナリプロトコルのリバースエンジニアリングに役立ちます。

お好みの逆アセンブラを開いてください。私はNSAの友人たちによるGhidraを使っています。無料でオープンソースだからです。
tdts.koの中でstringsを使って見つけた関数にはftp_decodeやftp_decoderがありました。そのため、他のALGにも_decode関数がある可能性があります。見てみましょう...

たくさんの_decode関数がありますね...下にスクロールすると、興味深いsip_decodeがあります。

ブラウザの制限ポート一覧を確認すると、デフォルトのSIPポートである5060はChromeで制限されていません :)
SIPはTCP/UDP 5060上で動作しますが、RTP(音声)などのメディアは動的に生成される代替ポートで送信されます。SIP通話のリクエストを送信する際、SIPクライアントはランダムなポートを選択し、それを開き、SIPヘッダに含めます。NATもSIP ALGが有効になっていれば(多くのルーターでデフォルトで有効)、それを認識してポートを開くはずです。
NATがSIPパケットを行単位で読み取ると仮定すると(SIPはHTTPと同様に改行ベースでバイナリプロトコルではありません)、おそらくHTTPヘッダを無視し、POSTデータに到達した時点でREGISTERを読み取り、SIPパケットであると認識するかもしれません。これは2010年のバージョンでIRC DCCに対して機能しました。NATはHTTPヘッダを無視し、IRC DCCコマンドだけを解析しました。
おかしなことに、これにより、私たちのサイトを訪れたユーザーを実際に正規のIRCサーバーに接続させ、チャンネルに参加させ、ユーザーが知らないうちにそのIPからメッセージを送信させることができました! :P 私はこのテクニックを、ポート25がブラウザでブロックされる前、SPFレコードが一般的になる前に、クライアントのIPアドレスでメールサーバーにメールを送信するためにデモしました...クレイジーです。
さて、簡単なテストでは、HTTP POST経由でポート5060にSIP REGISTERパケットを送信しても機能しないようです...おそらくパケットに何かが欠けているのでしょう。```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()
スニッフィングすると、次のように表示されます([`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--
しかし、これではポートが開かず、期待されるIPの書き換えも行われません(これについては後述)。何かが不足しているようです。
カーネルオブジェクトの調査を続けましょう。逆アセンブリでは、SIPパケットの「SIP/2.0」タグが見られるので、ここでパースしている可能性が高いです(「decode」という名前からの推測)。

ああ、これが失敗の原因です。どうやらstrncasecmpを使ってINVITE(REGISTERでも同様のパース)を照合しています。パケットの先頭にある「INVITE」という単語と大文字小文字を区別せずに比較し(SIP INVITEは大文字なのに、これは興味深い)、一致しない場合(ARMアセンブリのbne)には0以外のブランチに飛びます。つまり、単語が一致すれば辞書順で0になり、ct_sip_get_headerに進みます(これは面白そうです)。一致しない場合は処理を中断しているようです。
これが問題です。Webブラウザを使用してアウトバウンドソケット(HTTP(S)経由のTCP、WebRTC付きTURN経由のUDP)を生成することはできますが、ブラウザでTCPデータ部分を「INVITE」という単語で始めるほどの制御はできません。このモジュールはそれを期待しています。2010年のIRC版では、IRC ALGはHTTPヘッダーデータを無視して行ごとにチェックし、POSTデータ内の改行を使用して有効な「IRC DCC」を送信していました。しかし、このSIP ALGははるかに厳格で、リクエストの先頭を制御することは不可能です。TLSを使用する場合、暗号化されたヘッダーがパケットを開始します。HTTPの場合、HTTPメソッド(GET、POSTなど)がパケットを開始します。別の方法でこれを悪用できるでしょうか?
コネクショントラッキングとアプリケーションレベルゲートウェイをより深く理解するために、netfilter(Linuxのネットワークスタック)での動作を確認できます。Linuxソースの解析に基づいて、最も一般的なALGとその動作のチャートを作成しました。

このチャートから、最も興味深いもの(Chromeがブロックしないもの)は、sane(バックアップ)、sip(VoIP)、pptp(VPN)、h323(VoIP)です。これらのプロトコルの中でも特に広く使われているSIPを選びます。また、いくつかのルーターのファームウェアには既にSIPが含まれています。
Linuxにはプロトコルごとにコネクショントラッキングを処理するnf_conntrack_*.cファイルと、パケット改変(修正)を行うnf_nat_*.cファイルがあります。
SIPコネクショントラッキングモジュールをざっと見てみましょう。
module_init(nf_conntrack_sip_init) このコネクショントラッカーを初期化し、nf_conntrack_sip_initを呼び出します。nf_ct_helper_init(...AF_INET, IPPROTO_TCP, "sip", SIP_PORT...) シグナリングはIPv4 AF_INET、TCP IPPROTO_TCP、ポート5060 SIP_PORT から来ると想定しています。これはUDP、TCP、IPv4、IPv6で発生します。sip_help_tcp(...) TCP SIPパケットが一致したときに呼び出されます。
process_sip_msg(...) これがSIPパケットの可能性がある場合。
process_sip_request(...) リクエストの場合。現在わかる限りでは、ブラウザにアウトバウンドTCP接続を強制して任意のトラフィックを送信させることはできません。また、REGISTERやINVITEなどのSIPメソッドで始まるTCP/UDPパケットを作成する必要があります。
Flashは以前アウトバウンドソケットを許可していましたが、完全に制御できる形式ではありませんでした。Javaは許可が必要です。WebSocketも依然としてHTTPです。TLSは暗号化されています。WebRTC (RFC 7742)は暗号化されています。STUN (RFC 3489)とTURN (RFC 5766)は固定形式であり、TURNS (RFC 7065)は暗号化されています。
高レベルでは、TCPパケットの先頭を制御できませんが、大きなパケットを送信したらどうなるでしょうか? 最大パケットサイズが存在するはずです。そのサイズを超えると、パケットは複数のパケットに断片化されなければなりません。TCPパケットサイズをオーバーフローさせ、データの一部を正確に制御できれば、パケットのセグメンテーションを引き起こし、オーバーフローした次のパケットの先頭にデータを配置できるでしょうか?
そのためには、ブラウザが送信するデータ量を知る必要があります。これはブラウザごとに異なり、ユーザーごとにも異なります(HTTPヘッダーが異なるため)。HTTPSはほとんどのコンテンツが暗号化されているため機能しませんが、HTTP POSTではヘッダーの大部分を制御できます。
パケットの一般的なサイズを取得するために、隠しWebフォームからIDとパディングデータを含む大きな(6000バイト)HTTP POSTを http://our.attack.server:5060/pktsize に送信します。攻撃サーバーでは、パケットスニファを実行し、パケットの境界を調べてMTU(最大伝送単位)サイズ、IPヘッダーサイズ、潜在的なIPオプション、TCPヘッダーサイズ、潜在的なTCPオプション、データパケットサイズ、および制御可能なパケット部分を特定します。
また、カスタムサーバーをTCPポート5060で実行し、HTTPトラフィックで応答してブラウザをなだめるようにします。これにより、クライアント側で怪しまれなくなります(不正な応答のサーバーはコンソールにエラーを発生させ、誤った応答をするサーバーはステータススピナーを回し続けます)。

さらに、初期SYN応答時に最大セグメントサイズ(MSS)TCPオプションを送信し、被害者のアウトバウンドパケットサイズを操作してTCPパケットデータサイズを制御しようと試みます(RFC 793 x3.1)。これにより、被害者マシンはTCPパケットを一定サイズに保つよう指示されます。

Linuxでは、ip routeにadvmss <size>を追加することでこれを行えます。ここでは1500を使用します。```sh
ip route replace default via [gateway] dev eth0 advmss 1500
パケットを取得したら、サイズデータを別のPOSTで被害者のクライアントに送り返します。このPOSTには被害者のIDも含まれており、被害者からの元のリクエストと関連付けることができます。この時点で、クライアントは任意のデータをTCPパケット内の特定の位置に到達させるためにパケットをどのようにパディングすべきかをほぼ把握しています。
### UDPとTURNによるIPフラグメンテーション
一部のNATでは、SIP接続が元々UDPだった場合にのみUDPポートへのアクセスを許可するため、このケースではTURNを使用します。TURNはSIPやWebRTCのようなピアツーピア通信のリレーをサポートするプロトコルです。TURNはUDP、TURNS(TURN+TLS)はTCPです。モダンブラウザは、メディア共有のための直接のピアツーピア接続が確立できない場合に備えて、WebRTCでTURNをサポートしています。
TURNはユーザー名とパスワードによる認証を許可しており、ユーザー名は平文で送信されます。興味深いことに、ユーザー名のサイズや文字に制限がないため、これを使用して同じタイプのパケットオーバーフローを実行できます。
TURNはUDP上で動作するため、MTUサイズを超えてオーバーフローするとIPパケット自体が断片化します(UDPはセグメンテーションをサポートしていません)。2番目のパケットには、データ部分だけでなく、UDPヘッダも制御下に置かれます!これは我々の攻撃にとって重要なわけではありませんが、興味深く、別の攻撃を確実に生み出す可能性があります。最終的には、MSSサイズではなく計算されたMTUサイズに基づいてパケット境界を調整することで、UDP経由でも同じ攻撃を実行できます。これにより、SIP UDPパケットが2番目のパケット境界上(偽のUDPヘッダが前置された状態)に存在するようになり、UDPポートを被害者に転送できるようになります。
## TCPタイミング攻撃 / 内部サブネットとIPディスカバリ
ああ、これでもまだうまくいきません!ALGが正当なSIPパケットとして処理するためには、SIPの[`Contact`](https://tools.ietf.org/html/rfc2543#section-6.13)行でデータの戻り先として要求するIPアドレスが、SIPパケットの発信元である内部IP(被害者)でなければなりません。しかし、そのIPはわかりません。ルーターのパブリックIPアドレスだけがサーバに送信されます(NATがパブリック側に送出する際に送信元IPを書き換えるため)。
このチェックは、Linuxの`nf_conntrack_sip.c`の[process\_sip\_request](https://github.com/samyk/linux/blob/ea2cec24c8d429ee6f99040e4eb6c7ad627fe777/net/netfilter/nf_conntrack_sip.c#L1260)で確認できます。

[2010年](https://samy.pl/natpin/)には、[LiveConnect](https://developer.mozilla.org/en-US/docs/Archive/Web/LiveConnect)を使用して、JavaScriptから特定の条件下でJavaコードを実行し、ユーザーのローカルIPを抽出していました。しかし、それはすぐに時代遅れになりました。
一部のブラウザ(Chrome、Firefox)では、[WebRTC](https://www.w3.org/TR/webrtc/)を使用して、[ICE](https://tools.ietf.org/html/rfc5245)(単にSTUN/TURN/TURNSを使用する)経由で被害者の内部IPアドレスを取得できます。これらは、NATの背後にあるピアが自身に関する情報を判断するためのプロトコルです。皮肉なことに、ICE「リクエスト」でサーバを使用する必要はありません。ブラウザはすでに自身の内部IPを認識しており、リモートのSTUN/TURNサーバは、クライアントが最初に送信しない限りそれを知ることはできないからです。問題は、すべてのブラウザがこのメカニズムを提供しているわけではないことです。
現在、ChromeでWebRTCを使用してローカルIPアドレスを取得するには(`.local` mDNS/Bonjourアドレスではなく)、HTTPSが必要です。しかし、他の攻撃にはHTTPが必要なため、最初にHTTPかどうかを検出し、そうでなければHTTPSにリダイレクトします。次にWebRTCを使用してローカルIPアドレスを抽出しようとします。いずれにせよ、その後、URLにIPアドレスを付加してHTTPにリダイレクトし、他の通信方法によるクロスオリジン制限を回避します。
### タイミング攻撃
Safari、IE ≤ 11、またはWebRTCをサポートしていない、あるいは意図的に内部IPを公開しない(Safari)ブラウザを使用している場合は、Webタイミング攻撃を使用して被害者の内部IPアドレスを明らかにすることができます。
これはまず、共通のゲートウェイ(192.168.*.1、10.0.0.1、その他)宛ての隠しHTML ``タグをページに生成し、Javascriptの`onsuccess`および`onerror`イベントと組み合わせることで実現します。画像がページに書き込まれるたびにタイマーが開始され、`onsuccess`が読み込まれた場合、そのIPがWebサーバで応答したことを意味します。IPは存在するがWebサーバが実行されていない場合、TCP RST(リセット、つまりポートが開いていないことを示す)が返送され、`onerror`がトリガーされます。IPが存在しない場合、RSTは送信されず、応答に1秒以上かかるため、そのIPがネットワーク上に存在しないことがわかります。
これらのイベントのいずれかがトリガーされると、現在の内部サブネットの可能性がわかり、次にサブネット上のすべてのIP(例:192.168.0.[2-255])に対して同じ攻撃を実行し、今回はより正確なタイミングで**最も速く**応答するIPを特定します。これが自分の(被害者の)内部IPである可能性が最も高いです。なぜなら、ネットワークインターフェースから出る必要すらないからです。何らかの理由で自分が最初でなくても、ネットワーク上で応答したすべてのIPに対して攻撃を試行します。
## ブラウザプロトコル混乱
クライアントはパケットサイズと内部IPアドレスを取得すると、特別に細工されたWebフォームを構築します。このフォームは、パケットが断片化されると考えられる時点までPOSTデータをパディングし、その時点で内部IPアドレスを含むSIP REGISTERが追加されます。フォームは被害者の同意なしにJavaScript経由で送信されます。:)
[](img/pinpkt.png)
### ライブブラウザパケット改変
攻撃サーバでは、パケットが到着するのを確認できるため、SIPパケットがパブリックIPアドレスで書き換えられたかどうかを監視します。書き換えられていない場合、クライアントに(自動的に)SIPパケットが期待したパケット境界になく書き換えられなかったことを通知し、スニファから新しい境界位置を提供します。
クライアントコードは、2回連続で失敗した場合にのみ、新しいサイズに自動調整します。一部のブラウザ(Firefox)では、フォーム用に生成するマルチパートバウンダリが他のほとんどのブラウザと異なり固定長ではないため、パケットサイズがわずかに異なる場合があります。約10回試行すると同じサイズが使用され、攻撃が成功することがわかりました。
SIPパケットがパケット境界に到達すると、NATはこれを正当なSIP登録であり、被害者のマシン上のSIPクライアントからのものと信じて騙されます。サーバが適切なSIP応答(ブラウザが何か異常を検出しないように、適切なHTTP応答の中にネストされています)で応答すると、NATは被害者に送信させた元のパケットのポートを開放し、ルータは**攻撃者が選択した任意のポートを内部の被害者に転送するようになります。これは単にWebサイトを閲覧しただけで実現されます**。
攻撃完了。攻撃者は被害者上で動作する任意のTCP/UDPサービスに接続できるようになります。
# その他の発見
これらはこの攻撃では使用されていませんが、それでも興味深く、他の攻撃に使用できる可能性があります。
- IPフラグメンテーションにより、IPデータ部のすべてのデータを完全に制御できるため、オーバーフローしたパケット内のUDPヘッダ(送信元/宛先ポートを含む)を完全に制御できる
- 被害者のIPスタックは再構成しデータを解析しないが、パケットが通過するNATは影響を受けやすい
- 元のパケットのみが検査され、オーバーフローした断片化パケットは検査されないため、ブラウザやシステムファイアウォールをバイパスできる
- `Expires: 0`を含むSIPパケットを送信して他人のconntrackを削除することによるSIPクライアントへのDoS攻撃
- ポートがすでに使用されている場合、リッスンポートはポートが0にオーバーフローするまでインクリメントされる
- 現在のモダンブラウザでは、STUNに認証が実装されていない
# ダウンロード
お読みいただきありがとうございます!概念実証コードは私の[NAT Slipstream github](https://github.com/samyk/slipstream)からダウンロードできます。
# 連絡先
**窓口:** [@SamyKamkar](https://twitter.com/samykamkar)
私の他のプロジェクトは<https://samy.pl>で見つけるか、<[email protected]>までご連絡ください。
username フィールド内で送信され、IPフラグメンテーションと正確な境界制御を強制するusername フィールドは正確なTCPセグメントサイズ/パケット境界に「詰め込まれ」、次に「H.323パケット」が追加され、Webフォームを介して投稿されるstrncasecmp(*dptr, handler->method, ...) ハンドラは、メソッド(例:REGISTER)がパケットのデータ部分(TCPまたはUDP)の先頭に出現しない限り、処理を中断します。 これは前述のINVITEと同様です。REGISTERもSIPコマンドの1つです。process_register_request(...) nf_ct_expect_init(...) sip_handlers を介して、ファイアウォールのピンホール(リモート側が接続してくるためのポート)を初期化しますが、まだ開けません。nf_nat_sip_hooks -> nf_nat_sip(...) NATもクライアントの内部IPアドレスをNATのパブリックIPに書き換え(改変)、宛先が適切に到達できるようにします。sip_help_tcp(...) -> process_sip_msg(...) ->
process_sip_response(...) 今度はSIPサーバーからのSIP応答を確認します。
process_register_response(...) -> refresh_signalling_expectation(...) ポートがNATによって転送されるのは、SIPサーバーから有効なSIP応答が送信された場合のみです。