Skip to content

Swift: Add libxml2 sinks to the XXE query #11165

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

Merged

Conversation

atorralba
Copy link
Contributor

@atorralba atorralba commented Nov 8, 2022

Adds sinks for libxml2.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 8, 2022

QHelp previews:

swift/ql/src/queries/Security/CWE-611/XXE.qhelp

Resolving XML external entity in user-controlled data

Parsing untrusted XML files with a weakly configured XML parser may lead to an XML External Entity (XXE) attack. This type of attack uses external entity references to access arbitrary files on a system, carry out denial of service, or server side request forgery. Even when the result of parsing is not returned to the user, out-of-band data retrieval techniques may allow attackers to steal sensitive data. Denial of services can also be carried out in this situation.

Recommendation

The easiest way to prevent XXE attacks is to disable external entity handling when parsing untrusted data. How this is done depends on the library being used. Note that some libraries, such as recent versions of XMLParser, disable entity expansion by default, so unless you have explicitly enabled entity expansion, no further action needs to be taken.

Example

The following example uses the XMLParser class to parse a string data. If that string is from an untrusted source, this code may be vulnerable to an XXE attack, since the parser is also setting its shouldResolveExternalEntities option to true:

let parser = XMLParser(data: remoteData) // BAD (parser explicitly enables external entities)
parser.shouldResolveExternalEntities = true

To guard against XXE attacks, the shouldResolveExternalEntities option should be left unset or explicitly set to false.

let parser = XMLParser(data: remoteData) // GOOD (parser explicitly disables external entities)
parser.shouldResolveExternalEntities = false

References

* Holds if taint can flow from `source` to `sink` in one local step,
* including bitwise operations, accesses to `.rawValue`, and casts to `Int32`.
*/
private predicate lib2xmlOptionLocalTaintStep(DataFlow::Node source, DataFlow::Node sink) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a little ad-hoc, but the expected use should actually adhere to this pattern (Int32(XML_PARSE_NOENT.rawValue), potentially bitwise-OR'ed with other options, and reaching the options argument of a libxml2 XML-parsing call).

Copy link
Contributor

Choose a reason for hiding this comment

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

Which of the steps in this predicate do you think should ultimately be ported to the data flow or taint flow libraries? Most of them look like they could be taint steps to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The bitwise operation and .rawValue ones are probably of general interest as taint steps. The Int32 constructor technically applies globally too, although it may seem a little more niche.

Copy link
Contributor

Choose a reason for hiding this comment

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

Addressed in #14502 . Actually, we've chipped away at this in a few steps, but that PR gets rid of the last special case here.

@atorralba atorralba marked this pull request as draft November 8, 2022 14:36
Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

This looks great. I have a few questions, quite possibly none of them need any actual changes.

// --- tests ---

func sourcePtr() -> UnsafeMutablePointer<UInt8> { return UnsafeMutablePointer<UInt8>.allocate(capacity: 0) }
func sourceCharPtr() -> UnsafeMutablePointer<CChar> { return UnsafeMutablePointer<CChar>.allocate(capacity: 0) }
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think these sources would look like in actual Swift code? I'm very happy with this solution for the query test, but we might want to plan work to ensure proper taint flow through the UnsafeMutablePointer operations in real code leading up to an XML parse call.

We do have https://github.com/github/codeql-c-team/issues/1333 planned if that's relevant.

Copy link
Contributor Author

@atorralba atorralba Nov 8, 2022

Choose a reason for hiding this comment

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

I tried to model them properly at first, but it started getting too complicated and I used this "dummy" sources as a fallback.

I think we'd need to model, at the very least, the various flavors of initialize and with*, which seem the ones that could translate our current sources to an UnsafeMutablePointer. Probably there are more, this would need its own PR to do it right I guess.

I don't think we should worry about direct UnsafeMutablePointer sources for now, but that would be easy to model anyway.

* Holds if taint can flow from `source` to `sink` in one local step,
* including bitwise operations, accesses to `.rawValue`, and casts to `Int32`.
*/
private predicate lib2xmlOptionLocalTaintStep(DataFlow::Node source, DataFlow::Node sink) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Which of the steps in this predicate do you think should ultimately be ported to the data flow or taint flow libraries? Most of them look like they could be taint steps to me.

@atorralba atorralba force-pushed the atorralba/swift/xxe-query-libxml2-sinks branch from a927ac2 to 16a7685 Compare November 21, 2022 15:30
@atorralba atorralba marked this pull request as ready for review November 21, 2022 15:33
Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

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

LGTM. Sorry this one got forgotten about.

I'll write up an issue or two about the taint steps we'd like to generalize.

update: 1400, 1401, 1402

@atorralba atorralba merged commit 6cfa89e into github:main Nov 23, 2022
@atorralba atorralba deleted the atorralba/swift/xxe-query-libxml2-sinks branch November 23, 2022 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants