Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8296abc

Browse files
committed
Fix Modify the ql query (the qhelp part is not modified).
1 parent 23b508c commit 8296abc

8 files changed

Lines changed: 213 additions & 118 deletions

File tree

java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.java

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import org.apache.commons.lang3.StringUtils;
33
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
5+
import org.springframework.beans.factory.annotation.Autowired;
56
import org.springframework.stereotype.Controller;
67
import org.springframework.web.bind.annotation.GetMapping;
78
import org.springframework.web.bind.annotation.ResponseBody;
@@ -11,24 +12,13 @@ public class UseOfLessTrustedSource {
1112

1213
private static final Logger log = LoggerFactory.getLogger(UseOfLessTrustedSource.class);
1314

14-
@GetMapping(value = "bad1")
15-
@ResponseBody
16-
public String bad1(HttpServletRequest request) {
17-
String remoteAddr = "";
18-
if (request != null) {
19-
remoteAddr = request.getHeader("X-FORWARDED-FOR");
20-
remoteAddr = remoteAddr.split(",")[0];
21-
if (remoteAddr == null || "".equals(remoteAddr)) {
22-
remoteAddr = request.getRemoteAddr();
23-
}
24-
}
25-
return remoteAddr;
26-
}
15+
@Autowired
16+
private HttpServletRequest request;
2717

28-
@GetMapping(value = "bad2")
29-
public void bad2(HttpServletRequest request) {
18+
@GetMapping(value = "bad1")
19+
public void bad1(HttpServletRequest request) {
3020
String ip = request.getHeader("X-Forwarded-For");
31-
21+
3222
log.debug("getClientIP header X-Forwarded-For:{}", ip);
3323

3424
if (StringUtils.isBlank(ip) || StringUtils.equalsIgnoreCase("unknown", ip)) {
@@ -58,6 +48,14 @@ public void bad2(HttpServletRequest request) {
5848
System.out.println("client ip is: " + ip);
5949
}
6050

51+
@GetMapping(value = "bad2")
52+
public void bad2(HttpServletRequest request) {
53+
String ip = getClientIP();
54+
if (!StringUtils.startsWith(ip, "192.168.")) {
55+
new Exception("ip illegal");
56+
}
57+
}
58+
6159
@GetMapping(value = "good1")
6260
@ResponseBody
6361
public String good1(HttpServletRequest request) {
@@ -71,4 +69,12 @@ public String good1(HttpServletRequest request) {
7169
}
7270
return remoteAddr;
7371
}
72+
73+
protected String getClientIP() {
74+
String xfHeader = request.getHeader("X-Forwarded-For");
75+
if (xfHeader == null) {
76+
return request.getRemoteAddr();
77+
}
78+
return xfHeader.split(",")[0];
79+
}
7480
}

java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSource.qhelp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,11 @@ and get the last value of the split array.</p>
2424
</example>
2525
<references>
2626

27-
<li>Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring:
28-
<a href="https://www.dennis-schneider.com/blog/prevent-ip-address-spoofing-with-x-forwarded-for-header-and-aws-elb-in-clojure-ring/">
29-
Prevent IP address spoofing with...</a>
27+
<li>Dennis Schneider: <a href="https://www.dennis-schneider.com/blog/prevent-ip-address-spoofing-with-x-forwarded-for-header-and-aws-elb-in-clojure-ring/">
28+
Prevent IP address spoofing with X-Forwarded-For header when using AWS ELB and Clojure Ring</a>
3029
</li>
3130

32-
<li>Security Rule Zero: A Warning about X-Forwarded-For:
33-
<a href="https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for">
34-
Security Rule Zero: A Warning about X-Forwarded-For</a>
31+
<li>Security Rule Zero: <a href="https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for">A Warning about X-Forwarded-For</a>
3532
</li>
3633

3734
</references>
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
/**
2-
* @name X-Forwarded-For spoofing
2+
* @name IP address spoofing
33
* @description The software obtains the client ip through `X-Forwarded-For`,
44
* and the attacker can modify the value of `X-Forwarded-For` to forge the ip.
55
* @kind path-problem
66
* @problem.severity error
77
* @precision high
8-
* @id java/use-of-less-trusted-source
8+
* @id java/ip-address-spoofing
99
* @tags security
1010
* external/cwe/cwe-348
1111
*/
1212

1313
import java
1414
import UseOfLessTrustedSourceLib
15+
import semmle.code.java.dataflow.DataFlow2
16+
import semmle.code.java.dataflow.TaintTracking2
1517
import semmle.code.java.dataflow.FlowSources
1618
import DataFlow::PathGraph
1719

20+
/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
1821
class UseOfLessTrustedSourceConfig extends TaintTracking::Configuration {
1922
UseOfLessTrustedSourceConfig() { this = "UseOfLessTrustedSourceConfig" }
2023

@@ -23,22 +26,26 @@ class UseOfLessTrustedSourceConfig extends TaintTracking::Configuration {
2326
override predicate isSink(DataFlow::Node sink) { sink instanceof UseOfLessTrustedSink }
2427

2528
/**
26-
* When using `,` split request data and not taking the first value of
27-
* the array, it is considered as `good`.
29+
* When using `,` split request data and not taking the first value of the array, it is considered as `good`.
2830
*/
2931
override predicate isSanitizer(DataFlow::Node node) {
3032
exists(ArrayAccess aa, MethodAccess ma | aa.getArray() = ma |
3133
ma.getQualifier() = node.asExpr() and
3234
ma.getMethod() instanceof SplitMethod and
33-
not aa.getIndexExpr().toString() = "0"
35+
not aa.getIndexExpr().(CompileTimeConstantExpr).getIntValue() = 0
36+
)
37+
}
38+
39+
override predicate isAdditionalTaintStep(DataFlow::Node prod, DataFlow::Node succ) {
40+
exists(MethodAccess ma |
41+
ma.getAnArgument() = prod.asExpr() and
42+
ma = succ.asExpr() and
43+
ma.getMethod().getReturnType() instanceof BooleanType
3444
)
3545
}
3646
}
3747

3848
from DataFlow::PathNode source, DataFlow::PathNode sink, UseOfLessTrustedSourceConfig conf
39-
where
40-
conf.hasFlowPath(source, sink) and
41-
source.getNode().getEnclosingCallable() = sink.getNode().getEnclosingCallable() and
42-
xffIsFirstGet(source.getNode())
43-
select sink.getNode(), source, sink, "X-Forwarded-For spoofing might include code from $@.",
49+
where conf.hasFlowPath(source, sink)
50+
select sink.getNode(), source, sink, "IP address spoofing might include code from $@.",
4451
source.getNode(), "this user input"

java/ql/src/experimental/Security/CWE/CWE-348/UseOfLessTrustedSourceLib.qll

Lines changed: 89 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,105 @@
11
import java
22
import DataFlow
3-
import semmle.code.java.dataflow.DataFlow
3+
import semmle.code.java.dataflow.TaintTracking2
4+
import semmle.code.java.security.QueryInjection
45
import experimental.semmle.code.java.Logging
56

6-
/** A data flow source of the value of the `x-forwarded-for` field in the `header`. */
7+
/**
8+
* A data flow source of the client ip obtained according to the remote endpoint identifier specified
9+
* in the header (`X-Forwarded-For`, `X-Real-IP`, `Proxy-Client-IP`, etc.).
10+
*
11+
* For example: `ServletRequest.getHeader("X-Forwarded-For")`.
12+
*/
713
class UseOfLessTrustedSource extends DataFlow::Node {
814
UseOfLessTrustedSource() {
915
exists(MethodAccess ma |
1016
ma.getMethod().hasName("getHeader") and
11-
ma.getArgument(0).toString().toLowerCase() = "\"x-forwarded-for\"" and
17+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() in [
18+
"x-forwarded-for", "x-real-ip", "proxy-client-ip", "wl-proxy-client-ip",
19+
"http_x_forwarded_for", "http_x_forwarded", "http_x_cluster_client_ip", "http_client_ip",
20+
"http_forwarded_for", "http_forwarded", "http_via", "remote_addr"
21+
] and
1222
ma = this.asExpr()
1323
)
1424
}
1525
}
1626

17-
/** A data flow sink of method return or log output or local print. */
18-
class UseOfLessTrustedSink extends DataFlow::Node {
19-
UseOfLessTrustedSink() {
20-
exists(ReturnStmt rs | rs.getResult() = this.asExpr())
21-
or
27+
/** A data flow sink for ip address forgery vulnerabilities. */
28+
abstract class UseOfLessTrustedSink extends DataFlow::Node { }
29+
30+
/**
31+
* A data flow sink for the if condition, which does not include the null judgment of the remote client ip address.
32+
*
33+
* For example: `if (!StringUtils.startsWith(ipAddr, "192.168.")){...` determine whether the client ip starts
34+
* with `192.168.`, and the program can be deceived by forging the ip address.
35+
* `if (remoteAddr == null || "".equals(remoteAddr)) {...` judging whether the client ip is a null value,
36+
* it needs to be excluded
37+
*/
38+
private class IfConditionSink extends UseOfLessTrustedSink {
39+
IfConditionSink() {
40+
exists(IfStmt is |
41+
is.getCondition() = this.asExpr() and
42+
not exists(EQExpr eqe |
43+
eqe.getAnOperand() instanceof NullLiteral and
44+
is.getCondition() = eqe.getParent*()
45+
) and
46+
not exists(NEExpr nee |
47+
nee.getAnOperand() instanceof NullLiteral and
48+
is.getCondition() = nee.getParent*()
49+
) and
50+
not exists(MethodAccess ma |
51+
ma.getMethod().hasName("equals") and
52+
ma.getMethod().getNumberOfParameters() = 1 and
53+
(
54+
ma.getQualifier().(CompileTimeConstantExpr).getStringValue() = "" or
55+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = ""
56+
) and
57+
is.getCondition() = ma.getParent*()
58+
) and
59+
not exists(MethodAccess ma |
60+
ma.getMethod().hasName("equalsIgnoreCase") and
61+
ma.getMethod().getNumberOfParameters() = 1 and
62+
(
63+
ma.getQualifier().(CompileTimeConstantExpr).getStringValue() = "unknown" or
64+
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "unknown"
65+
) and
66+
is.getCondition() = ma.getParent*()
67+
) and
68+
not exists(MethodAccess ma |
69+
ma.getMethod().getName() in ["isEmpty", "isNotEmpty"] and
70+
ma.getMethod().getNumberOfParameters() = 1 and
71+
is.getCondition() = ma.getParent*()
72+
) and
73+
not exists(MethodAccess ma |
74+
(
75+
ma.getMethod().hasQualifiedName("org.apache.commons.lang3", "StringUtils", "isBlank") or
76+
ma.getMethod().hasQualifiedName("org.apache.commons.lang3", "StringUtils", "isNotBlank")
77+
) and
78+
is.getCondition() = ma.getParent*()
79+
) and
80+
not exists(MethodAccess ma |
81+
ma.getMethod()
82+
.hasQualifiedName("org.apache.commons.lang3", "StringUtils", "equalsIgnoreCase") and
83+
ma.getAnArgument().(CompileTimeConstantExpr).getStringValue() = "unknown" and
84+
is.getCondition() = ma.getParent*()
85+
)
86+
)
87+
}
88+
}
89+
90+
/** A data flow sink for sql operation. */
91+
private class SqlOperationSink extends UseOfLessTrustedSink {
92+
SqlOperationSink() { this instanceof QueryInjectionSink }
93+
}
94+
95+
/** A data flow sink for log operation. */
96+
private class LogOperationSink extends UseOfLessTrustedSink {
97+
LogOperationSink() { exists(LoggingCall lc | lc.getAnArgument() = this.asExpr()) }
98+
}
99+
100+
/** A data flow sink for local output. */
101+
private class PrintSink extends UseOfLessTrustedSink {
102+
PrintSink() {
22103
exists(MethodAccess ma |
23104
ma.getMethod().getName() in ["print", "println"] and
24105
(
@@ -27,8 +108,6 @@ class UseOfLessTrustedSink extends DataFlow::Node {
27108
) and
28109
ma.getAnArgument() = this.asExpr()
29110
)
30-
or
31-
exists(LoggingCall lc | lc.getAnArgument() = this.asExpr())
32111
}
33112
}
34113

@@ -39,50 +118,3 @@ class SplitMethod extends Method {
39118
this.hasQualifiedName("java.lang", "String", "split")
40119
}
41120
}
42-
43-
/**
44-
* A call to the ServletRequest.getHeader method and the argument are
45-
* `wl-proxy-client-ip`/`proxy-client-ip`/`http_client_ip`/`http_x_forwarded_for`/`x-real-ip`.
46-
*/
47-
class HeaderIpCall extends MethodAccess {
48-
HeaderIpCall() {
49-
this.getMethod().hasName("getHeader") and
50-
this.getMethod()
51-
.getDeclaringType()
52-
.getASupertype*()
53-
.hasQualifiedName("javax.servlet", "ServletRequest") and
54-
this.getArgument(0).toString().toLowerCase() in [
55-
"\"wl-proxy-client-ip\"", "\"proxy-client-ip\"", "\"http_client_ip\"",
56-
"\"http_x_forwarded_for\"", "\"x-real-ip\""
57-
]
58-
}
59-
}
60-
61-
/** A call to `ServletRequest.getRemoteAddr` method. */
62-
class RemoteAddrCall extends MethodAccess {
63-
RemoteAddrCall() {
64-
this.getMethod().hasName("getRemoteAddr") and
65-
this.getMethod()
66-
.getDeclaringType()
67-
.getASupertype*()
68-
.hasQualifiedName("javax.servlet", "ServletRequest")
69-
}
70-
}
71-
72-
/** The first one in the method to get the ip value through `x-forwarded-for`. */
73-
predicate xffIsFirstGet(Node node) {
74-
exists(HeaderIpCall hic |
75-
node.getEnclosingCallable() = hic.getEnclosingCallable() and
76-
node.getLocation().getEndLine() < hic.getLocation().getEndLine()
77-
)
78-
or
79-
exists(RemoteAddrCall rac |
80-
node.getEnclosingCallable() = rac.getEnclosingCallable() and
81-
node.getLocation().getEndLine() < rac.getLocation().getEndLine()
82-
)
83-
or
84-
not exists(HeaderIpCall hic, RemoteAddrCall rac |
85-
node.getEnclosingCallable() = hic.getEnclosingCallable() and
86-
node.getEnclosingCallable() = rac.getEnclosingCallable()
87-
)
88-
}

0 commit comments

Comments
 (0)