Mono

miocatのその後、あるいはcoinsLT #0で発表した話

情報科学類1年が企画したcoinsLT #0(ATNDの方が情報量が多いのだが、一応Webページも存在している)で発表した。タイトルがクソ長いが要するにこのブログでいくつか投稿してきた、.NET FrameworkとMonoにおけるGetFullPathとかnew Uriの挙動の違いについてである。

coinsLT#0 tkbctf3 miocatができるまで from Mei Akizuru

実際の発表時の動画も公開されている。だいたい07:58あたりからが私の発表である。

Different Behaviors of DownloadString and Uri between .NET and Mono

Note: This is the summary of my posts (they are written in Japanese):

I already reported this as a bug for Xamarin’s Bugzilla.

Introduction

I found the different behavior of WebClient.DownloadString(String) between Mono and .NET Framework when an invalid URI passed to it. In the Mono’s implementation, it may cause a security issue.

This causes by two different behaviors, in new Uri(String), and Path.GetFullPath(String).

DownloadString

DownloadString(String) and some methods (e.g. DownloadFile(String), OpenRead(String), etc.) calls CreateUri(String), a private method of WebClient. (-> source code on github)

CreateUri and GetUri

CreateUri(String) tries to make an instance of Uri with new Uri(String). If an invalid URI passed, the constructor raises an exception. For example, new Uri("http://../../../etc/passwd") will be failed because its hostname part (..) is invalid. However, the failure will be ignored, and CreateUri(String) returns new Uri(Path.GetFullPath(String)). It means the local file address with the full path will be returned.

結局Monoと.NETの挙動の違いはなんだったのか

続・アレな文字をWebClient.DownloadString(String)に渡すとローカルのファイルが読める

ここ2つの記事でMonoのWebClient.DownloadString(string)にアレな文字列渡すとローカルファイルを落としてきてしまうという挙動について調べてたわけですが、よくよくスタックトレースを見てみると、.NET FrameworkでもGetUriというメソッドを経由してPath.GetFullPathが呼ばれていたことがわかりました。

MonoのWebClientにおけるURI

発端

発端は前の記事にあるように、tkbctf3の問題としてmiocatなるものを出してみたのはいいものの、意図とは異なる脆弱性を作り込んで250点問題が超絶ボーナス問題になりましたよ、というお話しです。

調査

miocatはC#で書かれており、実際の運用ではMonoランタイムで動いていました。というわけでMonoのソースコードを読めば解決です。やったね。

そういうわけでまずはWebClientの実装を読んでみたのですが、怪しい箇所が一発で見つかりました。WebClient.cs#798、privateメソッドであるCreateUri(string)なるメソッドです。DownloadString(string)は、その引数をこのメソッドに渡してDownloadData(Uri)を呼び出します。

try-catchの中で渡されたアドレス(と、baseAddress)を元にUriのインスタンスを作ってCreateUri(Uri)に渡していますが、ここで例外が発生するとreturn new Uri(Path.GetFullPath(address))という恐怖のコードが走ります。