ポートスキャンのテクニック

自動車修理に関して全くの初心者である筆者が修理を行う場合、原始的な工具(ハンマー、ダクトテープ、レンチなど)を次々と取り出し、目の前の作業を行うのに適した工具を探してあれでもないこれでもないと何時間も悪戦苦闘する様が想像できる。結局うまく行かず、ポンコツ車を本物の修理工のところまで引っ張っていくと、彼はいつも巨大な工具箱をごそごそ漁って、最適な小道具を引っ張り出し、いとも簡単に作業を終わらせてしまう。ポートスキャンの技術も、これに似ている。専門家は数多くのスキャンテクニックを熟知しており、与えられた作業を行うのに最適のテクニック(あるいは組み合わせ)を選択する。一方、経験の乏しいユーザやスクリプトキディたちは、あらゆる問題をデフォルトのSYNスキャンだけで解決しようとする。Nmapは無料なので、ポートスキャン技術に精通するか否かの分かれ目は、知識だけなのだ。その点については、自動車業界よりましなのは間違いない。なにしろ自動車業界では、ストラット スプリングコンプレッサーが必要と判断するには卓越したスキルが必要であるばかりか、それを手に入れるために数千ドルを支払わなければならないからだ。

ほとんどのスキャンタイプを利用できるのは、高い権限を持つユーザに限られる。こうしたスキャンでは生パケットが送受信されるが、これを行うのにUNIXシステムではrootアクセス権が必要だからである。Windowsでは管理者アカウントの使用が推奨されるが、このプラットフォームでは、WinPcapがすでにOSにロードされている場合には、Nmapが高い権限を持たないユーザにも役に立つ場合がある。Nmapがリリースされた1997年当時は、共用のシェルアカウントしか使えないユーザが多かったので、root権限が必要という条件は重大な制約になった。だが今や、世の中は変わった。コンピュータはますます安くなり、常時接続のインターネットアクセスを利用するユーザは激増し、デスクトップのUNIXシステム(Linux や MAC OS Xなど)も広く普及している。WindowsバージョンのNmapも利用可能になり、さらに多くのデスクトップでNmapを実行できるようになっている。これらの理由により、ユーザが制限のある共用シェルアカウントからNmapを実行する必要性が少なくなってきている。これは好都合なことである。高い権限を必要とするオプションを利用できれば、Nmapははるかに大きな効果と柔軟性を発揮するからだ。ほとんどのスキャンタイプを利用できるのは、高い権限を持つユーザに限られる。こうしたスキャンでは生パケットが送受信されるが、これを行うのにUNIXシステムではrootアクセス権が必要だからである。Windowsでは管理者アカウントの使用が推奨されるが、このプラットフォームでは、WinPcapがすでにOSにロードされている場合には、Nmapが高い権限を持たないユーザにも役に立つ場合がある。Nmapがリリースされた1997年当時は、共用のシェルアカウントしか使えないユーザが多かったので、root権限が必要という条件は重大な制約になった。だが今や、世の中は変わった。コンピュータはますます安くなり、常時接続のインターネットアクセスを利用するユーザは激増し、デスクトップのUNIXシステム(Linux や MAC OS Xなど)も広く普及している。WindowsバージョンのNmapも利用可能になり、さらに多くのデスクトップでNmapを実行できるようになっている。これらの理由により、ユーザが制限のある共用シェルアカウントからNmapを実行する必要性が少なくなってきている。これは好都合なことである。高い権限を必要とするオプションを利用できれば、Nmapははるかに大きな効果と柔軟性を発揮するからだ。

Nmapは正確な結果を出そうと試みるが、その洞察はすべて、ターゲットマシン(もしくは前面のファイアウォール)から送り返されるパケットに基づいて得られたものであるという点に留意する必要がある。RFCに準拠していないホストがますます広く使われるようになっているが、これらのホストからは、Nmapのプローブに対して、当然想定される応答は返ってこない。FIN、Null、Xmasスキャンなどは特に、この問題の影響を受けやすい。これらは特定のスキャンタイプに固有の問題なので、個々のスキャンタイプの項で述べることにする。

