Skip to content

Commit 3681a1b

Browse files
authored
Merge pull request #7933 from geoffw0/cwe497
C++: Improve cpp/system-data-exposure
2 parents 71cd507 + 2962b12 commit 3681a1b

File tree

11 files changed

+173
-125
lines changed

11 files changed

+173
-125
lines changed

cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql

Lines changed: 33 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @description Exposing system data or debugging information helps
44
* an adversary learn about the system and form an
55
* attack plan.
6-
* @kind problem
6+
* @kind path-problem
77
* @problem.severity warning
88
* @security-severity 6.5
99
* @precision medium
@@ -14,7 +14,9 @@
1414

1515
import cpp
1616
import semmle.code.cpp.commons.Environment
17-
import semmle.code.cpp.security.OutputWrite
17+
import semmle.code.cpp.ir.dataflow.TaintTracking
18+
import semmle.code.cpp.models.interfaces.FlowSource
19+
import DataFlow::PathGraph
1820

1921
/**
2022
* An element that should not be exposed to an adversary.
@@ -24,42 +26,19 @@ abstract class SystemData extends Element {
2426
* Gets an expression that is part of this `SystemData`.
2527
*/
2628
abstract Expr getAnExpr();
27-
28-
/**
29-
* Gets an expression whose value originates from, or is used by,
30-
* this `SystemData`.
31-
*/
32-
Expr getAnExprIndirect() {
33-
// direct SystemData
34-
result = this.getAnExpr() or
35-
// flow via global or member variable (conservative approximation)
36-
result = this.getAnAffectedVar().getAnAccess() or
37-
// flow via stack variable
38-
definitionUsePair(_, this.getAnExprIndirect(), result) or
39-
useUsePair(_, this.getAnExprIndirect(), result) or
40-
useUsePair(_, result, this.getAnExprIndirect()) or
41-
// flow from assigned value to assignment expression
42-
result.(AssignExpr).getRValue() = this.getAnExprIndirect()
43-
}
44-
45-
/**
46-
* Gets a global or member variable that may be affected by this system
47-
* data (conservative approximation).
48-
*/
49-
private Variable getAnAffectedVar() {
50-
(
51-
result.getAnAssignedValue() = this.getAnExprIndirect() or
52-
result.getAnAccess() = this.getAnExprIndirect()
53-
) and
54-
not result instanceof LocalScopeVariable
55-
}
5629
}
5730

5831
/**
5932
* Data originating from the environment.
6033
*/
6134
class EnvData extends SystemData {
62-
EnvData() { this instanceof EnvironmentRead }
35+
EnvData() {
36+
// identify risky looking environment variables only
37+
this.(EnvironmentRead)
38+
.getEnvironmentVariable()
39+
.toLowerCase()
40+
.regexpMatch(".*(user|host|admin|root|home|path|http|ssl|snmp|sock|port|proxy|pass|token|crypt|key).*")
41+
}
6342

6443
override Expr getAnExpr() { result = this }
6544
}
@@ -91,11 +70,6 @@ class SQLConnectInfo extends SystemData {
9170
}
9271

9372
private predicate posixSystemInfo(FunctionCall source, Element use) {
94-
// long sysconf(int name)
95-
// - various OS / system values and limits
96-
source.getTarget().hasName("sysconf") and
97-
use = source
98-
or
9973
// size_t confstr(int name, char *buf, size_t len)
10074
// - various OS / system strings, such as the libc version
10175
// int statvfs(const char *__path, struct statvfs *__buf)
@@ -311,70 +285,31 @@ class RegQuery extends SystemData {
311285
override Expr getAnExpr() { regQuery(this, result) }
312286
}
313287

314-
/**
315-
* Somewhere data is output.
316-
*/
317-
abstract class DataOutput extends Element {
318-
/**
319-
* Get an expression containing data that is output.
320-
*/
321-
abstract Expr getASource();
322-
}
288+
class ExposedSystemDataConfiguration extends TaintTracking::Configuration {
289+
ExposedSystemDataConfiguration() { this = "ExposedSystemDataConfiguration" }
323290

324-
/**
325-
* Data that is output via standard output or standard error.
326-
*/
327-
class StandardOutput extends DataOutput instanceof OutputWrite {
328-
override Expr getASource() { result = OutputWrite.super.getASource() }
329-
}
291+
override predicate isSource(DataFlow::Node source) {
292+
source.asConvertedExpr() = any(SystemData sd).getAnExpr()
293+
}
330294

331-
private predicate socketCallOrIndirect(FunctionCall call) {
332-
// direct socket call
333-
// int socket(int domain, int type, int protocol);
334-
call.getTarget().getName() = "socket"
335-
or
336-
exists(ReturnStmt rtn |
337-
// indirect socket call
338-
call.getTarget() = rtn.getEnclosingFunction() and
339-
(
340-
socketCallOrIndirect(rtn.getExpr()) or
341-
socketCallOrIndirect(rtn.getExpr().(VariableAccess).getTarget().getAnAssignedValue())
295+
override predicate isSink(DataFlow::Node sink) {
296+
exists(FunctionCall fc, FunctionInput input, int arg |
297+
fc.getTarget().(RemoteFlowSinkFunction).hasRemoteFlowSink(input, _) and
298+
input.isParameterDeref(arg) and
299+
fc.getArgument(arg).getAChild*() = sink.asExpr()
342300
)
343-
)
344-
}
345-
346-
private predicate socketFileDescriptor(Expr e) {
347-
exists(Variable var, FunctionCall socket |
348-
socketCallOrIndirect(socket) and
349-
var.getAnAssignedValue() = socket and
350-
e = var.getAnAccess()
351-
)
352-
}
353-
354-
private predicate socketOutput(FunctionCall call, Expr data) {
355-
(
356-
// ssize_t send(int sockfd, const void *buf, size_t len, int flags);
357-
// ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
358-
// const struct sockaddr *dest_addr, socklen_t addrlen);
359-
// ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
360-
// int write(int handle, void *buffer, int nbyte);
361-
call.getTarget().hasGlobalName(["send", "sendto", "sendmsg", "write"]) and
362-
data = call.getArgument(1) and
363-
socketFileDescriptor(call.getArgument(0))
364-
)
365-
}
366-
367-
/**
368-
* Data that is output via a socket.
369-
*/
370-
class SocketOutput extends DataOutput {
371-
SocketOutput() { socketOutput(this, _) }
372-
373-
override Expr getASource() { socketOutput(this, result) }
301+
}
374302
}
375303

376-
from SystemData sd, DataOutput ow
304+
from ExposedSystemDataConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
377305
where
378-
sd.getAnExprIndirect() = ow.getASource() or
379-
sd.getAnExprIndirect() = ow.getASource().getAChild*()
380-
select ow, "This operation exposes system data from $@.", sd, sd.toString()
306+
config.hasFlowPath(source, sink) and
307+
not exists(
308+
DataFlow::Node alt // remove duplicate results on conversions
309+
|
310+
config.hasFlow(source.getNode(), alt) and
311+
alt.asConvertedExpr() = sink.getNode().asExpr() and
312+
alt != sink.getNode()
313+
)
314+
select sink, source, sink, "This operation exposes system data from $@.", source,
315+
source.getNode().toString()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `cpp/system-data-exposure` query has been modernized and has converted to a `path-problem` query. There are now fewer false positive results.

cpp/ql/src/codeql-suites/exclude-slow-queries.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# These queries are infeasible to compute on large projects:
33
- exclude:
44
query path:
5-
- Security/CWE/CWE-497/ExposedSystemData.ql
65
- Critical/DescriptorMayNotBeClosed.ql
76
- Critical/DescriptorNeverClosed.ql
87
- Critical/FileMayNotBeClosed.ql
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
| tests.c:70:9:70:15 | call to fprintf | This operation exposes system data from $@. | tests.c:54:13:54:22 | call to LogonUserA | call to LogonUserA |
1+
edges
2+
nodes
3+
subpaths
4+
#select

cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.expected

Lines changed: 0 additions & 7 deletions
This file was deleted.

cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.ql

Lines changed: 0 additions & 4 deletions
This file was deleted.

cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/tests.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,6 @@ void CWE535_Info_Exposure_Shell_Error__w32_char_01_bad()
6767
printLine("Unable to login.");
6868
}
6969
/* FLAW: Write sensitive data to stderr */
70-
fprintf(stderr, "User attempted access with password: %s\n", password);
70+
fprintf(stderr, "User attempted access with password: %s\n", password); // [NOT DETECTED]
7171
}
7272
}
Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,47 @@
1-
| tests2.cpp:27:12:27:12 | call to operator<< | This operation exposes system data from $@. | tests2.cpp:27:15:27:20 | call to getenv | call to getenv |
2-
| tests2.cpp:28:25:28:25 | call to operator<< | This operation exposes system data from $@. | tests2.cpp:28:28:28:33 | call to getenv | call to getenv |
1+
edges
2+
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... |
3+
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... |
4+
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... |
5+
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... |
6+
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
7+
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
8+
| tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 |
9+
| tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw |
10+
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | tests2.cpp:109:14:109:15 | c1 [read] [ptr] |
11+
| tests2.cpp:107:6:107:8 | ptr [post update] | tests2.cpp:107:3:107:4 | c1 [post update] [ptr] |
12+
| tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:107:6:107:8 | ptr [post update] |
13+
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | tests2.cpp:109:14:109:19 | (const char *)... |
14+
nodes
15+
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
16+
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
17+
| tests2.cpp:63:13:63:26 | (const char *)... | semmle.label | (const char *)... |
18+
| tests2.cpp:64:13:64:18 | call to getenv | semmle.label | call to getenv |
19+
| tests2.cpp:64:13:64:18 | call to getenv | semmle.label | call to getenv |
20+
| tests2.cpp:64:13:64:26 | (const char *)... | semmle.label | (const char *)... |
21+
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
22+
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
23+
| tests2.cpp:65:13:65:30 | (const char *)... | semmle.label | (const char *)... |
24+
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
25+
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
26+
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
27+
| tests2.cpp:79:14:79:19 | (const char *)... | semmle.label | (const char *)... |
28+
| tests2.cpp:89:42:89:45 | str1 | semmle.label | str1 |
29+
| tests2.cpp:91:14:91:17 | str1 | semmle.label | str1 |
30+
| tests2.cpp:99:8:99:15 | call to getpwuid | semmle.label | call to getpwuid |
31+
| tests2.cpp:100:14:100:15 | pw | semmle.label | pw |
32+
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | semmle.label | c1 [post update] [ptr] |
33+
| tests2.cpp:107:6:107:8 | ptr [post update] | semmle.label | ptr [post update] |
34+
| tests2.cpp:107:12:107:17 | call to getenv | semmle.label | call to getenv |
35+
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | semmle.label | c1 [read] [ptr] |
36+
| tests2.cpp:109:14:109:19 | (const char *)... | semmle.label | (const char *)... |
37+
subpaths
38+
#select
39+
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:63:13:63:18 | call to getenv | call to getenv |
40+
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:64:13:64:18 | call to getenv | call to getenv |
41+
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:65:13:65:18 | call to getenv | call to getenv |
42+
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
43+
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
44+
| tests2.cpp:79:14:79:19 | (const char *)... | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | call to mysql_get_client_info |
45+
| tests2.cpp:91:14:91:17 | str1 | tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 | This operation exposes system data from $@. | tests2.cpp:89:42:89:45 | str1 | str1 |
46+
| tests2.cpp:100:14:100:15 | pw | tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw | This operation exposes system data from $@. | tests2.cpp:99:8:99:15 | call to getpwuid | call to getpwuid |
47+
| tests2.cpp:109:14:109:19 | (const char *)... | tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:109:14:109:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:107:12:107:17 | call to getenv | call to getenv |

cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/OutputWrite.expected

Lines changed: 0 additions & 5 deletions
This file was deleted.

cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/OutputWrite.ql

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)