-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prevent bypassing safe guard in pathbuf segments #134
Conversation
Okay, let's get a fix in as soon as possible. I think that if a '/' character is in the URL-decoded segment, than it's an obvious attempt to subvert, and we can immediately bail. So, I think Rocket should simply reject that kind of string. But I'm not sure what others might expect. Is there ever a valid case to encode a '/' with |
I also cannot expect any usual case for that. I think if any user want this special case, then they can use Does the bail means 404 error? One more, does we also need to reject |
Bail in this case means that I think we should also reject '\'. |
Okay, I've gone through OWASP guide and have come up with the following implementation: /// Creates a `PathBuf` from a `Segments` iterator. The returned `PathBuf` is
/// percent-decoded. If a segment is equal to "..", the previous segment (if
/// any) is skipped.
///
/// For security purposes, if a segment meets any of the following conditions,
/// an `Err` is returned indicating the condition met:
///
/// * Decoded segment starts with any of: `.`, `*`
/// * Decoded segment ends with any of: `:`, `>`, `<`
/// * Decoded segment contains with any of: `/`, `\`
/// * Percent-encoding results in invalid UTF8.
///
/// As a result of these conditions, a `PathBuf` derived `FromSegments` is safe
/// to use as the suffix of a relative path without additional checks.
impl<'a> FromSegments<'a> for PathBuf {
type Error = SegmentError;
fn from_segments(segments: Segments<'a>) -> Result<PathBuf, SegmentError> {
let mut buf = PathBuf::new();
for segment in segments {
let decoded = URI::percent_decode(segment.as_bytes())
.map_err(|e| SegmentError::Utf8(e))?;
if decoded == ".." {
buf.pop();
} else if decoded.starts_with('.') {
return Err(SegmentError::BadStart('.'))
} else if decoded.starts_with('*') {
return Err(SegmentError::BadStart('*'))
} else if decoded.ends_with(':') {
return Err(SegmentError::BadEnd(':'))
} else if decoded.ends_with('>') {
return Err(SegmentError::BadEnd('>'))
} else if decoded.ends_with('<') {
return Err(SegmentError::BadEnd('<'))
} else if decoded.contains('/') {
return Err(SegmentError::BadChar('/'))
} else if decoded.contains('\\') {
return Err(SegmentError::BadChar('\\'))
} else {
buf.push(&*decoded)
}
}
Ok(buf)
}
} Does this look good to you? |
I think checking only |
We can make it so that the check happens only on Windows via |
Okey, that looks good. |
Thank you so much for finding and reporting this. You are absolutely awesome. |
Thank you for the fix! |
In #134, @tunz discovered that Rocket does not properly prevent path traversal or local file inclusion attacks. The issue is caused by a failure to check for some dangerous characters after decoding. In this case, the path separator '/' was left as-is after decoding. As such, an attacker could construct a path with containing any number of `..%2f..` sequences to traverse the file system. This commit resolves the issue by ensuring that the decoded segment does not contains any `/` characters. It further hardens the `FromSegments` implementation by checking for additional risky characters: ':', '>', '<' as the last character, and '\' on Windows. This is in addition to the already present checks for '.' and '*' as the first character. The behavior for a failing check has also changed. Previously, Rocket would skip segments that contained illegal characters. In this commit, the implementation instead return an error. The `Error` type of the `PathBuf::FromSegment` implementations was changed to a new `SegmentError` type that indicates the condition that failed. Closes #134.
In #134, @tunz discovered that Rocket does not properly prevent path traversal or local file inclusion attacks. The issue is caused by a failure to check for some dangerous characters after decoding. In this case, the path separator '/' was left as-is after decoding. As such, an attacker could construct a path with containing any number of `..%2f..` sequences to traverse the file system. This commit resolves the issue by ensuring that the decoded segment does not contains any `/` characters. It further hardens the `FromSegments` implementation by checking for additional risky characters: ':', '>', '<' as the last character, and '\' on Windows. This is in addition to the already present checks for '.' and '*' as the first character. The behavior for a failing check has also changed. Previously, Rocket would skip segments that contained illegal characters. In this commit, the implementation instead return an error. The `Error` type of the `PathBuf::FromSegment` implementations was changed to a new `SegmentError` type that indicates the condition that failed. Closes #134.
Problem
In the example static_file, there exist a LFI vulnerability. An attacker can read any file from the server.
For example,
Invoking the
curl
command may show you the content of/etc/passwd
that must not be disclosed.It seems that Rocket is already filtering
..
for PathBuf in params.rs. But, URI decode is invoked after segmenting, so the filtering can be bypassed.Fix
I fixed it by creating another segments for each URI decoded segment. Since I'm new to Rust and Rocket, I'm not sure if it is the best solution or not. I think we can call URI decode before segmenting, but it concerns me to make a big change.
Feel free to give me any feedback.