|
11 | 11 | */
|
12 | 12 |
|
13 | 13 | import python
|
| 14 | +import semmle.python.dataflow.new.DataFlow |
| 15 | +import semmle.python.ApiGraphs |
14 | 16 |
|
15 |
| -Value aSocket() { result.getClass() = Value::named("socket.socket") } |
| 17 | +/** Gets a hostname that can be used to bind to all interfaces. */ |
| 18 | +private string vulnerableHostname() { |
| 19 | + result in [ |
| 20 | + // IPv4 |
| 21 | + "0.0.0.0", "", |
| 22 | + // IPv6 |
| 23 | + "::", "::0" |
| 24 | + ] |
| 25 | +} |
16 | 26 |
|
17 |
| -CallNode socketBindCall() { |
18 |
| - result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3 |
| 27 | +/** Gets a reference to a hostname that can be used to bind to all interfaces. */ |
| 28 | +private DataFlow::LocalSourceNode vulnerableHostnameRef(DataFlow::TypeTracker t, string hostname) { |
| 29 | + t.start() and |
| 30 | + exists(StrConst allInterfacesStrConst | hostname = vulnerableHostname() | |
| 31 | + allInterfacesStrConst.getText() = hostname and |
| 32 | + result.asExpr() = allInterfacesStrConst |
| 33 | + ) |
19 | 34 | or
|
20 |
| - result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and |
21 |
| - major_version() = 2 |
| 35 | + // Due to bad performance when using normal setup with `vulnerableHostnameRef(t2, hostname).track(t2, t)` |
| 36 | + // we have inlined that code and forced a join |
| 37 | + exists(DataFlow::TypeTracker t2 | |
| 38 | + exists(DataFlow::StepSummary summary | |
| 39 | + vulnerableHostnameRef_first_join(t2, hostname, result, summary) and |
| 40 | + t = t2.append(summary) |
| 41 | + ) |
| 42 | + ) |
| 43 | +} |
| 44 | + |
| 45 | +pragma[nomagic] |
| 46 | +private predicate vulnerableHostnameRef_first_join( |
| 47 | + DataFlow::TypeTracker t2, string hostname, DataFlow::Node res, DataFlow::StepSummary summary |
| 48 | +) { |
| 49 | + DataFlow::StepSummary::step(vulnerableHostnameRef(t2, hostname), res, summary) |
22 | 50 | }
|
23 | 51 |
|
24 |
| -string allInterfaces() { result = "0.0.0.0" or result = "" } |
| 52 | +/** Gets a reference to a hostname that can be used to bind to all interfaces. */ |
| 53 | +DataFlow::Node vulnerableHostnameRef(string hostname) { |
| 54 | + vulnerableHostnameRef(DataFlow::TypeTracker::end(), hostname).flowsTo(result) |
| 55 | +} |
25 | 56 |
|
26 |
| -Value getTextValue(string address) { |
27 |
| - result = Value::forUnicode(address) and major_version() = 3 |
| 57 | +/** Gets a reference to a tuple for which the first element is a hostname that can be used to bind to all interfaces. */ |
| 58 | +private DataFlow::LocalSourceNode vulnerableAddressTuple(DataFlow::TypeTracker t, string hostname) { |
| 59 | + t.start() and |
| 60 | + result.asExpr() = any(Tuple tup | tup.getElt(0) = vulnerableHostnameRef(hostname).asExpr()) |
28 | 61 | or
|
29 |
| - result = Value::forString(address) and major_version() = 2 |
| 62 | + // Due to bad performance when using normal setup with `vulnerableAddressTuple(t2, hostname).track(t2, t)` |
| 63 | + // we have inlined that code and forced a join |
| 64 | + exists(DataFlow::TypeTracker t2 | |
| 65 | + exists(DataFlow::StepSummary summary | |
| 66 | + vulnerableAddressTuple_first_join(t2, hostname, result, summary) and |
| 67 | + t = t2.append(summary) |
| 68 | + ) |
| 69 | + ) |
| 70 | +} |
| 71 | + |
| 72 | +pragma[nomagic] |
| 73 | +private predicate vulnerableAddressTuple_first_join( |
| 74 | + DataFlow::TypeTracker t2, string hostname, DataFlow::Node res, DataFlow::StepSummary summary |
| 75 | +) { |
| 76 | + DataFlow::StepSummary::step(vulnerableAddressTuple(t2, hostname), res, summary) |
30 | 77 | }
|
31 | 78 |
|
32 |
| -from CallNode call, TupleValue args, string address |
| 79 | +/** Gets a reference to a tuple for which the first element is a hostname that can be used to bind to all interfaces. */ |
| 80 | +DataFlow::Node vulnerableAddressTuple(string hostname) { |
| 81 | + vulnerableAddressTuple(DataFlow::TypeTracker::end(), hostname).flowsTo(result) |
| 82 | +} |
| 83 | + |
| 84 | +/** |
| 85 | + * Gets an instance of `socket.socket` using _some_ address family. |
| 86 | + * |
| 87 | + * See https://docs.python.org/3/library/socket.html |
| 88 | + */ |
| 89 | +API::Node socketInstance() { result = API::moduleImport("socket").getMember("socket").getReturn() } |
| 90 | + |
| 91 | +from DataFlow::CallCfgNode bindCall, DataFlow::Node addressArg, string hostname |
33 | 92 | where
|
34 |
| - call = socketBindCall() and |
35 |
| - call.getArg(0).pointsTo(args) and |
36 |
| - args.getItem(0) = getTextValue(address) and |
37 |
| - address = allInterfaces() |
38 |
| -select call.getNode(), "'" + address + "' binds a socket to all interfaces." |
| 93 | + bindCall = socketInstance().getMember("bind").getACall() and |
| 94 | + addressArg = bindCall.getArg(0) and |
| 95 | + addressArg = vulnerableAddressTuple(hostname) |
| 96 | +select bindCall.asExpr(), "'" + hostname + "' binds a socket to all interfaces." |
0 commit comments