Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2021-4045 — TP-Link Tapo C200カメラ(CVE-2021-4045)向けのコマンドインジェクションエクスプロイト。UARTを介したルートシェルアクセスと、リバースエンジニアリングされたuhttpdバイナリ分析を提供します。 | Kitploit
ツール/GitHubGitHub/kaleth4/cve-2021-4045
組み込みシステムセキュリティIoTセキュリティ脆弱性分析エクスプロイトリバースエンジニアリングハードウェアハッキングペネトレーションテストコマンド&コントロールファームウェア解析
GitHubkaleth4/cve-2021-4045

CVE-2021-4045

TP-Link Tapo C200カメラ(CVE-2021-4045)向けのコマンドインジェクションエクスプロイト。UARTを介したルートシェルアクセスと、リバースエンジニアリングされたuhttpdバイナリ分析を提供します。

2ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
リポジトリを見る

🔍 CVE-2021-4045: TP-Link Tapo C200 におけるコマンドインジェクションの脆弱性

image

CVE-2021-4045


📌 概要

CVE-2021-4045 は、TP-Link Tapo C200 カメラで発見された コマンドインジェクション の脆弱性であり、攻撃者が root 権限でデバイスを完全に制御 することを可能にします。この脆弱性は ファームウェアバージョン 1.1.16 Build 211209 Rel. 37726N より前のすべてのバージョン に影響します。

🔗 INCIBE の公式通知: https://www.incibe.es/incibe-cert/alerta-temprana/vulnerabilidades/cve-2021-4045 (実際のリンクに置き換えてください)

🔧 推奨される対策: ファームウェアをバージョン 1.1.16 以上 にアップデートしてください。



🛠 初期認識

デバイスの設定と機能

  • 高度な機能を備えた低価格IPカメラ(約30€):
    • SDカードへの録画。
    • 水平 360°、垂直 90° の回転。
    • モバイルアプリからのリアルタイム音声再生。

ポートスキャン```bash

$ nmap -sV -p- 192.168.1.81

root@kitploit:~
**結果**:```
PORT     STATE SERVICE
443/tcp  open  https
554/tcp  open  rtsp
2020/tcp open  xinupageserver
8800/tcp open  sunwebadmin
image ご覧の通り、このデバイスには興味深いオープンポートがいくつかあります。最初に試したのはポート443でした。nmapは明らかにhttpsを使用していると示していますが、初期スキャン時にそれを見落としてしまい、ポート443がhttpを使用していると長時間思い込んでいました。そのため、https://192.168.1.81:443 ではなく http://192.168.1.81:443 だけを試してしまい、400応答しか得られませんでした。序文で述べたように、このプロセスは失敗の連続でした。他のポートに関しては、そこで実行されているサービスは全く未知のもので、明確な情報は見つかりませんでした。その時点で既知の選択肢は尽きてしまったので、さらに深く調査する時が来ました。

----[ シェルを取得する ]-------------------------------

カメラを購入する前に、インターネットでこのデバイスに関する以前の研究を検索しました。幸運なことに、人々がリバースエンジニアリングのために協力しているGitHubリポジトリを見つけました。そのIssueの一つに、UARTポートを介してコンソールにアクセスする方法が説明されており、当時私はそのことを全く知りませんでした。そこで基本を学び、接続するためにUSB-TTL変換器を購入しました。 image 前述のIssueの助けを借りて、ナイフとドライバーでデバイスを開け、すぐにUARTを見つけることができました。数回の試行と多くの忍耐の後、ようやくパッドにワイヤーをはんだ付けできました。

image

次に、はんだ付けがデータ転送に十分かどうかを確認する時が来ました。ワイヤーをUSBアダプターに接続しました。UARTのRxはアダプターのTxに、その逆も同様になるように注意し、アダプターをコンピューターに接続しました。再び、前述のIssueのおかげで、シリアル接続のボーレートが57600であることを知っていたので、次のコマンドを実行しました。

$ sudo screen /dev/tty.usbserial-0001 57600

ここで、'/dev/tty.usbserial-0001'はアダプターが接続され、デバイスに電源を供給しているUSBポートです。すぐにデータを受信し始めました。素晴らしい。

しかし、まだコンソールへのアクセスは得られていませんでした。受信していたのは単にデバイスの起動シーケンスであり、実際にはU-Bootブートローダーでした。次のようなものでした:

U-Boot 2014.01-v1.2 (Jul 16 2021 - 18:41:10)

Board: IPCAM RTS3903 CPU: 500M :rx5281 prid=0xdc02 force spi nor mode DRAM: 64 MiB @ 1066 MHz Skipping flash_init Flash: 0 Bytes flash status is 0, 0, 0 SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB Using default environment

Autobooting in 1 seconds copying flash to 0x81500000 flash status is 0, 0, 0 SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB SF: 8388608 bytes @ 0x0 Read: OK

[...]

Enterキーを押すと、ユーザー名とパスワードの入力を求められます。そのGitHub Issueのおかげで認証情報がわかっているので、ユーザー'root'、パスワード'slprealtek'で正常にログインし、ついにコンソールにアクセスできました。

接続が機能することを確認した後、はんだ付けを強化する必要がありました。ケースを組み立てる過程で2回も壊れてしまったからです。ホットメルトシリコンを塗布してすべてのワイヤーを固定し、すべてのモーターを外してデバイスを閉じました。これでテストユニットの準備が整いました。

image

----[ デバイスを探索する ]--------------------------

ケースができたので、デバイスを探索してみましょう:

root@SLP:~# uname -a Linux SLP 3.10.27 #1 PREEMPT Wed Nov 11 20:42:05 CST 2020 rlx GNU/Linux

root@SLP:~# cat /etc/openwrt_version 12.09-rc1

ご覧の通り、これはOpenWRTマシンで、Linux 3.10.27を実行しています。次に、アクティブなプロセスとオープンポートを確認してみましょう:

root@SLP:~# ps PID USER VSZ STAT COMMAND 1 root 2328 S init 2 root 0 SW [kthreadd] 3 root 0 SW [ksoftirqd/0] 4 root 0 SW [kworker/0:0] 5 root 0 SW< [kworker/0:0H] 6 root 0 SW [kworker/u2:0] 7 root 0 SW [rcu_preempt] 8 root 0 SW [rcu_bh] 9 root 0 SW [rcu_sched] 10 root 0 SW< [khelper] 11 root 0 SW< [writeback] 12 root 0 SW< [bioset] 13 root 0 SW< [kblockd] 14 root 0 SW [khubd] 15 root 0 SW [kworker/0:1] 16 root 0 SW [kswapd0] 17 root 0 SW [fsnotify_mark] 18 root 0 SW< [crypto] 27 root 0 SW [kworker/u2:1] 46 root 0 SW< [deferwq] 47 root 0 SW< [kworker/0:1H] 247 root 2328 S -ash 262 root 0 SW [irq/27-gpio res] 273 root 0 SW< [cryptodev_queue] 282 root 860 S /sbin/hotplug2 --override --persistent --set-rules-f 304 root 888 S /sbin/ubusd 325 root 8152 S tp_manage 357 root 3416 S /usr/bin/ledd 361 root 3408 S /sbin/msglogd 367 root 3220 S /usr/sbin/netlinkd 370 root 5468 S < /usr/bin/system_state_audio 379 root 10180 S /usr/sbin/wlan-manager 491 root 1636 S /sbin/netifd 492 root 1520 S /usr/sbin/connModed 494 root 11488 S /usr/bin/dsd 496 root 1532 S /usr/sbin/connModed 502 root 7640 S /bin/cloud-service 520 root 4360 S /bin/cloud-brd -c /var/etc/cloud_brd_conf 653 root 15020 S /bin/cloud-client 830 root 2320 S /usr/sbin/telnetd -b 127.0.0.1 861 root 3852 S /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C 870 root 6048 S /usr/bin/relayd 872 root 5948 S /usr/bin/rtspd 879 root 4612 S /usr/bin/p2pd 884 root 11152 S /bin/dn_switch 889 root 4180 S /bin/storage_manager 920 root 40940 S /bin/cet 956 root 32336 S /bin/vda 960 root 3808 S /bin/wtd 970 root 11288 S /bin/nvid 1019 root 2332 S udhcpc -p /var/run/static-dhcpc.pid -s /lib/netifd/s 1037 root 0 SW [RTW_CMD_THREAD] 1059 root 1212 S wpa_supplicant -B -Dwext -iwlan0 -P/tmp/supplicant_p 1089 root 2332 S /usr/sbin/ntpd -n -p time.nist.gov -p 133.100.9.2 -p 1103 root 3840 S /usr/bin/motord 1447 root 2324 R ps

