
LINKSYS AC1900 EA7500v3 IGD UPnP スタックバッファオーバーフロー リモートコード実行脆弱性
この脆弱性により、ネットワークに隣接する攻撃者は、影響を受けるLINKSYS EA7500ルーターのインストール環境で任意のコードを実行できます。この脆弱性を悪用するために認証は必要ありません。
この具体的な欠陥は、IGD UPnPサービスへのHTTPリクエストデータの処理に存在します。特定のSOAP UPnPアクションリクエストのユーザー指定変数の内容を解析する際、プロセスは固定長のスタックバッファにデータをコピーする前に、ユーザー指定データの長さを適切に検証しません。攻撃者はこの脆弱性を悪用して、root権限でコードを実行できます。
製品/ファームウェア: Linksys EA7500 ファームウェア 全バージョン(Ver.3.0.1.207964 を含む) - このバイナリを使用する他のルーターモデルおよびファームウェアバージョンも影響を受けます
UPnP IGDサービスのバイナリは /usr/sbin/IGD にあり、デフォルトでポート49152で実行されています。このサービスは IGDdevicedesc.xml UPnP XML記述ファイルを提供し、XML本文を含むHTTPリクエストを使用して呼び出すことができるいくつかの関数を詳細に説明しています。これらの関数の1つが SetDefaultConnectionService です。この関数には1つの文字列型変数が必要です。SetDefaultConnectionService 関数が呼び出されて実行されると、プログラムはこのバッファを含む strncpy 呼び出しを実行する前に、ユーザー指定変数の長さを検証しません。strncpy 呼び出しの送信元アドレスとコピーサイズの両方の変数はユーザーが制御可能で、コピーサイズはユーザー指定変数の長さになります。データは184バイトの固定バッファにコピーされ、スタックバッファオーバーフローの脆弱性が発生します。
SetDefaultConnectionService 関数は _set_connection_type としてラベル付けされています。この関数は184バイトのバッファを初期化し、次にリクエストに含まれるユーザー指定の文字列変数を保持するバッファへのポインタを取得します(NULLでない場合)。これは PAL_xml_node_GetFirstbyName を呼び出し、続いて PAL_xml_node_get_value を呼び出すことによって行われます。以下のコードを参照してください。
int _set_connection_type(int **param_1)
{
int iVar1;
char *var_value;
size_t var_value_length;
undefined uVar2;
undefined1 *puVar3;
char **ppcVar4;
undefined4 *puVar5;
char *pcVar6;
int *piVar7;
char acStack_d4 [184]; -----> /* 184バイトのバッファを初期化 */
memset(acStack_d4,0,0xb4);
iVar1 = PAL_xml_node_GetFirstbyName((*param_1)[0xf0],"NewConnectionType",0); -----> /* iVar1はユーザー指定の値を指すようになる */
if ((iVar1 != 0) && (var_value = (char *)PAL_xml_node_get_value(), var_value != (char *)0x0)) { -----> /* ユーザー指定の値が空でないことを確認し、そのポインタを取得 */
...
同じ関数の後半で、strlen 呼び出しが実行され、ユーザー指定の文字列のサイズに静的オフセット0x174を加えた値が取得されます。脆弱な状態は、その後の strncpy 呼び出しで発生します。この呼び出しでは、宛先引数が新しく初期化された184バイトバッファのアドレス、送信元がユーザー指定の文字列へのポインタ、コピー操作のサイズが strlen 呼び出しによって返されたユーザー指定の文字列のサイズに静的オフセット0x174を加えたものとなります。これにより、送信元アドレスとサイズ変数の両方がユーザーによって制御され、サイズ検証チェックが行われないため、バッファオーバーフローの脆弱性が発生します。以下のコードを参照してください。
int _set_connection_type(int **param_1)
{
...
var_value_length = strlen((char *)(iVar1 + 0x174)); ----> /* iVar1はユーザー指定の文字列へのポインタ */
strncpy(acStack_d4,(char *)(iVar1 + 0x174),var_value_length + 1); ----> /* 脆弱なstrncpy呼び出し */
...
スタック上の関数戻りアドレスを上書きするためのオフセットは276バイトです。次の4バイトを使用して実行を任意のアドレスにリダイレクトし、プログラムの制御フローを乗っ取ることができます。
より安全な文字列コピー関数を使用する: 宛先バッファのヌル終端を保証しない strncpy の代わりに、strncpy_s や memcpy_s などのより安全な文字列コピー関数を使用できます。これらの関数は、宛先バッファが常にヌル終端されることを保証し、バッファが保持できる以上のデータのコピーを許可しません。
入力検証: ユーザー入力を検証して、宛先バッファのサイズを超えないようにします。入力がバッファサイズより長い場合は、拒否するかバッファに収まるように切り詰めます。
動的に割り当てられたバッファを使用する: 固定サイズのスタックバッファの代わりに、宛先バッファ用のメモリを動的に割り当てます。このアプローチにより、入力データサイズに応じてバッファサイズを調整できます。(リソースが限られているシステムには推奨されません)
PoC 実行: python poc.py 192.168.1.1 49152
ソフトウェアダウンロードリンク: https://support.linksys.com/kb/article/559-en/ (ファームウェアバージョン Ver. 3.0.1.207964)
gdbserver-7.7.1-armel-eabi5-v1-sysv を使用しました)。pkill IGD を使用してバイナリを終了し、./gdbserver :1337 /usr/sbin/IGD を使用して再起動します。以下の .gdbinit ファイルを使用して、安定したデバッグセッションを開始できます。> set follow-fork-mode child
> set detach-on-fork off
> target remote 192.168.1.1:1337
> c