本節では、Nmapがサポートする10個あまりのスキャンテクニックについて述べる。この手法は一度に1つしか使えないが、UDPスキャン(-sU)だけは例外で、TCPスキャンタイプのいずれか1つと組み合わせて用いることができる。ポートスキャンタイプのオプションは、覚えやすいように-s<C>の形式になっている。ここで<C>は、スキャン名のなかの目立つ文字で、通常は頭文字になる。この規則の例外の1つは、廃止予定のFTPバウンススキャン(-b)である。デフォルトでは、NmapはSYNスキャンを実行するが、ユーザが生パケットを送信するための然るべき権限(UNIX上ではrootアクセス権が必要)を持っていない場合や、IPv6のターゲットが指定された場合は、代わりにConnect()スキャンが用いられる。ここで一覧したスキャンのなかで、高い権限を持たないユーザが実行できるのは、connect()スキャンと ftpバウンススキャンだけである。

-sS (TCP SYN スキャン)

SYNスキャンはデフォルトであり、正当な理由で最もよく使用されるスキャンオプションである。強制的なファイアウォールによる妨害のない、高速なネットワーク上では、数千ポート毎秒という高速なスキャンを実行できる。SYNスキャンは、TCPコネクションを確立しないため、比較的秘匿性が高い。また、NmapのFin/Null/Xmas、Maimon、Idleスキャンのように特定のプラットフォームの特質に左右されることはなく、規格準拠のTCPスタックなら何に対しても機能する。さらには、openclosed、およびfilteredというポートの状態を明確かつ確実に区別することができる。

この技法は、完全なTCPコネクションを開くわけではないので、 ハーフオープン(half-open)スキャンと呼ばれることも多い。あたかも実際にコネクションを開くつもりがあるかのように、SYNパケットを送信し、応答を待つ。SYN/ACKの応答は、ポートが待ち受け状態(open)であることを示し、またRST(reset)は、待ち受け状態にないことを示している。数回再送信しても何の応答もない場合、ポートはfilteredと見なされる。また、ICMP到達不能エラー(タイプ 3、コード 1、2、3、9、10、13)が送り返された場合も、ポートはfilteredと見なされる。

-sT (TCP connect() スキャン)

 TCP Connect()スキャンは、SYNスキャンを選択できない場合のデフォルトのTCPスキャンタイプである。ユーザが生パケットの権限を持たないか、IPv6ネットワークをスキャンする場合がこれにあてはまる。Nmapは、他のほとんどのスキャンタイプのように生パケットに書き込むのではなく、connect()システムコールを発行して、ターゲットのマシンやポートにとのコネクションを確立するよう下位OSに要求する。これは、Webブラウザ、P2Pクライアント、その他ほとんどのネットワーク対応アプリケーションがコネクションを確立するために使用するのと同じ高レベルのシステムコールである。これは、「BerkeleyソケットAPI」というプログラミングインターフェースの一部である。Nmapは、生パケットの応答を回線から読み込むのではなく、このAPIを使って、接続を試みるたびにステータス情報を入手する。

SYNスキャンが利用できる場合は通常、そちらを使用した方がよい。Nmapは生パケットよりも、高レベルのシステムコールであるconnect()に対するほうが制御の自由度が低いので、処理効率も悪くなるからだ。connect()システムコールは、SYNスキャンが行うようにハーフオープン接続をリセットするのではなく、ターゲットのopenポートとのコネクションを確立する。この処理は、同じ情報を得るのにさらに多くの時間とパケットを必要とするだけでなく、ターゲットマシンのログに接続が記録される可能性も高くなる。まともなIDSならどちらも検知するはずだが、たいがいのマシンにはそのような警告システムは備わっていない。平均的なUNIXシステムで実行されているサービスの多くは、Nmapが接続を確立し、その後データ送信を行わずに接続を閉じた場合、syslogに簡単な記録や時には不可解なエラーメッセージを追加する。真にお粗末なサービスは、これが起きた場合に停止してしまうが、まずめったにないことだ。管理者は、特定のシステムからの接続試行がかなりの回数にわたってログに記録されているのを発見したら、このconnect()スキャンのターゲットになっていると見なすべきである。

-sU (UDP スキャン)