root@SLP:~# netstat -natpu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8800 0.0.0.0:* LISTEN 920/cet tcp 0 0 127.0.0.1:929 0.0.0.0:* LISTEN 875/p2pd tcp 0 0 0.0.0.0:20002 0.0.0.0:* LISTEN 325/tp_manage tcp 0 0 0.0.0.0:2020 0.0.0.0:* LISTEN 969/nvid tcp 0 0 0.0.0.0:554 0.0.0.0:* LISTEN 920/cet tcp 0 0 127.0.0.1:23 0.0.0.0:* LISTEN 832/telnetd tcp 0 0 127.0.0.1:921 0.0.0.0:* LISTEN 878/relayd tcp 0 0 127.0.0.1:922 0.0.0.0:* LISTEN 877/rtspd tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 863/uhttpd tcp 0 0 192.168.1.80:37380 52.19.66.90:443 ESTABLISHED 507/cloud-brd udp 0 0 0.0.0.0:20002 0.0.0.0:* 325/tp_manage udp 0 0 0.0.0.0:38000 0.0.0.0:* 1087/ntpd udp 0 0 0.0.0.0:3702 0.0.0.0:* 969/nvid

nmapスキャンで見られたオープンポートの背後にあるプロセス、例えばuhttpdやcetなどが確認できます。私は特にuhttpdプロセスに注目しました。なぜなら、それがhttpsサーバー(その時点ではまだhttpだと思っていました)の背後にあり、httpプロトコルにすでに非常に精通していたからです。

uhttpdは、OpenWRTがこのディストリビューションを実行する組み込みデバイスで使用するために作成したウェブサーバーです。この時点で、ソースコードや少なくともパスなど、それに関する詳細情報を入手できるかどうかを知りたくなりました。OpenWRTのWikiを訪れ、uhttpdとOpenWRT全般について学びました。OpenWRTマシンには、統合設定インターフェース(UCI)と呼ばれるシステムが存在し、基本的にシステムサービスを簡単に設定するために使用されます。これを使って、uhttpdの設定を取得できます:

root@SLP:~# uci show | grep uhttpd ucitrack.@uhttpd[0]=uhttpd ucitrack.@uhttpd[0].init=uhttpd uhttpd.main=uhttpd uhttpd.main.listen_https=443 uhttpd.main.home=/ww uhttpd.main.rfc1918_filter=1 uhttpd.main.max_requests=8 uhttpd.main.cert=/tmp/uhttpd.crt uhttpd.main.key=/tmp/uhttpd.key uhttpd.main.cgi_prefix=/cgi-bin uhttpd.main.lua_prefix=/luci uhttpd.main.lua_handler=/usr/lib/lua/luci/sgi/uhttpd.lua uhttpd.main.script_timeout=180 uhttpd.main.network_timeout=180 uhttpd.main.tcp_keepalive=0 uhttpd.px5g=cert uhttpd.px5g.days=3600 uhttpd.px5g.bits=1024 uhttpd.px5g.country=CN uhttpd.px5g.state=China uhttpd.px5g.location=China uhttpd.px5g.commonname=TP-Link upnpc.uhttpd=entry upnpc.uhttpd.proto=TCP upnpc.uhttpd.ext_port=80 upnpc.uhttpd.desc=uhttpd

ここにいくつかの興味深いパラメータがあります。まず、'uhttpd.main.home'はサーバーのルートディレクトリを指しているので、ウェブサーバーのファイルが見つかるかもしれません。次に、'uhttpd.main.lua_handler'は、サーバー起動時にLua実行環境を初期化するために使用されるLuaハンドラスクリプトを指しています。uhttpdはLuaスクリプトをサポートしているため、そこにもっと興味深いファイルがある可能性があります。しかし、'/www'ディレクトリは空で、'/usr/lib/lua/luci'には'sgi'ディレクトリはなく、システムに'uhttpd.lua'ファイルも存在しませんでした。このuhttpdインスタンスがどのように動作するかについての情報を見つけようとしましたが、何も見つかりませんでした。どこも指していない設定パラメータがあるだけでした。

この時点で、解決策はuhttpdバイナリを直接解析してリバースエンジニアリングを適用することだと分かりましたが、その前にテスト環境を作成して、リクエスト時にウェブサーバー内部で何が起こっているかを知りたかったのです。というのも、プロセスの作成方法から、どこにも出力がなかったからです。

psコマンドの出力にあったプロセス861のコマンドを実行しようとしました:

$ /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C

しかし、多くのエラーが発生し、動作させることができませんでした。異なるポートで同じuhttpdプロセスを作成できなかったため、'/proc'エントリを調べて不足している出力を探そうとしました。存在すればそれらを読み取るためです(PwnFunctionのこのビデオで説明されています)。しかし、大きな問題がありました:

root@SLP:~# sudo ls -l /proc/864/fd/ lrwx------ 1 root root 64 Nov 10 22:44 0 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 1 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 2 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 3 -> anon_inode:[eventpoll] lrwx------ 1 root root 64 Nov 10 22:44 4 -> socket:[1830]

'stdin'、'stdout'、'stderr'のすべてのファイル記述子が'/dev/null'にリダイレクトされており、基本的にブラックホールにリダイレクトされていて、見つけることができませんでした。行き詰まってしまい、何をすればいいのかわかりませんでした。すでに'/proc'エントリにいたので、調査を始めました。というのも、'/proc'エントリにはプロセスに関するこんなに多くの情報があることを覚えておらず、好奇心が湧いたからです。この偶然の好奇心のおかげで、'environ'エントリにたどり着きました。そこにはそのプロセスのすべての環境変数が含まれています。その環境変数の一つは次の通りでした:

UHTTPD_ARGS=-h /www -T 180 -A 0 -n 8 -R -r C200 -C /tmp/uhttpd.crt -K /tmp/uhttpd.key -s 443

すぐに、psに表示されたコマンドが正しくないことに気づき、その後、UARTインターフェースの幅がすべての文字を表示するのに十分ではなかったためだとわかりました。またもや失敗でしたが、重要な教訓を得ました:UARTポートの出力を決して信頼してはいけないということです。

これで、同じパラメータで、'/dev/null'へのパイプなしで別のuhttpdインスタンスを作成し、リバースエンジニアリング中にバイナリをテストできるようになりました。

----[ Ghidra を使った uhttpd のリバースエンジニアリング ]--------

今回が Ghidra を初めて使用する機会でした。いくつかのビデオを見たり、記事を読んだりしていました(stacksmashing と liveoverflow の素晴らしく理解しやすいコンテンツに感謝します)が、実際に使ったことはなかったので、これは学ぶのに非常に良い機会でした。

uhttpd バイナリを開き、何度かの試行の後、言語が MIPS32、リトルエンディアン、mips16e であることがわかりました。一部の関数名はバイナリにデフォルトで付いていましたが、他のものはありませんでした。また、関数の名前変更にも時間を費やしました。どうやら Ghidra は外部関数と混同することが多く、それらに対する奇妙なラッパーが生成されるからです。例えば次のようなものです:

image `main()` 関数やその他の重要な関数を分析して、バイナリのロジックとその構造を理解しました。いくつかの興味深い既に特定された関数が見つかりました。その中には `do_login()` や `uh_slp_proto_request()` がありました。後者については後で詳しく説明します。

この最初の接触の後、バグを探し始めました。私はオーバーフロー脆弱性については完全な初心者なので、最初に行ったことは、system()、exec()、popen() の呼び出しを探して、簡単に悪用できるコマンドインジェクション脆弱性が存在するかどうかを確認することでした。そして、なんと幸運なことでしょう。

関数 'exec_and_read_json()' は 'popen()' を使用してコマンドを実行します:

ejecutar_y_leer_json

