mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
JS: add query MethodNameInjection
This commit is contained in:
43
javascript/ql/src/Security/CWE-094/MethodNameInjection.qhelp
Normal file
43
javascript/ql/src/Security/CWE-094/MethodNameInjection.qhelp
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Invoking a user-controlled method on certain objects can lead to invocation of unsafe functions,
|
||||
such as <code>eval</code> or the <code>Function</code> constructor. In particular, the global object
|
||||
contains the <code>eval</code> function, and any function object contains the <code>Function</code> constructor
|
||||
in its <code>constructor</code> property.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Avoid invoking user-controlled methods on the global object or on any function object.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
In the following example, a user-controlled string is used as the method name on a cross-window message handler.
|
||||
In this case, a malicious website can embed the page in an iframe, and execute arbitary code in the page by
|
||||
sending a message to it with <code>name</code> set to <code>eval</code>.
|
||||
</p>
|
||||
|
||||
<sample src="examples/MethodNameInjection.js" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Code_Injection">Code Injection</a>.
|
||||
</li>
|
||||
<li>
|
||||
MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#Function_properties">Global functions</a>.
|
||||
</li>
|
||||
<li>
|
||||
MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function">Function constructor</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
17
javascript/ql/src/Security/CWE-094/MethodNameInjection.ql
Normal file
17
javascript/ql/src/Security/CWE-094/MethodNameInjection.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Method name injection
|
||||
* @description Invoking user-controlled methods on a arbitrary objects can lead to remote code execution.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @id js/method-name-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.MethodNameInjection::MethodNameInjection
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink, source, sink, "Invocation of method derived from $@ may lead to remote code execution.", source.getNode(), "user-controlled value"
|
||||
@@ -0,0 +1,4 @@
|
||||
window.addEventListener("message", (ev) => {
|
||||
let message = JSON.parse(ev.data);
|
||||
window[message.name](message.payload);
|
||||
});
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Provides a taint tracking configuration for reasoning about method invocations
|
||||
* with a user-controlled method name.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.frameworks.Express
|
||||
|
||||
module MethodNameInjection {
|
||||
private import DataFlow::FlowLabel
|
||||
|
||||
/**
|
||||
* A data flow source for method name injection.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the flow label relevant for this source.
|
||||
*/
|
||||
DataFlow::FlowLabel getFlowLabel() {
|
||||
result = data()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for method name injection.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the flow label relevant for this sink
|
||||
*/
|
||||
abstract DataFlow::FlowLabel getFlowLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for method name injection.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* Gets the flow label describing values that may refer to an unsafe
|
||||
* function as a result of an attacker-controlled property name.
|
||||
*/
|
||||
UnsafeFunction unsafeFunction() { any() }
|
||||
private class UnsafeFunction extends DataFlow::FlowLabel {
|
||||
UnsafeFunction() { this = "UnsafeFunction" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about method name injection.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "RemotePropertyInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
|
||||
sink.(Sink).getFlowLabel() = label
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a property of the given object is an unsafe function.
|
||||
*/
|
||||
predicate isUnsafeBaseObject(DataFlow::SourceNode node) {
|
||||
// eval an friends can be accessed from the global object.
|
||||
node = DataFlow::globalObjectRef()
|
||||
or
|
||||
// 'constructor' property leads to the Function constructor.
|
||||
node.analyze().getAValue() instanceof AbstractCallable
|
||||
or
|
||||
// Assume that a value that is invoked can refer to a function.
|
||||
exists (node.getAnInvocation())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `node` is of form `Object.create(null)` and so it has no prototype.
|
||||
*/
|
||||
predicate isPrototypeLessObject(DataFlow::MethodCallNode node) {
|
||||
node = DataFlow::globalVarRef("Object").getAMethodCall("create") and
|
||||
node.getArgument(0).asExpr() instanceof NullLiteral
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dst, DataFlow::FlowLabel srclabel, DataFlow::FlowLabel dstlabel) {
|
||||
// Reading a property of the global object or of a function
|
||||
exists (DataFlow::PropRead read |
|
||||
isUnsafeBaseObject(read.getBase().getALocalSource()) and
|
||||
src = read.getPropertyNameExpr().flow() and
|
||||
dst = read and
|
||||
srclabel = taint() and
|
||||
dstlabel = unsafeFunction())
|
||||
or
|
||||
// Reading a chain of properties from any object with a prototype can lead to Function
|
||||
exists (PropertyProjection proj |
|
||||
not isPrototypeLessObject(proj.getObject().getALocalSource()) and
|
||||
src = proj.getASelector() and
|
||||
dst = proj and
|
||||
srclabel = taint() and
|
||||
dstlabel = unsafeFunction())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a source for method name injection.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* The page URL considered as a flow source for method name injection.
|
||||
*/
|
||||
class DocumentUrlAsSource extends Source {
|
||||
DocumentUrlAsSource() { isDocumentURL(asExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A function invocation of an unsafe function, as a sink for remote method name injection.
|
||||
*/
|
||||
class CalleeAsSink extends Sink {
|
||||
CalleeAsSink() {
|
||||
this = any(DataFlow::InvokeNode node).getCalleeNode()
|
||||
}
|
||||
|
||||
override DataFlow::FlowLabel getFlowLabel() {
|
||||
result = unsafeFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A binary expression that sanitzes a value for method name injection. That
|
||||
* is, if a string is prepended or appended to the remote input, an attacker
|
||||
* cannot access arbitrary properties.
|
||||
*/
|
||||
class ConcatSanitizer extends Sanitizer, DataFlow::ValueNode {
|
||||
|
||||
override BinaryExpr astNode;
|
||||
|
||||
ConcatSanitizer() {
|
||||
astNode.getAnOperand() instanceof ConstantString
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user