Skip to content

[Python] CWE-348: Client supplied ip used in security check#6214

Merged
yoff merged 8 commits into
github:mainfrom
haby0:python/ClientSuppliedIpUsedInSecurityCheck
Oct 11, 2021
Merged

[Python] CWE-348: Client supplied ip used in security check#6214
yoff merged 8 commits into
github:mainfrom
haby0:python/ClientSuppliedIpUsedInSecurityCheck

Conversation

@haby0

@haby0 haby0 commented Jul 2, 2021

Copy link
Copy Markdown
Contributor

If an application trusts an HTTP request header like X-Forwarded-For to accurately specify the remote IP address of the connecting client.

@haby0 haby0 requested a review from a team as a code owner July 2, 2021 11:33

@yoff yoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Generally clean and well structured 👍. There are a number of uncommented source and sink implementations.

Comment on lines +50 to +67
TornadoClientSuppliedIpUsedInSecurityCheck() {
exists(RemoteFlowSource rfs, DataFlow::LocalSourceNode lsn |
rfs.getSourceType() = "tornado.web.RequestHandler" and rfs.asCfgNode() = lsn.asCfgNode()
|
lsn.flowsTo(DataFlow::exprNode(this.getFunction()
.asExpr()
.(Attribute)
.getObject()
.(Attribute)
.getObject()
.(Attribute)
.getObject())) and
this.getFunction().asExpr().(Attribute).getName() in ["get", "get_list"] and
this.getFunction().asExpr().(Attribute).getObject().(Attribute).getName() = "headers" and
this.getArg(0).asCfgNode().getNode().(StrConst).getText().toLowerCase() =
clientIpParameterName()
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I suggest not quantifying over the local source node, since it will just be rfs. Instead I suggest adding a shorthand for this.getFunction().(Attribute). And then to use DataFlow::AttrRead instead of Attribute as it operates on a higher level. Finally, I would comment the long chain of casts.

Suggested change
TornadoClientSuppliedIpUsedInSecurityCheck() {
exists(RemoteFlowSource rfs, DataFlow::LocalSourceNode lsn |
rfs.getSourceType() = "tornado.web.RequestHandler" and rfs.asCfgNode() = lsn.asCfgNode()
|
lsn.flowsTo(DataFlow::exprNode(this.getFunction()
.asExpr()
.(Attribute)
.getObject()
.(Attribute)
.getObject()
.(Attribute)
.getObject())) and
this.getFunction().asExpr().(Attribute).getName() in ["get", "get_list"] and
this.getFunction().asExpr().(Attribute).getObject().(Attribute).getName() = "headers" and
this.getArg(0).asCfgNode().getNode().(StrConst).getText().toLowerCase() =
clientIpParameterName()
)
}
TornadoClientSuppliedIpUsedInSecurityCheck() {
exists(RemoteFlowSource rfs, DataFlow::AttrRead get |
rfs.getSourceType() = "tornado.web.RequestHandler" and
this.getFunction() = get
|
// `get` is a call to `rfs`.request.headers.get
// `rfs`.request.headers
get.getObject()
.(DataFlow::AttrRead)
// `rfs`.request
.getObject()
.(DataFlow::AttrRead)
// `rfs`
.getObject()
.getALocalSource() = rfs and
get.getAttributeName() in ["get", "get_list"] and
get.getObject().(DataFlow::AttrRead).getAttributeName() = "headers" and
this.getArg(0).asExpr().(StrConst).getText().toLowerCase() = clientIpParameterName()
)
}

A similar rewrite could be made to the one for Flask.

*
* For example: `request.headers.get("X-Forwarded-For")`.
*/
abstract class ClientSuppliedIpUsedInSecurityCheck extends DataFlow::CallCfgNode { }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would rename this to ClientSuppliedIp (and similarly drop the suffix on all the implementations) since that is what we know about these nodes. It also reads better in the configuration: "A client supplied IP (source) flows to a security check (sink)".

}

/** A data flow sink for ip address forgery vulnerabilities. */
abstract class ClientSuppliedIpUsedInSecurityCheckSink extends DataFlow::Node { }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would rename this class to PossibleSecurityCheck or SecurityCheck.

abstract class ClientSuppliedIpUsedInSecurityCheckSink extends DataFlow::Node { }

/** A data flow sink for sql operation. */
private class SqlOperationSink extends ClientSuppliedIpUsedInSecurityCheckSink {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would rename this class to SqlOperationAsSecurityCheck

Comment on lines +42 to +50
override predicate isSanitizer(DataFlow::Node node) {
exists(Subscript ss |
not ss.getIndex().(IntegerLiteral).getText() = "0" and
ss.getObject().(Call).getFunc().(Attribute).getName() = "split" and
ss.getObject().(Call).getArg(0).(StrConst).getText() = "," and
ss.getObject().(Call).getFunc().(Attribute).getObject() = node.asExpr()
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should the sanitizer (node) not be the result of the subscripting rather than object? Consider the code

  client_ip = request.headers.get('x-forwarded-for')
  dummy = client_ip.split(",")[2]
  if not client_ip == '127.0.0.1':
         raise Exception('ip illegal')

This would be deemed sanitized as the flow is blocked in the line assigning dummy. Yet, no change was made to client_ip prior to testing it. (If the sanitizer is the subscript, the flow is blocked into dummy, but there is still use-use-flow to the variable on the next line.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is there such a way of writing in real open source projects?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

X-Forwarded-For is a superposition process when used, format: client, proxy1, proxy2. X-Real-IP is a process of coverage when in use.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It is possible that we have found a possible improvement to the java query :-) While this particular pattern looks unlikely, I think it could easily happen as a typo, at which point an alert would be great. And other patterns could be constructed as well, so we should always strive to point to the correct node. In any case, it should not be hard to fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK. Can you elaborate on this disinfectant? Let me optimize the configuration.

@haby0 haby0 Sep 24, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

override predicate isSanitizer(DataFlow::Node node) {
    exists(Subscript ss |
      ss.getObject().(Call).getFunc().(Attribute).getName() = "split" and
      ss.getObject().(Call).getArg(0).(StrConst).getText() = "," and
      ss = node.asExpr()
    )
  }

@yoff Do you want this result?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes exactly. Except I think you still need to exclude index 0.

@haby0 haby0 requested a review from yoff September 24, 2021 10:52
@haby0

haby0 commented Sep 30, 2021

Copy link
Copy Markdown
Contributor Author

Friendly ping.

@yoff

yoff commented Oct 4, 2021

Copy link
Copy Markdown
Contributor

Friendly ping.

Thanks for the heads up, I must have missed the notification. I did not mean for 10 days to pass by!

@haby0

haby0 commented Oct 5, 2021

Copy link
Copy Markdown
Contributor Author

Friendly ping.

Thanks for the heads up, I must have missed the notification. I did not mean for 10 days to pass by!

Please review, thank you!

@yoff yoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe you still need to inspect the index? Otherwise looks fine now.

…edInSecurityCheck.ql

Co-authored-by: yoff <lerchedahl@gmail.com>
yoff
yoff previously approved these changes Oct 10, 2021

@yoff yoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@yoff

yoff commented Oct 11, 2021

Copy link
Copy Markdown
Contributor

The language tests fail because we now have a mechanism for computing subpaths. You can merge from main and rerun tests. I will also make a suggestion, that I think will make them pass..

…tSuppliedIpUsedInSecurityCheck.expected

Co-authored-by: yoff <lerchedahl@gmail.com>
@haby0

haby0 commented Oct 11, 2021

Copy link
Copy Markdown
Contributor Author

The language tests fail because we now have a mechanism for computing subpaths. You can merge from main and rerun tests. I will also make a suggestion, that I think will make them pass..

Do I need to make other changes?

@yoff

yoff commented Oct 11, 2021

Copy link
Copy Markdown
Contributor

Do I need to make other changes?

No, I think it is fine for now :-)

@yoff yoff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

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.

2 participants