正しいサーバーの乗っ取り方〜OS Command Injection編〜

正しいサーバーの乗っ取り方〜OS Command Injection編〜

初めまして。インフラエンジニアの田坂です。
先日OS Command Injection攻撃のテストをしてみたので内容を投稿したいと思います。

概要

セキュリティ等が正しく設定されていないLinuxサーバー上で運用されているアプリケーションに対して細工したURLを利用して、OS Command InjectionによりサーバーのOS側を操作出来るかテストします。
また、サーバーへのFirewall等の設定が正しく行われていない場合、OSへログイン可能となりサーバーそのものを奪取出来る事を確認します。

今回はテストであり、各種環境、設定等は、実際の運用サーバーの構成、監視等とは違いがありますのでご了承ください。
なお、実際のコマンドについてはセキュリティ上の観点から掲載致しません。

OS Command Injectionについてはこちらを参考に…

CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)
CWE-78 OSコマンドインジェクション

準備

サーバー準備

  • CentOS 7 minimal
  • JDK 1.8.0
  • Tomcat 7.0.x
  • 簡単なWebアプリケーション
    ※今回はサンプルでログイン画面の雛形を表示するだけのものを用意しました(画面サンプルで実際には「Submit」ボタンは機能しません)

不正ユーザーのパスワード設定用SHA-512ハッシュを準備

$6$4KevkAwZaLI1bZG/$jmDr8TfqL.fcl57GMGP8SO4I8iZDQx0Z2LgxH4BdF(~略~)

今回のOS、アプリケーションはデモのため下記の設定を行なっています。

  • OSインストール後にyum updateでパッケージを最新にする
  • SELinux無効化
  • visudo等でwheelグループへのsudo権限を付与
  • Tomcatの動作Userをwheelグループへ追加
  • TomcatはApache等と連携せず、Coyoteで8080ポートを利用
  • Firewallはアプリが利用する8080ポート、SSHポートのみ外部接続を全て許可とする
  • SSHのパスワードログイン許可(※許可しなくてもTomcatの実行ユーザーにroot権限があれば突破可能)
今回は簡単に準備できたLinuxとTomcatを使用していますが、C、C#、Ruby、PHP、Python、Perl等、他の言語でも同様に良くない設定や、起動方法、アプリケーションの実装等が存在するとこの攻撃手法が成立します。
特定のアプリケーション、ミドルウェアが悪いと言った誤解をされないようにご注意ください。

動作確認

実際にサーバーがOS Command Injectionを受け付けるかをテストしてみます

取り敢えず手っ取り早くTomcatを止めてみましょう!

まず、Tomcatが起動していることを確認します。

$ ps aux|grep [t]omcat
root 2620 0.1 13.0 1533696 132720 ? Sl 02:04 0:51 /usr/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/opt/apache-tomcat/endorsed -classpath /opt/apache-tomcat/bin/bootstrap.jar:/opt/apache-tomcat/bin/tomcat-juli.jar -Dcatalina.base=/opt/apache-tomcat -Dcatalina.home=/opt/apache-tomcat -Djava.io.tmpdir=/opt/apache-tomcat/temp org.apache.catalina.startup.Bootstrap start
$

ブラウザで細工したURL(CWE-78 参照)を使ってアクセスします。
Tomcatが停止したため、サイトへのアクセスが出来なくなりました!

実行例)
Javaのプログラムに対して、「java.lang.Runtime.getRuntime().exit(0);」を実行

$ ps aux|grep [t]omcat
$

はい。Tomcatが停止しました。ほぼ期待通りです。
本来のテストのためにTomcatを再び起動しておきます。

テスト開始!

ユーザーを作成してみます

では早速tmpuserと言うユーザーを作成してみます。
まず、ユーザーが居ない事を確認します。

$ grep tmpuser /etc/passwd
$

ブラウザで細工したURL(CWE-78 参照)を使ってアクセスします。
表示上変化は見られませんが実際にはユーザーが作成されています。

Shellでの実行例)
useradd tmpuser
再度確認すると作成されています。

$ grep tmpuser /etc/passwd
tmpuser:x:501:501::/home/tmpuser:/bin/bash
$

ついでにパスワードも設定しておきましょう。

ブラウザで細工したURL(CWE-78 参照)を使ってアクセスします。
やはり表示上変化は見られませんが先ほど作成したユーザーのパスワードが変更されています。

Shellでの実行例)
usermod -p $6$4KevkAwZaLI1bZG/$jmDr8TfqL.fcl57GMGP8SO4I8iZDQx0Z2LgxH4BdF(~略~) tmpuser

再度確認するとパスワードが設定されています。

