
https://hackerone.com/reports/863553
/appsuite/api/oxodocumentfilter&action=addfile の処理時に適用されるURL検証ロジックには、Time of Check Time of Use (ToCToU) SSRF攻撃を実行するために使用できる3つの欠陥があります。この問題により、悪意のある攻撃者は内部ネットワークサービスに対してHTTP GETリクエストを実行でき、保護された情報の漏洩につながる可能性があります。
URITools.getFinalURL メソッドのURL検証ロジック(こちらで実装)にはToCToU脆弱性があります
URL u = new URL(url);
if (validator.isPresent()) {
Optional<OXException> exception = validator.get().apply(u);
if (exception.isPresent()) {
throw exception.get();
}
}
URLConnection urlConnnection = u.openConnection();
上記のコードでは、チェック時 (validator.get().apply(u)) と使用時 (u.openConnection()) の両方で、InetAddress.getByName メソッドを呼び出すことにより、入力URLのホスト名のDNS解決が行われます。したがって、攻撃者がチェック後かつ使用前にDNS解決結果を変更できれば、バリデーターをバイパスできます。この欠陥だけでは、以下の制限により極めて悪用が困難です。
InetAddress.getByName はDNS解決が成功した場合30秒間、失敗した場合10秒間、DNS解決結果をキャッシュします。validator.get().apply(u) と u.openConnection() の間の時間間隔は非常に短い(通常のURLでは1ミリ秒未満)。欠陥#2はこの制限をバイパスするために使用されます。URLバリデーター AddFileAction.validator(こちらで実装)は入力URLの長さを検証しません。大きなサイズのURLを提供することで、攻撃者はチェック時と使用時の InetAddress.getByName メソッド呼び出しの間の時間間隔を増やすことができます。時間間隔が大きくなると、攻撃者は欠陥#1を容易に悪用できるようになります。
HostList.contains メソッド(こちらで実装)は、入力URLのホスト名のDNS解決が失敗した場合にfalseを返します
// Need to resolve as last resort
try {
return contains(InetAddress.getByName(toCheck), false);
} catch (UnknownHostException e) {
// Cannot be resolved
return false;
}
AddFileAction.validator は blackImageUrlHostlist.contains メソッドを呼び出して、入力URLのホスト名または解決されるIPアドレスがブラックリストに含まれているかどうかを確認します。攻撃者がホスト名のDNS解決を失敗させると、IPアドレスのチェックがスキップされ、URLが受け入れられます。
攻撃者が制御する権威ネームサーバーを持つホスト名 randomid.dns.pointer.pw を考えます。攻撃戦略は、チェック時 (validator.get().apply(u);) に randomid.dns.pointer.pw のDNS解決を失敗させ、使用時 (u.openConnection()) に成功させることです。これにより、攻撃者はIPアドレスのブラックリストフィルターをバイパスし、ブラックリストに載っている任意のIPアドレスに対してHTTP GETリクエストを実行できます。攻撃者は以下の手順に従ってこの攻撃を実行する可能性があります。
InetAddress.getByName キャッシュをトリガーするために、リクエストデータ {"add_imageurl": "http://randomid.dns.pointer.pw"} で addfile アクションを実行します。このとき、権威DNSサーバーは randomid.dns.pointer.pw のDNS解決を SERVFAIL ステータスを返して失敗させるように設定されます。InetAddress.getByName はこの失敗結果を10秒間キャッシュします。pointer.pw の権威ネームサーバーに、ホスト名 randomid.dns.pointer.pw のタイプAのDNSリクエストに対して 127.0.0.1 を返すように指示します。InetAddress.getByName がチェック時にはキャッシュされた応答(SERVFAIL)を返し、使用時(url.openConnection())には127.0.0.1を返すようにします。add_imageurl 値を http://u{25MB}:[email protected]/path/to/internal/resource.png の形式に設定してSSRFリクエストを実行します。ここでu{25MB}は、ユーザー名部分が25 x 1024 x 1024文字の長さの文字列であることを意味します。https://golang.org/dl/ からgolangをインストールします
少なくとも4GBのRAMを搭載した仮想マシンに、ガイド https://oxpedia.org/wiki/index.php?title=AppSuite:Open-Xchange_Installation_Guide_for_Debian_9.0 および https://oxpedia.org/wiki/index.php?title=AppSuite:Documents_Installation_Guide#Debian_GNU.2FLinux_9.0_.28valid_from_v7.10.29 に従ってOpen-XchangeとDocumentsをインストールします
poc.zipファイルをダウンロードして展開します
ターミナル/コマンドラインを開き、カレントディレクトリを展開したpoc.zipフォルダに設定します
コマンドを実行します
go run . -serverRoot="http://172.16.66.130" -username="testuser" -password="secret" -targetPath="appsuite/v=7.10.3-9.20200409.083030/apps/themes/logo.png" -targetPort="80" -payloadSize=25 -startSleepDuration=6600
ここで 172.16.66.130 はVMのIPアドレスです。