HTTP request smuggling primitive: bare LF accepted as a request-line terminator in strict mode
維護者通常 6 天內回覆
還沒有人認領這個 Issue。
評估
- 難度
- 4/5
- 預估耗時
- 3-5 天
- 新手友好度
- 55/100
- Issue 類型
- 缺陷
- 描述清晰度
- 描述清楚
- 活躍度
- 活躍
- 技術堆疊
- node.js, typescript
研究方向
從 url.ts:176 開始,追蹤 url.exit.toHTTP09 到 headers_start,將單獨 LF 的路徑與用於其他 request-line terminator 的 LENIENT_OPTIONAL_CR_BEFORE_LF 檢查進行比較。為 strict mode 下無版本和有版本的請求加入回歸覆蓋,然後確認 strict mode 會拒絕單獨 LF 的情況,且不會解析 HTTP/0.9 headers 或 body。
由索引模型根據 Issue 內容生成。
描述
llhttp_set_lenient_optional_cr_before_lf documents that llhttp "would error when a LF is not
preceded by CR when terminating the request line", and that relaxing this exposes request smuggling.
url.ts:176 exits on a bare \n straight to the HTTP/0.9 adapter with no
LENIENT_OPTIONAL_CR_BEFORE_LF check — the only line terminator in the grammar that is ungated.
url.exit.toHTTP09 then continues into headers_start with no http_minor guard, so the message is
labelled HTTP/0.9 but headers and a Content-Length body are still parsed.
PoC
const http = require('http'), net = require('net');
const srv = http.createServer((req, res) => { // default parser, no options
let body = '';
req.on('data', c => body += c);
req.on('end', () => {
console.log(` ACCEPTED HTTP/${req.httpVersion} ${req.method} ${req.url}` +
` headers=${JSON.stringify(req.headers)} body=${JSON.stringify(body)}`);
res.end('ok');
});
});
srv.on('clientError', e => console.log(` REJECTED ${e.code}`));
const cases = [
['versionless + bare LF', 'GET /x\nHost: a\r\nContent-Length: 5\r\n\r\nhello'],
['versioned + bare LF', 'GET /x HTTP/1.1\nHost: a\r\n\r\n'],
];
srv.listen(0, async () => {
for (const [name, raw] of cases) {
console.log(name);
await new Promise(done => {
const c = net.connect(srv.address().port, '127.0.0.1', () => c.write(raw));
c.on('close', done); c.on('error', done);
setTimeout(() => c.destroy(), 300);
});
}
srv.close();
});
versionless + bare LF
ACCEPTED HTTP/0.9 GET /x headers={"host":"a","content-length":"5"} body="hello"
versioned + bare LF
REJECTED HPE_INVALID_VERSION
The same terminator is rejected when a version is present and accepted when it is absent. The
accepted message is reported as HTTP/0.9 yet carries headers and a body, neither of which HTTP/0.9
defines.
Scope, stated plainly: this path sets keepalive=0, and a pipelined follow-up request is rejected
with HPE_CLOSED_CONNECTION, so on its own it is a divergence from the documented strict-mode
guarantee rather than a demonstrated desync. Two requests parse on one connection only with
lenient_keep_alive also enabled.
- 主要語言
- TypeScript
- 星號
- 2k
- 分支
- 236
- 平均合併
- 5 天 2 小時
- 30 天內合併 PR
- 1
環境準備
- 提供 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
nodejs/llhttp 的其他 Issue
-
難度 2/5 1-3 小時 新手友好度 72/100
維護者通常 6 天內回覆
-
難度 4/5 3-5 天 新手友好度 52/100
維護者通常 6 天內回覆
-
Not easy to get the number of methods llhttp has可能已有人在做 @official-burak 於 53 天前認領。 未關閉
難度 3/5 1-2 天 新手友好度 54/100
維護者通常 6 天內回覆
-
Should there be a way to block requests with multiple hosts or none are sent?可能已有人在做 @Vizonex 於 201 天前認領。 未關閉
難度 5/5 一週以上 新手友好度 32/100
維護者通常 6 天內回覆
-
難度 2/5 1-3 小時 新手友好度 45/100
nodejs/llhttp#782 · 2 則留言 · 4 個 reaction ·
維護者通常 6 天內回覆
相似的 Issue
-
難度 2/5 1-3 小時 新手友好度 72/100
grafana/profiles-drilldown#1212 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 78/100
grafana/traces-drilldown#980 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 76/100
openremote/openremote#3487 · 1 則留言 ·
-
triage:needs-human
難度 2/5 1-3 小時 新手友好度 67/100
garrytan/gbrain#7635 · 1 則留言 ·
維護者通常 1 天內回覆
-
難度 2/5 1-3 小時 新手友好度 82/100
coollabsio/jean#867 ·
維護者通常 1 天內回覆