$ sudo grep tmpuser /etc/shadow
tmpuser:$6$4KevkAwZaLI1bZG/$jmDr8TfqL.fcl57GMGP8SO4I8iZDQx0Z2LgxH4BdF(~略~):17544:0:99999:7:::
$

作成したユーザーをwheelグループへ追加しsudo権限を付与します

ユーザーの所属グループを確認します。

$ grep tmpuser /etc/group
tmpuser:x:501:
$

ブラウザで細工したURL(CWE-78 参照)を使ってアクセスします。
今回も、やはり表示上変化は見られませんが、wheelグループへ追加されています。

Shellでの実行例)
usermod -G wheel tmpuser

ユーザーがwheelグループに所属している事が確認出来ました。

$ grep tmpuser /etc/group
wheel:x:10:tmpuser
tmpuser:x:501:
$

作成したユーザーでサーバーへログイン!

今回は自分のMacから接続してみます

$ ssh tmpuser@192.168.10.46
The authenticity of host '192.168.10.46 (192.168.10.46)' can't be established.
RSA key fingerprint is c4:5f:48:ca:f9:(~略~):5f:2b:20:4f:5e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.10.46' (RSA) to the list of known hosts.
tmpuser@192.168.10.46's password:
[tmpuser@localhost ~]$

はい。接続できました。

sudoコマンドも実行してみます。

[tmpuser@localhost ~]$ sudo grep tmpuser /etc/shadow
tmpuser:$6$4KevkAwZaLI1bZG/$jmDr8TfqL.fcl57GMGP8SO4I8iZDQx0Z2LgxH4BdF(~略~):17544:0:99999:7:::
[tmpuser@localhost ~]$ sudo shutdown -h now
Broadcast message from tmpuser@localhost.localdomain
(/dev/pts/1) at 11:04 ...

The system is going down for halt NOW!
[tmpuser@localhost ~]$

はい。サーバーも停止できました。
これでユーザー作成や、ログイン履歴も消し放題です!

ちなみにユーザーが作成されたログはこんな感じで残ります。
知らない間に勝手にユーザーが作られています。怖いですね。。。

$ sudo tail /var/log/secure
Jun 13 10:09:26 localhost useradd[4352]: new group: name=tmpuser, GID=501
Jun 13 10:09:26 localhost useradd[4352]: new user: name=tmpuser, UID=501, GID=501, home=/home/tmpuser, shell=/bin/bash
Jun 13 10:16:05 localhost usermod[4380]: add 'tmpuser' to group 'wheel'
Jun 13 10:16:05 localhost usermod[4380]: add 'tmpuser' to shadow group 'wheel'
Jun 13 10:56:25 localhost usermod[4494]: change user 'tmpuser' password

もしパスワードでSSH出来ない場合

下記の項目を追加で設定しますが複雑になるため今回は割愛させて頂きます。

  • sshdの設定に、パスワードログインを許可する設定を追加
  • sshdを再起動

結果

セキュリティ、特に実行権限が正しく使用されていないサーバーに対して無事?にOS Command Injection攻撃が成立しました。
アプリケーションユーザーに設定されたsudo権限を悪用してサーバーを乗っ取る事が出来ました。

まとめ

アプリケーションを外部公開するサーバーではOSや、ミドルウェアの脆弱性、アプリケーションを実行するユーザーの権限や、実装には細心の注意を払いましょう。

実際に攻撃を行ってみることにより、攻撃者が何を行いたいのか、どこを攻撃したいのか等の部分が見えてきます。
最近は敷居が下がってきていて、自宅サーバーや、自前のクラウドサーバーを公開する方も増えてきていますが、攻撃されてしまった、乗っ取られてしまったという事が無い様に設定の見直しを行ってもらう機会になればと思います。

例えば、下記の様な事が考えられます。

  • SELinuxを有効化しておく(ポリシーの設定はしましょう!)
  • Firewallで不要なポートは閉じ、接続元のIP制限をしっかりしておく
  • アプリケーションの実行ユーザーは最小限度の権限に設定しておく
  • アプリケーションの実装を見直してみる
  • ミドルウェアを常に最新状態に保つ
  • 脆弱性発見ツール等を利用してみる
  • etc…

今回のテストでは「アプリケーションの実行ユーザーは最小限度の権限に設定しておく」と言う部分で、root権限を持たないtomcatと言うユーザーでアプリケーションを起動してみたところ、アプリケーションで下記のようなエラーとなりユーザーを作成する事は出来ませんでした。

java.io.IOException: Cannot run program "useradd": error=13, 許可がありません

これだけでかなりリスクの軽減が期待できます。

最後に

OS(特にLinux系)や、オープンソース、アプリケーション等のセキュリティに興味をお持ちの方、ぜひ一緒に働きませんか?
ご応募お待ちしております!