qmailでmbox形式でメールを保存すると、特定環境下で外部からメールが消されてしまうのの対策+DoS喰らいにくくするパッチ 数年前(ソース解析時だから、2000年頃?)から気づいていたけれど、実証がめんどくさくて放置していた、qmailでmbox形式で保存する時に運が悪いとメールが消えてしまう事に対するパッチです。 んで、実証の時に、効率良くmboxを破壊するために風呂場で思いついたDoSが本当に通ってしまったので、その対策パッチも合わせて公開します。 まず、「qmailでmbox形式でメールを保存すると、特定環境下で外部からメールが消されてしまう」事についてです。 問題が起きるケースは次の条件の時です。 ・ファイルシステムが4G以上のファイルサイズを扱える ・32bit OSを使用。ただし、64bit OSでも、unsigned longが4byteの場合はアウト。 mbox形式のファイルが4Gを超えている状態で、qmail-localが(4Gを超えた)ファイルをいじっている時にエラーが起きると、ファイルが本来のサイズよりかなり小さくされてしまいます。 具体的には、オリジナルのqmail-local.cの204行目や225行目のseek_trunc(fd,pos);が実行されると問題が発生します。 何が起きているかというと、seek_posがseek.hにおいて、unsigned longと定義されている為、mboxのファイルサイズを記録している192行目あたりで(seek_pos posと定義されている)pos = seek_cur(fd);とやっちゃってる為、off_t(ファイルサイズを記録する為の型。ファイルシステムが4G以上のファイルを扱える場合は64bitである事が多い)をseek_posに代入、という処理が発生しています。 つまり、sizeof(seek_pos) = sizeof(unsigned long) < sizeof(off_t)となってしまい、posが32bitの表現限界を超えて0に戻ってしまうため、その後のエラー処理で元のサイズに戻そうとして、元のサイズの下位32bit分の大きさにしてしまうのです。このため、mboxの内容が消えるという現象が発生します。 ちなみに、Maildirを使っている時はseek_truncしないので、問題が顕在化しません。 この現象を起こすのに一番簡単な方法は、「qmail-localのヘッダ処理中にメモリ不足でエラーにする」コードです。これは(exploitコードを見てもらえば分かりますが)巨大なヘッダのメールを大量に投げると遠隔からでも簡単に発生することができます。 ……と思ってましたが、エラーメッセージを見たら、「Unable_to_write_/usr/home/mail/Mailbox:_out_of_disk_space._(#4.3.0)」だそうで。確かにこっちの方がエラー起きやすい(笑)DoSすればいいんだから簡単だねぇ。 ちなみに、問題の根本は、unsigned longを0〜∞の整数を扱えるごとく扱っている事です。そういえば、以前あったqmail-smtpdの2G超えの時にアクセス違反で落ちるのも同じ原因ですね(^^; なので、類似のバグは他にも何かありそう。探すのがめんどくさいので今回はこれだけ。 何か気づいた人は実証してパッチを公開しましょう(笑) mboxが壊れるのはFreeBSD 6.2 p8 + portsでデフォルトでインストールしたqmailで実証しました パッチも、多分動きます(笑) 簡単なテストはしましたが、問題なさげなので。 ただし、一切の保証をしません。自己責任でどうぞ。 次、DoS喰らいにくくするパッチです。 これは上で書いた「qmailでmbox形式でメールを保存すると、特定環境下で外部からメールが消されてしまう」を起こすのに、4G以上のメールボックスが必要で、効率よく4G超えのメールボックスを作るために「qmailでは複数のrcpt toがあると、全部ばらして配信する」という特徴を使ってファイルサイズを巨大化させた時に、DoS状態になったため、それを回避する為のパッチです。 DoS状態になる理由は簡単で、受け取ったメール本文をrcpt toが入力された回数展開して配信する為です。 exploitコードでは、rcpt toを1024回、本文を1Mで4回送信(つまり、1024*1M*4=4G!の配信が行われる)という送信方法をとっています。(実際にはヘッダが付加されるため、もっと小さくてもexploitが可能なはずですが、安全係数取ってあります) やっている事は簡単で、controlの中にmaxrcpttoというファイルに整数を書いておいておくと、その回数までしかrcpt toを受け取らなくなります。 常識的に考えて、10個も同時に来ないでしょうから、10とでも書いておけばかなりDoS状態になりにくくなります。 以上、qmailのパッチの説明でした。 おまけ:mbox-dos.pl.txt ローカルのroot宛に大量にメールを送ります。perlがあれば、多分実行できます。 &mail_send_mbox()が4Gオーバーにさせるための部分で、&mail_send_mbox2()が、メモリ不足を誘発しやすいメールを送る部分です。 あとは自力で読んでください。 (c)2007 Copyright, Ituki Kirihara/NI, All rights reserved. なお、本パッチ・expolitコードは使用、再配布、転載等、自由に行っていただいて結構ですが、出来るだけこのテキストも一緒に配布してください。と言うか、誰か英訳してくれ(笑) 著作権は放棄しませんが、よほど悪質なケースを除いて権利の行使をしません。 私のホームページ http://fc.to/ituki/ or http://blog.proj.jp/ituki/ パッチ公開場所 http://fc.to/ituki/software/patch/