Skip to content

Instantly share code, notes, and snippets.

@tyage
Created October 15, 2020 09:25
Show Gist options
  • Save tyage/39d518c9edf1ba04342ae58bd16238f3 to your computer and use it in GitHub Desktop.
Save tyage/39d518c9edf1ba04342ae58bd16238f3 to your computer and use it in GitHub Desktop.
import javascript
import DataFlow
import DataFlow::PathGraph
class SSRFConfiguration extends TaintTracking::Configuration {
SSRFConfiguration() { this = "SSRFConfiguration" }
override predicate isSource(DataFlow::Node source) {
exists(DataFlow::SourceNode req |
isHTTPRequest(req) and
(
exists(MethodCallNode call |
source = call and
call = req.getALocalSource().getAMethodCall("get") and
call.getAnArgument().getStringValue() = "Host"
) or
source = req.getALocalSource().getAPropertyRead("headers").getAPropertyRead("hostname")
)
)
}
override predicate isSink(DataFlow::Node sink) { sink instanceof SSRFSink }
}
// In next.js, req argument of getInitialProps and getServerSideProps function is HTTP request object
predicate isHTTPRequest(DataFlow::Node source) {
exists(FunctionNode func, ParameterNode param |
isServerSidePropsFunc(func) and
param = func.getAParameter() and
(
param.getAPropertyRead("req") = source or
param.getAPropertyRead("ctx").getAPropertyRead("req") = source
)
) or exists(HTTP::Servers::RequestSource req | source = req)
}
predicate isServerSidePropsFunc(DataFlow::Node source) {
exists(FunctionNode func |
source = func and
(
func.getName() = "getServerSideProps" or
func.getName() = "getInitialProps"
)
) or
exists(SourceNode page |
page.getAPropertyWrite("getServerSideProps").getAFunctionValue() = source
)
}
abstract class SSRFSink extends DataFlow::Node { }
class SSRFAxiosSink extends SSRFSink {
SSRFAxiosSink() {
exists(CallNode func, ModuleImportNode axiosImport |
axiosImport = moduleImport(["axios"]) and
(
func = axiosImport.getAMemberCall("get")
or
func = axiosImport.getAMemberCall("post")
or
func = axiosImport.getAMemberCall("put")
or
func = axiosImport.getAMemberCall("patch")
or
func = axiosImport.getAMemberCall("delete")
or
func = axiosImport.getACall()
) and
this = func.getArgument(0)
)
}
}
class SSRFFetchSink extends SSRFSink {
SSRFFetchSink() {
exists(CallNode compile |
compile = moduleImport(["isomorphic-fetch"]).getACall() and
this = compile.getArgument(0)
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, SSRFConfiguration c
where c.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"$@ flows to here and unsafely used as part of fetch URL", source.getNode(),
"User-provided value"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment