シェルコードとは、ソフトウェアの脆弱性を悪用するためのペイロードとして使用されることを目的とした実行可能コードです。この用語には「シェル」が含まれていますが、これは元々攻撃者がターゲットマシンを制御するために使用できるコマンドシェルを開く攻撃を指していたためです。しかし、本来許可されていないアクセスを取得するために注入されるコードはすべてシェルコードと呼ばれる可能性があります。このため、シェルコードという名称は不正確だと考える人もいます。[ 1 ]
攻撃では、脆弱性を悪用して制御権を奪取する前、あるいは悪用している最中に、実行可能コードを含むデータをプロセスに注入するのが一般的です。プログラムカウンタはシェルコードのエントリポイントに設定され、シェルコードが実行されます。シェルコードの展開は、脆弱性のあるプロセスがダウンロードしてメモリにロードするファイルにコードを含めることで行われることがよくあります。
一般的に、効果を最大化するには、シェルコードのペイロードは小さくする必要があるとされています。[ 2 ]マシンコードは、その目的を達成するために必要な柔軟性を提供します。シェルコードの作成者は、小さなオペコードを利用してコンパクトなシェルコードを作成します。[ 3 ] [ 4 ]
ローカルシェルコード攻撃は、攻撃者がコンピュータ上で高いアクセス権限を取得することを可能にします。場合によっては、バッファオーバーフローなどのエラーを引き起こすことで脆弱性を悪用できます。攻撃が成功すると、シェルコードは標的プロセスに付与された高い権限を介してマシンへのアクセスを可能にします。
リモートシェルコード攻撃は、同じローカルエリアネットワーク、イントラネット、またはインターネット上のリモートマシンで実行されているプロセスを標的とします。攻撃が成功すると、シェルコードはネットワーク経由で標的マシンへのアクセスを可能にします。シェルコードは通常、TCP/IPソケット接続を開き、標的マシン上のシェルへのアクセスを許可します。
リモートシェルコード攻撃は、その動作によって分類できます。シェルコードが接続を確立する場合、それはリバースシェルまたはコネクトバックシェルコードと呼ばれます。一方、攻撃者が接続を確立する場合、シェルコードは被害者のマシンの特定のポートにバインドするため、バインドシェルと呼ばれます。バインドシェルランダムポートは、バインド部分をスキップしてランダムなポートでリッスンします。[ a ]ソケット再利用シェルコードは、シェルコードが実行される前に閉じられない脆弱なプロセスへの接続を確立し、シェルコードが接続を再利用してリモートアクセスを可能にするエクスプロイトです。ソケット再利用シェルコードは、シェルコードが再利用する接続を見つける必要があり、マシンに多くの開いている接続がある可能性があるため、より複雑です。[ 5 ]
ファイアウォールは、コネクトバックシェルコードによる発信接続とバインドシェルによる着信接続の両方を検出できるため、攻撃に対する一定の防御策となります。システムに脆弱性があっても、ファイアウォールは攻撃者がシェルコードによって作成されたシェルに接続するのを阻止できます。ソケット再利用シェルコードが使用される理由の一つは、新しい接続を作成しないため、検出やブロックが困難であることです。
ダウンロード実行シェルコード攻撃は、標的システムにマルウェアをダウンロードして実行します。このタイプのシェルコードはシェルを起動するのではなく、ネットワークから特定の実行可能ファイルをダウンロードして実行するようにマシンに指示します。現在では、ドライブバイダウンロード攻撃でよく使用されています。この攻撃では、被害者が悪意のあるウェブページにアクセスし、そのページがダウンロード実行シェルコードを実行して被害者のマシンにソフトウェアをインストールしようとします。
この攻撃の変種では、ライブラリをダウンロードしてロードします。[ 6 ] [ 7 ]この手法の利点は、コードが小さくできること、シェルコードがターゲットシステム上で新しいプロセスを生成する必要がないこと、シェルコードがターゲットプロセスをクリーンアップするコードを必要としないことです。これは、プロセスにロードされたライブラリによって実行できます。
攻撃者が標的プロセスに注入できるデータ量が、目的の効果を達成するには少なすぎる場合、段階的にアクセス権限を拡大していくシェルコードを展開することが可能になるかもしれません。最初の段階では、目的のアクセス権限を提供する第2段階をダウンロードするだけで済む場合もあります。
エッグハントシェルコード攻撃は、攻撃者がプロセスにシェルコードを注入できるものの、それがプロセスのどこにあるのか分からない段階的な攻撃です。第2段階のシェルコード(通常は第1段階よりも小さい)がプロセスに注入され、プロセスのアドレス空間から第1段階のシェルコード(エッグ)を検索して実行します。[ 8 ]
オムレットシェルコード攻撃は、エッグハントと同様に、複数の小さなデータブロック(卵)を探し出し、それらを結合してより大きなブロック(オムレット)を作成し、それを実行します。これは、攻撃者が注入するコードのサイズに制限があるものの、複数のコードを挿入できる場合に使用されます。[ 9 ]
シェルコードは、プロセスが許可するデータに対する制限を回避するために作成されることが多い。一般的な手法としては、以下のようなものがある。
コードを最適化してサイズを小さくする。
実行前に自身のコードを修正し、通常は制限されているバイト値を使用するようにする。
侵入検知を回避するには、自己復号化またはポリモーフィックな方法でエンコードしてください。
ブラウザを標的とする攻撃では、拡張文字エンコーディングを使用してJavaScript文字列内のシェルコードを難読化する可能性があります。 [ 10 ]例えば、IA-32アーキテクチャでは、エンコードされていない2つの無操作命令(NOPスライドで使用)は次のとおりです。
90 NOP 90 NOP
エンコードされた形式:
unescape("%u9090")\u9090邐または邐シェルコードは、ターゲット プロセスで通常のアルゴリズム ( strcpy ) によってコピーされるヌル終端文字列に挿入されることを想定している場合、ゼロ値バイトを含まないように記述する必要があります。このアルゴリズムでは、コピーは最初のゼロ バイト (一般的な文字セットではヌル文字と呼ばれます) で終了します。シェルコードにヌルが含まれている場合、コピーは切り捨てられ、正しく機能しません。ヌルを含むコードからヌルを含まないコードを生成するには、ゼロを含むマシン命令を、ゼロを含まない命令に置き換えることができます。たとえば、IA-32アーキテクチャでは、レジスタ EAX を 1 に設定する命令は、リテラルの一部としてゼロを含みます (は に展開されます)。10x00000001
B8 01000000 MOV EAX,1
以下の手順は、まずEAXを0に設定し、次にEAXを1にインクリメントすることで、ゼロバイトを埋め込むことなく同じ目的(EAXに1を格納すること)を達成します。
33C0 XOR EAX,EAX 40 INC EAX
英数字シェルコードは、英数字(0~9、A~Z、a~z)のみで構成されます。 [ 11 ] [ 12 ]このタイプのエンコーディングは、ハッカーがプレーンテキストのように見えるものの中にマシンコードを難読化するために作成しました。これは、コードの検出を回避するのに役立ちます。つまり、文字列から英数字以外の文字を削除するフィルタをコードが通過できるようにします。[ b ]同様のタイプのエンコーディングは印刷可能コードと呼ばれ、印刷可能な文字 (英数字と !@#%^&* などの記号) をすべて使用します。同様に制限されたバリアントは、ECHOコマンドで受け入れられない文字を含まないECHOable コードです。通常の英語のテキストのように見えるシェルコードを作成できることが示されています。[ 13 ]このようなシェルコードを作成するには、ターゲットマシンの命令セットアーキテクチャ を深く理解する必要があります。複数のマシンで実行可能な英数字コードを記述することが可能であることが実証されており、[ 14 ]これによりマルチアーキテクチャ実行可能コードが構成されます。
回避策はRixによってPhrack 57 [ 11 ]で公開され、任意のコードを英数字コードに変換できることが示されています。多くの場合、自己修正コードが利用されます。これは、実行時にコード化された値を置き換えることで、通常は許可されないバイト値をコードに持たせることができるためです。最初は許可されたバイトのみを使用する自己修正デコーダを作成できます。シェルコードのメインコードも、許可された範囲のバイトのみを使用してエンコードされます。出力シェルコードが実行されると、デコーダは必要な命令を使用するようにコードを変更し、元のシェルコードをデコードします。シェルコードをデコードした後、デコーダは制御をシェルコードに渡します。通常の英語のテキストのように見える任意の複雑なシェルコードを作成できることが示されています。[ 13 ]
現代のソフトウェアは、国際化とローカライズをサポートするためにUnicodeを使用します。多くの場合、入力されたASCIIテキストは処理前に Unicode に変換されます。ASCII (一般的にはLatin-1 ) 文字が UTF-16 (16 ビット Unicode) に変換される場合、元のテキストの各バイト (文字) の後にゼロ バイトが挿入されます。Obscou はPhrack 61 [ 12 ]で、この変換後に正常に実行できるシェルコードを作成できることを証明しました。元のシェルコードをデコードする小さな自己修正デコーダと同じ原理に基づいて、任意のシェルコードを自動的に英数字の UTF-16 耐性シェルコードにエンコードできるプログラムが存在します。
一般的に、シェルコードは、ターゲット プロセスへの比較的保護されていないアクセスを可能にするため、マシン コードとして展開されます。マシン コードは比較的狭いコンピューティング コンテキスト (プロセッサ、オペレーティングシステムなど) 内で互換性があるため、シェルコード フラグメントの互換性は限られています。また、シェルコード攻撃はコードが小さい場合に最も効果的であり、複数のエクスプロイトをターゲットにするとサイズが大きくなるため、通常はコードは 1 つのエクスプロイトのみをターゲットにします。それでも、単一のシェルコード フラグメントは、複数のコンテキストとエクスプロイトで機能することができます。[ 15 ] [ 16 ] [ 17 ]複数のコンテキストの実装を含む単一のフラグメントを作成することで、汎用性を実現できます。共通コードは、実行時コンテキストの実装に分岐します。
シェルコードは一般的に単独では実行できないため、その動作を調べるには、通常、特別なプロセスにロードされます。一般的な手法は、シェルコードをデータ(つまりバイトバッファ)として含み、データ関数ポインタまたはインラインアセンブリコードにエンコードされた命令に制御を移す小さなCプログラムを作成することです。別の手法としては、 shellcode_2_exeなどのオンラインツールを使用して、シェルコードを事前に作成された実行可能ハスクに埋め込み、それを標準のデバッガで分析する方法があります。iDefense sclogプロジェクト(元々は2005年にMalcode Analyst Packでリリース)のような、シェルコード分析に特化したツールも存在します。Sclogは、外部シェルコードファイルをロードし、APIロギングフレームワーク内で実行するように設計されています。クロスプラットフォームのlibemuパッケージの一部であるsctestアプリケーションのような、エミュレーションベースのシェルコード分析ツールも存在します。libemuライブラリをベースにした別のエミュレーションベースのシェルコード分析ツールとしてscdbgがあり、基本的なデバッグシェルと統合されたレポート機能が含まれています。