January 28, 2020

How to Bypass Falco

falco のルールは、そこそこ柔軟に書けるのだが、故にバイパスできてしまうよという話。

1. /proc/self/root を使う

falco のデフォルトルールに /etc/shadow などが開かれたことを検知するルールがある。

抜粋するとこういうルール。

- list: sensitive_file_names
  items: [/etc/shadow, /etc/sudoers, /etc/pam.conf, /etc/security/pwquality.conf]

- macro: sensitive_files
  condition: >
    fd.name startswith /etc and
    (fd.name in (sensitive_file_names)
     or fd.directory in (/etc/sudoers.d, /etc/pam.d))

- rule: Read sensitive file untrusted
  desc: >
    an attempt to read any sensitive file (e.g. files containing user/password/authentication
    information). Exceptions are made for known trusted programs.
  condition: >
    sensitive_files and open_read and proc_name_exists

これは fd.name/etc/shadow の場合に検知するので、 /proc/self/root/etc/shadow のようなパスを指定されると、このルールをバイパスされる。

2. $() を使う

例えば、PHP から system() を使って git が使われるのを検知するシナリオを考える。
一つ方法として思いつくのは proc.cmdlinesh -c git から始まるかをチェックすることだろう。

これもデフォルトルールにマクロとしてある。

もうちょっと分かりやすく書くとこう。

- macro: spawned_process
  condition: evt.type = execve

- rule: Spawn git process from php
  desc: Spawn git process from php
  condition: proc.pname=php and spawned_process and proc.cmdline startswith "sh -c git"
  output: Spawn git process (command=%proc.cmdline pid=%proc.pid user=%user.name %container.info image=%container.image)
  priority: WARNING

しかし、この場合、 $() を使うことでバイパスできる。

# 検知される
$ php -r 'system("git --version")';
# 検知されない
$ php -r 'system("$(echo \"git --version\")");'

3. # コメントを使ったバイパス

OS コマンドインジェクションを検知するために、PHP から生成されるプロセスを検知したいシナリオを考える。
ただし、バッチスクリプト( run-job.sh )を system() 経由で実行しているという背景があり、これに関しては除外したいとする。

この場合、思いつくルールは次のような感じ。

- macro: batch_job
  condition: (proc.cmdline contains "run-job.sh")

- rule: Spawn processes from php
  desc: Spawn processes from php
  condition: proc.pname=php and spawned_process and not batch_job
  output: Spawn process (command=%proc.cmdline proc.name=%proc.name pid=%proc.pid user=%user.name %container.info image=%container.image)
  priority: WARNING

ここで、実際に PHP に OS コマンドインジェクションがあった場合、次のようにコメントを含めることで proc.cmdlinewhoami;# run-job.sh となり、 proc.cmdline contains "run-job.sh" でマッチするのでバイパス可能。

# 検知される
<?php
  $user_input = "whoami";
  system($user_input);
?>

# 検知されない
<?php
  $user_input = "whoami;# run-job.sh";
  system($user_input);
?>

まとめ

/proc/self/root などのシンボリックリンクを使うことで fd.name に関してはバイパス可能である。
そのため、そのシンボリックリンクも検知対象に含めなければ完全な検知は不可能。

また、 proc.cmdline (だけに限らないが)に関しても前述したようなバイパス手段があるため、そのような場合も考慮したルールを書いたりしなければならない。

References

このエントリーをはてなブックマークに追加

© Kouhei Morita 2018