Skip to content
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

Go: Add and Modify Sanitizers For TaintedPath #11703

Merged
merged 5 commits into from
Apr 10, 2024

Conversation

Kwstubbs
Copy link
Contributor

This PR adds some additional sanitizers to the TaintedPath Customization due to a high number of false positives I have found after doing some research.
I have added:
filepath.Base
strings.ReplaceAll
http.ParseMultipartForm
I have also removed path.Clean as it does not sufficiently clean paths for software that can run on Windows.
Please see: labstack/echo#1718 as an example.

@Kwstubbs Kwstubbs requested a review from a team as a code owner December 15, 2022 05:38
@github-actions github-actions bot added the Go label Dec 15, 2022
Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your reasoning on filepath.Base? In particular filepath.Base("..") is "..", which seems like we shouldn't be letting that through?

go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll Outdated Show resolved Hide resolved
Comment on lines 126 to 128
cleanCall.asInstruction() = node and
frn.getField().hasQualifiedName("mime/multipart", "FileHeader", "Filename") and
node.getASuccessor*() = frn.asInstruction()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use DataFlow::localFlow to establish dataflow from the multipart parse to the access of the Filename field.

Also why are we sanitizing frn.getBase() (the Form that was read from), not frn itself (the Filename we read?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, also don't need flow here since according to documentation the field is only available after ParseMultipartForm is called.

// MultipartForm is the parsed multipart form, including file uploads.
// This field is only available after ParseMultipartForm is called.
// The HTTP client ignores MultipartForm and uses Body instead.
MultipartForm *[multipart](https://pkg.go.dev/mime/multipart).[Form](https://pkg.go.dev/mime/multipart#Form)

go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll Outdated Show resolved Hide resolved
@Kwstubbs
Copy link
Contributor Author

What's your reasoning on filepath.Base? In particular filepath.Base("..") is "..", which seems like we shouldn't be letting that through?

Thats fair, I found that most developers have used filepath.Base to correctly prevent path traversal that is why I included it. Since ParseMultipartForm uses filepath.Base on Filename, do you want to include it? It "cleans" the filename of the multipart form and since ".." is not a valid filename across platforms, it should normally error out on any file operations.

@smowton
Copy link
Contributor

smowton commented Dec 19, 2022

@Kwstubbs I would have to appeal to the security lab for a definitive opinion, but my gut take is that filepath.Base is an inadequate sanitizer since I can still traverse one layer outside the directory I was expected to. I might get lucky with some file ops because $cwd/.. is a directory, but in general that still strikes me as dangerous.

@Kwstubbs
Copy link
Contributor Author

Kwstubbs commented Dec 19, 2022

@smowton Is the policy for the sanitizers that no true positive can be discarded in exchange for a better FP rate? If so I will remove it because I agree that while is possible that ".." can be possibly smuggled in if let's say the developer wants to create/remove a directory with a similar name to the file, I just find it highly unlikely. In general, I suggested filepath.Base and ParseMultipartForm because I found developers correctly used them as files, not as directories and because golang has seperate file/directory APIs I thought the lower FP rate would be worth missing a few TPs.

@Kwstubbs Kwstubbs force-pushed the go-taintedpath-additions branch 2 times, most recently from a45343f to d14497f Compare October 5, 2023 00:39
@Kwstubbs Kwstubbs requested a review from smowton October 9, 2023 07:00
@Kwstubbs
Copy link
Contributor Author

Kwstubbs commented Oct 9, 2023

hi @smowton could you take a look at this again? Thank you

@owen-mc
Copy link
Contributor

owen-mc commented Mar 4, 2024

@Kwstubbs Yes, please remove that sanitizer and we can merge the rest.

@Kwstubbs Kwstubbs force-pushed the go-taintedpath-additions branch from ffd4e52 to 30fe416 Compare March 11, 2024 22:12
@Kwstubbs
Copy link
Contributor Author

@owen-mc good to go!

Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting back to this.

Comment on lines +91 to +100
/**An call to ParseMultipartForm creates multipart.Form and cleans multipart.Form.FileHeader.Filename using path.Base() */
class MultipartClean extends Sanitizer {
MultipartClean() {
exists(DataFlow::FieldReadNode frn |
frn.getField().hasQualifiedName("mime/multipart", "FileHeader", "Filename") and
this = frn
)
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we have decided that filepath.Base is not an adequate sanitizer, I don't think we should include this, as it uses filepath.Base (docs link).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@owen-mc Yup I'm aware, however I would still like to advocate for this sanitizer because it is a filename, not just part of a path. If the .. is treated as a filename, it references a folder which is a failure in Golang. If any path is prepended, then the path also references the previous folder, which again fails for APIs that expect a file. It also makes no sense to add any path after the .. since its a file, not a directory. I have seen this particular FP many times and its never been exploitable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, I am confused. Did you mean to sanitize reads of the field Filename in the struct FileHeader (docs)? Or did you mean calls to the method FileName() (docs), which is defined on *Part, i.e. parts of the multipart message. Your code does the first one, but I don't see why that should be sanitized, so I'm guessing maybe you meant to do the second one, because the docs say it has been run through filepath.Base(). I consulted with a colleague and he agreed it was reasonable for the second one to be a sanitizer, so if that's what you meant then please change the code to refer to the right thing and update the test.

Copy link
Contributor Author

@Kwstubbs Kwstubbs Apr 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@owen-mc I added the Filename in FileHeader because it previously led me to a FP when testing it. The only way FileHeader to be instantiated is through readForm, which sanitizes Filename using FileName which uses Base. I understand if you don't wanna add it since the documentation doesn't guarantee anything, but I thought i would include it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense. I think we can merge this then.

Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this contribution.

@owen-mc owen-mc merged commit dc3ea6c into github:main Apr 10, 2024
14 checks passed
@owen-mc
Copy link
Contributor

owen-mc commented Apr 10, 2024

I made a PR with a few follow-ups here: #16180

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants