Merge pull request #10077 from intrigus-lgtm/cpp/wexpand-commmand-injection

Add query for tainted `wordexp` calls.
This commit is contained in:
Geoffrey White
2022-10-17 11:18:38 +01:00
committed by GitHub
6 changed files with 175 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
int main(int argc, char** argv) {
char *filePath = argv[2];
{
// BAD: the user-controlled string is injected
// directly into `wordexp` which performs command substitution
wordexp_t we;
wordexp(filePath, &we, 0);
}
{
// GOOD: command substitution is disabled
wordexp_t we;
wordexp(filePath, &we, WRDE_NOCMD);
}
}

View File

@@ -0,0 +1,42 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The code passes user input to <code>wordexp</code>. This leaves the code
vulnerable to attack by command injection, because <code>wordexp</code> performs command substitution.
Command substitution is a feature that replaces <code>$(command)</code> or <code>`command`</code> with the
output of the given command, allowing the user to run arbitrary code on the system.
</p>
</overview>
<recommendation>
<p>When calling <code>wordexp</code>, pass the <code>WRDE_NOCMD</code> flag to prevent command substitution.</p>
</recommendation>
<example>
<p>The following example passes a user-supplied file path to <code>wordexp</code> in two ways. The
first way uses <code>wordexp</code> with no specified flags. As such, it is vulnerable to command
injection.
The second way uses <code>wordexp</code> with the <code>WRDE_NOCMD</code> flag. As such, no command substitution
is performed, making this safe from command injection.</p>
<sample src="WordexpTainted.c" />
</example>
<references>
<li>CERT C Coding Standard:
<a href="https://www.securecoding.cert.org/confluence/display/c/STR02-C.+Sanitize+data+passed+to+complex+subsystems">STR02-C.
Sanitize data passed to complex subsystems</a>.</li>
<li>
OWASP:
<a href="https://www.owasp.org/index.php/Command_Injection">Command Injection</a>.
</li>
<!-- LocalWords: CWE STR
-->
</references>
</qhelp>

View File

@@ -0,0 +1,57 @@
/**
* @name Uncontrolled data used in `wordexp` command
* @description Using user-supplied data in a `wordexp` command, without
* disabling command substitution, can make code vulnerable
* to command injection.
* @kind path-problem
* @problem.severity error
* @precision high
* @id cpp/wordexp-injection
* @tags security
* external/cwe/cwe-078
*/
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.security.FlowSources
import DataFlow::PathGraph
/**
* The `wordexp` function, which can perform command substitution.
*/
private class WordexpFunction extends Function {
WordexpFunction() { hasGlobalName("wordexp") }
}
/**
* Holds if `fc` disables command substitution by containing `WRDE_NOCMD` as a flag argument.
*/
private predicate isCommandSubstitutionDisabled(FunctionCall fc) {
fc.getArgument(2).getValue().toInt().bitAnd(4) = 4
/* 4 = WRDE_NOCMD. Check whether the flag is set. */
}
/**
* A configuration to track user-supplied data to the `wordexp` function.
*/
class WordexpTaintConfiguration extends TaintTracking::Configuration {
WordexpTaintConfiguration() { this = "WordexpTaintConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall fc | fc.getTarget() instanceof WordexpFunction |
fc.getArgument(0) = sink.asExpr() and
not isCommandSubstitutionDisabled(fc)
)
}
override predicate isSanitizer(DataFlow::Node node) {
node.asExpr().getUnspecifiedType() instanceof IntegralType
}
}
from WordexpTaintConfiguration conf, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode
where conf.hasFlowPath(sourceNode, sinkNode)
select sinkNode.getNode(), sourceNode, sinkNode,
"Using user-supplied data in a `wordexp` command, without disabling command substitution, can make code vulnerable to command injection."