インターネット上で最も広く利用されているサービスの大部分は、TCPプロトコルで実行されているが、UDPサービスも広く導入されている。DNS、SNMP、DHCP(それぞれ登録ポートは 53、161/162、67/68)の3つは、最もよく利用されているUDPサービスである。UDPスキャンは通常、TCPよりも処理に時間がかかり難易度も高いので、セキュリティ監査人のなかにはこれらのポートを無視する人もいる。だがこれは誤りである。悪用可能なUDPサービスは極めてよくあるものであり、攻撃者がこうしたプロトコルを見過ごすわけはないからだ。好都合なことに、NmapはUDPポートの一覧表を作成するのに役立てることができる。

UDPスキャンを作動させるには、-sUオプションを指定する。SYN スキャン(-sS)などのTCPスキャンタイプと組み合わせて用いて、同じ実行時間中に両方のプロトコルをチェックできる。

UDPスキャンは、空の(データなし)UDPヘッダを各ターゲットポートに送ることで機能する。ICMPポート到達不能エラー(タイプ3、コード 1、2、9、10、13)が返された場合、ポートはclosed(閉じている)状態にある。その他のICMPポート到達不能エラー(タイプ3、コード3)が返された場合、ポートはfiltered(フィルタあり)と見なされる。まれにサービスがUDPパケットで応答することがあるが、その場合はポートがopenであることがわかる。数回の再試行の後も応答がない場合、ポートはopen|filteredに分類される。これは、ポートが開いているか、もしくはパケットフィルタが通信を阻んでいることを意味する。バージョンスキャン(-sV)を用いて、実際に開いているポートとフィルタ処理されたポートを識別することもできる。

UDPスキャンに関する大きな課題は、処理の高速化である。Openポートやfilteredポートから応答が送り返されることはほとんどないため、Nmapはそのままタイムアウトし、プローブや応答が行方不明になった場合に備えて再試行を行うことになる。閉じたポートは、さらに大きな問題になる場合が多い。閉じたポートからは通常、ICMPポート到達不能エラーが返されるが、閉じたTCPポートがSYNやConnectスキャンに応答してRSTパケットを送る場合とは異なり、多くのホストでは、ICMPポート到達不能メッセージがデフォルトでレート制限されている。Linux や Solarisは、この点に関して特に厳しい。例えば、Linux 2.4.20カーネルは、宛先到達不能メッセージを毎秒1個(net/ipv4/icmp.cで指定)に制限している。

Nmapはレート制限を検出し、それに応じて処理速度を下げて、ターゲットマシンで落とされるような無用なパケットでネットワークを溢れさせないようにする。残念ながら、Linux方式で毎秒1パケットに制限されると、65,536個のポートをスキャンするのに18時間あまりかかる。UDPスキャンの速度を上げるためのアイデアには、次のようなものがある。同時並行でスキャンするホストの数を増やす、よく使われるポートだけを先に重点的にスキャンする、ファイアウォールの背後からスキャンする、--host-timeoutオプションを使って低速なホストをスキップする。

-sN; -sF; -sX (TCP Null、FIN、およびXmasスキャン)

これら3つのスキャンタイプ(次の節で述べる--scanflagsオプションを併用するとさらにいろいろなことができる)は、TCP RFCの巧妙な抜け穴を突いて、openポートとclosedポートを識別するためのものである。TCP RFCの65ページには、宛先ポートの状態が CLOSEDならば... RSTを含まない入力セグメントは、その応答としてRSTを送信するとある。次のページでは、SYN、RST、ACKなどのビットセットを含まない、openポート宛てパケットについて述べてあり、ここに至ることはなさそうであるが、もし至ったらセグメントを破棄してリターンするとある。

このRFC文書に準拠しているシステムをスキャンすると、SYN、RST、ACKなどのフラグビットを含まないパケットに対しては、ポートが閉じている場合はRSTが返され、ポートが開いている場合は何の応答も返されないことになる。これら3つのフラグビットが含まれない限り、他の3つ(FIN、PSH、URG)をどのように組み合わせてもよい。Nmapは以下の3つのスキャンタイプでこの弱点を突く。

Null スキャン (-sN)

何のビットも設定しない(tcpヘッダのフラグは0)

FIN スキャン (-sF)

TCP FINビットだけを設定する

