mirror of
https://github.com/xroche/httrack.git
synced 2026-07-23 17:19:17 +03:00
treathead() parsed the Content-Type and Content-Encoding response-header
values with a width-less sscanf("%s") into a 1100-byte stack scratch,
bounds-checking the result only after the copy. The receive path in
back_wait hands treathead a line buffer of up to 2000 bytes (htsback.c
rcvd[2048], binput cap 2000), so a hostile server sending a header value
longer than 1099 non-whitespace bytes overflowed the stack. Bound both
scans to the buffer (%1099s), matching the existing %31s/%255s idiom in
the request-line parsers.
The Content-Encoding branch also ignored the sscanf return, so an empty
value (e.g. "Content-Encoding:") left tempo uninitialized and then ran
strlen() over it: an uninitialized read that can also over-read past the
buffer. The Content-Type branch already guarded this; mirror it.
Found via the P3-3 static-analysis baseline. New -#test=headerlong plus
empty-value cases in 01_engine-header.test build the hostile inputs in
the handler (the CLI caps argument length); they abort on the pre-fix
binary under ASan (overflow) and MSan (uninitialized read).
Signed-off-by: Xavier Roche <roche@httrack.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
43 lines
1.5 KiB
Bash
43 lines
1.5 KiB
Bash
#!/bin/bash
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# Response header-line parsing (treathead via -#test=header <raw-line> ...).
|
|
# Isolates the wire layer from url_savename, which strips traversal on its own.
|
|
|
|
hdr() {
|
|
local want="$1"
|
|
shift
|
|
out="$(httrack -O /dev/null -#test=header "$@" | grep '^contenttype=')"
|
|
test "$out" == "$want" || {
|
|
echo "FAIL: $* -> '$out' (want '$want')"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
hdr 'contenttype=application/pdf cdispo=' 'Content-Type: application/pdf'
|
|
|
|
# filename= is honored quoted or bare.
|
|
hdr 'contenttype= cdispo=report.pdf' \
|
|
'Content-Disposition: attachment; filename="report.pdf"'
|
|
hdr 'contenttype= cdispo=report.pdf' \
|
|
'Content-Disposition: attachment; filename=report.pdf'
|
|
|
|
# Path components in the filename are dropped on the wire (RFC 2616).
|
|
hdr 'contenttype= cdispo=evil.pdf' \
|
|
'Content-Disposition: attachment; filename="../../evil.pdf"'
|
|
|
|
# An over-long header value must not overflow the parse scratch (ASan aborts
|
|
# here on the pre-fix binary).
|
|
test "$(httrack -O /dev/null -#test=headerlong 'Content-Type:')" \
|
|
== 'contenttype_len=32 contentencoding_len=0'
|
|
test "$(httrack -O /dev/null -#test=headerlong 'Content-Encoding:')" \
|
|
== 'contenttype_len=0 contentencoding_len=0'
|
|
|
|
# An empty Content-Encoding yields no token: leave it empty, don't read the
|
|
# uninitialized scratch (MSan aborts here on the pre-fix binary).
|
|
enc() { httrack -O /dev/null -#test=header "$1" | grep '^contentencoding='; }
|
|
test "$(enc 'Content-Encoding:')" == 'contentencoding='
|
|
test "$(enc 'Content-Encoding: gzip')" == 'contentencoding=gzip'
|