Null bytes in url could cause some problems #39592
Copy link
Copy link
Closed
Labels
urlIssues and PRs related to the legacy built-in url module.Issues and PRs related to the legacy built-in url module.whatwg-urlIssues and PRs related to the WHATWG URL implementation.Issues and PRs related to the WHATWG URL implementation.
Description
Version
v16.6.0
Platform
Linux MAPLE 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 GNU/Linux
Subsystem
url
What steps will reproduce the bug?
There are two bugs about null byte:
const url = require('url')
const u = url.parse('http://[127.0.0.1\0c8763]:8000/')
console.log(u.hostname) // '127.0.0.1\0c8763'new URL('a\0b')And the error will be:
Uncaught TypeError [ERR_INVALID_URL]: Invalid URL
at __node_internal_captureLargerStackTrace (node:internal/errors:464:5)
at new NodeError (node:internal/errors:371:5)
at onParseError (node:internal/url:536:9)
at new URL (node:internal/url:612:5) {
input: 'a',
code: 'ERR_INVALID_URL'
The error input is apprently truncated by the null byte.
How often does it reproduce? Is there a required condition?
I think this could only happen when attacker is trying to bypass some SSRF filter in some scenario, but I think it is almost unlikely to happen in realworld.
const url = require('url')
const http = require('http')
const u = url.parse('http://[127.0.0.1\0.github.io]:8000/')
console.log(u)
if (!u.hostname.endsWith('.github.io')) {
console.log('Sorry, you can only fetch *.github.io')
process.exit(1)
}
http.request(
{
host: u.hostname, // null byte truncated
port: u.port,
path: u.path,
headers: {
Host: 'xx' // http will automatically set host header by default, and \0 will cause an error in header
}
},
msg => {
msg.on('data', data => {
console.log(data.toString())
})
}
)
.on('error', console.error)
.end()What is the expected behavior?
It should be invalid url, and http module shouldn't accept null byte.
What do you see instead?
Parsed successfully into a hostname with null byte.
Additional information
No response
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
urlIssues and PRs related to the legacy built-in url module.Issues and PRs related to the legacy built-in url module.whatwg-urlIssues and PRs related to the WHATWG URL implementation.Issues and PRs related to the WHATWG URL implementation.
Activity
RaisinTen commentedon Jul 31, 2021
url.parse()has a legacy status: https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehostWhy not just use the WHATWG URL API? It already classifies both urls as invalid ones.
targos commentedon Aug 9, 2021
@nodejs/url
Trott commentedon Mar 8, 2022
The second bug described here, where
new URL('a\0b')reports the input as'a'in the error message, originates in the C++ code. The null character gets treated as the end-of-string marker, but only in the error path. It gets encoded and handled correctly, for example, innew URL('http://example.com/a\0b'). (Safari and Firefox also handle that last one correctly, but Chrome throws. I believe Chrome is deviating from the spec, but even if I'm wrong about that, for the purposes of this conversation, it doesn't matter.)Possible solutions:
std::stringinstead ofchar *and then be very careful so we can preserve the NULL. (I suppose this approach might not work depending on the nature of how the string is handled elsewhere in the C++ code.)TimothyGu commentedon Mar 8, 2022
Chrome's behavior is indeed counter to the spec. https://crbug.com/1099721
Trott commentedon Mar 9, 2022
This is the approach used in #42263.
url: preserve null char in WHATWG URL errors
Trott commentedon Mar 11, 2022
The WHATWG URL error message issue has been fixed. The legacy
url.parse()has several possible solutions. I think the best is to bail on parsing when there is a NULL character (and possibly other C0 characters) in the host (and possibly a few other places?) and return an object with onlypath/pathname/hrefvalues set to anything other thannull.Trott commentedon Mar 11, 2022
https://url.spec.whatwg.org/#host-miscellaneous
Trott commentedon Mar 12, 2022
Looks like
forbiddenHostCharsinurl.jsomitsNULLeven though it includes tab, lf, cr, space, etc. from the list above. It also skips checking on IPv6 hostnames. These seems like bugs that should be fixed. PR coming soon....Trott commentedon Mar 12, 2022
#42313
url: throw on NULL in IPv6 hostname
url: throw on NULL in IPv6 hostname
url: preserve null char in WHATWG URL errors
url: preserve null char in WHATWG URL errors
url: preserve null char in WHATWG URL errors
url: preserve null char in WHATWG URL errors
url: throw on NULL in IPv6 hostname