Xmas スキャン (-sX)

FIN、PSH、URGのフラグをすべて設定し、クリスマスツリーのようにパケットをライトアップする

これら3つのスキャンタイプは、プローブパケットに設定されるTCPフラグの違いを除けば、まったく同じ動作を示す。RSTが返された場合、ターゲットポートはclosedと見なされ、何の応答もない場合はopen|filteredになる。ポートがfilteredに分類されるのは、ICMP到達不能エラー(タイプ 3、コード 1、2、3、9、10、13)が返された場合である。

これらのスキャンの最大の利点は、特定のステートレスなファイアウォールやパケットフィルタリング・ルータをすり抜けることができる点である。さらには、SYNスキャンよりもやや秘匿性が高いことも利点として挙げられる。しかし、あまり当てにしないように。最近のIDS製品はほとんど、これらを検知するように設定できるからだ。不利な点は、すべてのシステムがRFC 793に忠実に準拠しているわけではないことだ。ポートが開いているか否かに関係なく、プローブに対してRST応答を送信するシステムは数多くある。これにより、すべてのポートはclosedに分類されることになる。メジャーなOSでこれを行うのは、マイクロソフトWindows、多くのシスコ製デバイス、BSDI、IBM OS/400などが挙げられる。それでもこのスキャンは、ほとんどのUNIXベースのシステムに対しては有効である。またもう1つ不利な点は、openポートと特定のfilteredポートを区別できないので、応答がopen|filteredに分類されることである。

-sA (TCP ACK スキャン)

このACKスキャンは、openポート(open|filteredも)を判別しないという点で、これまで述べてきたスキャンとは異なっている。ファイアウォールのルールセットを明らかにするために用いられ、ファイアウォールがステートフルか否か、どのポートがフィルタされているかなどを決定する。

ACKスキャンのプローブパケットは、ACKフラグだけが設定されている(--scanflagsを用いている場合を除く)。フィルタなしのシステムをスキャンする場合は、openポートとclosedポートの両方からRSTパケットが返される。Nmapはこれらをunfilteredとして分類する。すなわち、ポートはACKパケットで到達可能だが、openclosedかは判別できないことを意味する。応答を返さないポートや、特定のICMPエラーメッセージ(タイプ 3、コード 1、2、3、9、10、13)を返すポートはfilteredに分類される。

-sW (TCP ウィンドウスキャン)

ウィンドウスキャンは、以下の点を除いては、ACKスキャンとまったく同じものである。すなわち、RSTが返されたら常にunfilteredと分類するのではなく、特定のシステムの実装に関する情報を用いて、openポートとclosedポートを識別する点である。これは、返されるRSTパケットのTCPウィンドウのフィールドを調査して判断する。一部のシステムでは、openポートで正の値のウィンドウサイズ(RSTパケットに対しても)が使われ、closedポートではゼロになる。これにより、ウィンドウスキャンは、RSTが返された場合は常にポートをunfilteredに分類するのではなく、RSTパケット内のTCPウィンドウサイズの値が正であるかゼロであるかによって、それぞれopenポートかclosedポートかに分類する。

このスキャンは、インターネット上では少数派のシステムの実装に関する情報に基づいているので、必ずしも信用できるとは限らない。通常、この実装をサポートしていないシステムは、すべてのポートがclosedという応答を返す。もちろん、対象マシンに開ポートが本当に1つもない場合もあり得る。スキャンしたポートのほとんどがclosedでも、よく使われるポート番号(22、25、53など)がいくつかfilteredである場合、このシステムは影響を受ける可能性が最も高い。またまれに、システムがまさに正反対の挙動を示す場合もある。スキャンの結果、開ポートが1000個で、closed や filteredが3個あることがわかった場合、この3個のポートこそが、本当はopenポートである可能性はかなり高い。

-sM (TCP Maimon スキャン)

Maimonスキャンは、発見者であるUriel Maimon氏の名前にちなんで名付けられた。この技法に関する同氏の論文は、「Phrack」誌の第49号(1996年11月発行)に掲載された。この技法を搭載したNmapは、これの2号後の第51号で公開された。Maimonスキャンは、プローブがFIN/ACKであるという点以外は、Null、FIN、Xmasスキャンとまったく同じものである。RFC 793 (TCP)によると、この種のプローブの応答としては、ポートがopenか closedかに関係なく、RSTパケットが生成されることになっている。だがMaimon氏は、BSD由来のシステムの多くで、ポートが開いている場合には、単にパケットが破棄されるだけになるという現象を見出した。