関数 'exec_and_read_json()' は、私が 'set_language()' および 'wifi_connect()' と名付けた2つの名前のない関数によって使用されています。これらの関数はそれぞれ、言語設定とWi-Fi接続(明らかに)を処理します。'wifi_connect()' はシングルクォート (') を解析しているように見えますが、'set_language()' は解析していません。つまり、'set_language()' 関数の入力を制御できれば、独自のコマンドを正常に注入できるということです:

conexión wifi establecer_idioma

関数 'set_language()' は、先ほど言及した 'uh_slp_proto_request()' によって使用され、この関数はユーザーから受信した解析されたデータを入力として渡します。

función_principal_1 función_principal_2

ユーザーデータを解析するために、uh_slp_proto_request() はそれが有効なJSONオブジェクトかどうかをチェックします。次に、キー method で識別される文字列値と、キー params で識別される辞書値を取得します(少なくとも私はそう考えています。Ghidra が関数呼び出しを解決できなかったためですが、このように動作しているように見えました)。選択されたメソッドに応じて、uh_slp_proto_request() は実行する関数を選択します。

それでは、次のペイロードを送信すると:{"method": "setLanguage", "params":{}}

正しく'set_language()'関数を呼び出し、'language_json'パラメータとして'{}'を渡します。次に、'set_language()'内部で、'language_json'オブジェクトは文字列に変換され、"ubus call system_state_audio set_language '%s'"に直接挿入されて実行されます。

このペイロードを送信すると:{"method": "setLanguage", "params": {"payload": "'; touch poc;'"}}

以下が実行されます。

ubus call system_state_audio set_language '{"payload": "'; touch poc;'"}'

実際には3つのコマンドが含まれています:

ubus call system_state_audio set_language '{"payload": "' touch poc '"}'

2つ目のコマンドにより、完全なコード実行が可能になります。

では、関数 uh_slp_proto_request() は、すべてのリクエストを処理する別の無名関数(私が main_server_function() と名付けました)によって使用されます。リクエストが有効(最大長を超えず、サーバー設定に従って http または https を使用するなど)である場合、main_server_function() はURLに /cgi-bin/luci または /web-static が含まれているかをチェックします。そうでない場合、uh_slp_proto_request() が呼び出されます。

uh_slp_proto_request_entrypoint

テストを行い、カメラにいくつかのリクエストを送信すると、uh_slp_proto_request() が使用するデータが標準的なPOSTデータであることが確認できます。したがって、前記のペイロードを含むPOSTリクエストを / に送信すると、uh_slp_proto_request() がこのデータを処理し、set_language() を呼び出し、ペイロードが exec_and_get_result() によって実行されるコマンドに注入されます。

ご覧のとおり、認証については何も触れていません。なぜなら、setLanguage() 関数はログインなしで呼び出せるからです。これにより、任意のユーザーが認証なしで1回のリクエストでカメラを完全に制御できるようになります。

----[ エクスプロイト ]----------------------------------

さて、エクスプロイトを作成する時です。netcatを使用してリバースシェルを取得する方法を調べるのに時間を費やしました。簡単そうに見えましたが、うまくいきませんでした。BusyBoxに搭載されているnetcatのバージョンは機能がかなり制限されており、従来のリバースシェルは使えませんでした。しかし、PayloadsAllTheThingsリポジトリ(いつものように)で探していたものを見つけ、完璧なリバースシェルを手に入れました。uhttpdはrootとして実行されているため(TP-Linkのおかげで)、悪意のあるPOSTリクエストを送信するだけで特権シェルが得られます。エクスプロイトはGitHubページで入手できます: https://github.com/hacefresko/CVE-2021-4045/blob/master/pwntapo.py image

🚨 学んだ教訓:

  • 443ポートで https と http を混同し、エラーに気づくまで時間を無駄にしました。
  • 2020、554、8800 ポートのサービスは不明であり、追加の調査が必要でした。

🔓 シェルの取得

UART経由のコンソールアクセス

  1. 事前調査:
    • デバイスのリバースエンジニアリングに関する情報を含むGitHubリポジトリを見つけました。
    • USB-シリアル変換アダプタを使用してUARTポートにアクセスする方法を学びました。
ツールをダウンロード