--scanflags (カスタム TCP スキャン)

本物のNmap上級ユーザなら、あらかじめ用意されたスキャンタイプを使うだけで満足している必要はない。この--scanflagsオプションを使うと、任意のTCPフラグを指定することで、ユーザ独自のスキャンを設計することができる。さあ、創造力を全開にして、Nmapのmanページをただ流し読みして具体的なルールを追加しているようなメーカーのIDSの裏をかいてやろう。

--scanflagsの引数は、例えば9(PSH と FIN)などの数字のフラグ値で指定することもできるが、記号名を使った方が簡単である。URGACKPSHRSTSYNFINをごちゃまぜに組み合わせればよいだけだ。例えば--scanflags URGACKPSHRSTSYNFINで全部指定できるわけだが、もっともこれは、実際のスキャンには使えない。引数を指定する順序は不同である。

またここでは、使いたいフラグだけでなく、TCPスキャンタイプ(-sA-sFなど)も指定できる。この基本タイプによって、応答を解釈する方法をNmapに伝える。例えば、SYNスキャンであれば、応答なしはfilteredポートであることと見なし、FINスキャンであれば、同じ応答なしをopen|filteredと解釈するわけだ。Nmapは、この基本のスキャンタイプと同じ動作をするが、異なる点は、ユーザが指定するTCPフラグを代わりに使うことである。基本のスキャンタイプが指定されない場合は、SYNスキャンが使用される。

-sI <zombie host>[:<probeport>] (Idle スキャン)

この高度なスキャン手法を使用すると、対象ホストに対して完全に匿名でTCPポートスキャンを実行できる(スキャンする側の実IPアドレスからは、対象ホストにパケットが送信されない)。それだけではなく、ゾンビホスト上で連続的に生成されるIPフラグメントID(識別子)が予測可能であることを巧妙に利用した独自のサイドチャネル攻撃を実行して、対象ホスト上のopenポートに関する情報を収集することもできる。IDSシステムでは、このスキャンはこちらで指定したゾンビマシン(稼動中でかつ特定の条件を満たす必要がある)から行われているものとして表示される。この非常に興味深いスキャンタイプは複雑すぎて本稿ではとても全容を説明しきれないので、完全な詳細を掲載した非公式の論文を以下に投稿しておくことにする:https://nmap.org/book/idlescan.html

このスキャンタイプは、(その匿名性のために)格別に秘匿性が高いことに加え、マシン間のIPベースの信頼関係を明らかにすることができる。ポートリストには、指定したゾンビホストから見たopenポートが表示される。よって、(ルータ/パケットフィルタのルールから)信頼関係にあると思われる様々なゾンビマシンを使ってターゲットをスキャンしてみることもできる。

IPIDの変化について、ゾンビホストの特定のポートを調査したい場合は、コロンの後にポート番号を付けたものをゾンビホストに追加して指定できる(ゾンビホスト:プローブポート)。ここでポートを指定しない場合、NmapはTCP Ping用にデフォルトで使用するポート(80)を用いる。

-sO (IP プロトコル スキャン)

IPプロトコルスキャンを使うと、ターゲットマシン上でどのIPプロトコル(TCP、ICMP、IGMPなど)がサポートされているかを特定できる。繰り返し表示されるのは、TCP や UDPのポート番号ではなくて、IPプロトコル番号なので、厳密にはポートスキャンとは言えない。とはいえ、スキャンするプロトコル番号を選定するのに-pオプションを使い、結果は標準的なポートテーブル形式でレポートし、実際のポートスキャン手法と同じスキャンエンジンを基礎に用いている。そのため、ポートスキャンに十分近いものとして、ここに含めた。

プロトコルスキャンは機能として有用であるだけでなく、オープンソースソフトウェアとしての強力さを示すものでもある。この機能については、基本となるアイデアは極めて単純だが、筆者自身追加しようと思ったこともなかったし、周りからそうした要望が寄せられることもなかった。そして2000年の夏、Gerhard Rieger氏がアイデアを考案し、素晴らしい実装パッチを作成して、「nmap-hackers」メーリングリストに投稿してくれた。筆者はこのパッチをNmapのツリーに組み込んで、その翌日に新バージョンとして公開した。市販のソフトウェアで、その機能向上のために設計段階から寄与するほど熱心なユーザを持つソフトはほとんどない

プロトコルスキャンは、UDPスキャンと同様の仕組みで機能する。すなわち、UDPパケットのポート番号フィールドをすべて繰り返し試行する代わりに、IPパケットヘッダを送信して、8bitのIPプロトコル番号フィールドをすべて繰り返し試行する。このヘッダは通常は空で、何のデータも、求められるプロトコルに適したヘッダすら含まれていない。これには例外が3つあり、TCP、UDP、ICMPである。これらのプロトコルについては、適切なプロトコルヘッダが含まれる。そうしないとヘッダを送信しないシステムがあるからで、Nmapはすでにこれらを作成する機能を備えている。プロトコルスキャンは、ICMPポート到達不能メッセージではなくて、ICMPprotocol到達不能メッセージが返されるのを待つ。Nmapはターゲットホストから何らかの応答を何らかのプロトコルで受信した場合、そのプロトコルをopenとして分類する。ICMPプロトコル到達不能エラー(タイプ 3、 コード 2)が返されたら、プロトコルはclosedと分類される。その他のICMP到達不能エラー(タイプ 3、 コード 1、3、9、10、13)が返されたら、プロトコルはfilteredとマークされる(またこれにより、ICMPがopenであることも同時に明らかになる)。数回再送しても何の応答もない場合、プロトコルはopen|filteredとして分類される。

-b <ftp relay host> (FTP バウンス スキャン)

FTPプロトコル(RFC 959)の興味深い特徴の1つは、いわゆるプロキシFTP接続に対応していることである。これにより、ユーザは一台のFTPサーバに接続し、そのファイルを第三者サーバに送るように要求できる。これは、様々なレベルの悪用にうってつけの機能なので、たいていのサーバでは、サポートするのを止めている。例えば、この機能を悪用して、FTPサーバに他のホストをポートスキャンさせることも可能である。単に、ターゲットホストの興味あるポートに順にファイルを送信するよう、そのFTPサーバに要求するだけでよい。エラーメッセージには、ポートが開いているか否かが記述される。これは、ファイアウォールをすり抜けるための有効な手段になる。組織のFTPサーバは、どんなインターネットホストよりも、他の内部ホストにアクセスしやすい場所に設置されている場合が多いからだ。Nmapは、-bオプションでftpバウンススキャンを実行できる。引数は<username>:<password>@<server>:<port>のような形式になる。<Server>は、この脆弱性の影響を受けるFTPサーバの名前かIPアドレスを指定する。通常のURLの場合と同様に、匿名ログインの認証情報(user: anonymous password:-wwwuser@)が使われる場合は、<username>:<password>の部分は省略できる。<server>のデフォルトのFTPポート(21)を用いる場合は、ポート番号(と前のコロン)も省略可能である。

この脆弱性は、Nmapがリリースされた1997年に大きく広まったが、今ではほとんど修正されている。それでも、脆弱なサーバは、いまだにあちらこちらにあるので、その他の方法がすべて失敗した場合は、試してみるだけの価値はある。ファイアウォールの回避が目的なら、ターゲットネットワークをスキャンして開いている21番ポート(もしくはバージョン検出ですべてのポートをスキャンする場合はftpサービスなら何でもよい)を探し出し、それぞれのポートを用いてバウンススキャンを試してみることだ。Nmapを使うと、対象のホストが脆弱か否かを見分けることができる。単に自分の行動の形跡を隠そうとしているだけであれば、ターゲットネットワーク上のホストだけに対象を限定する必要はない(し、むしろそうするべきではない)。脆弱なFTPサーバを求めてインターネットアドレスを無作為にスキャンする場合は、始める前に、システム管理者はこのような方法で自分のサーバを不正に使用されることを迷惑がる場合もあることを頭に入れておく必要がある。