diff --git a/go/ql/src/experimental/CWE-20/MemoryAllocationDos.qhelp b/go/ql/src/experimental/CWE-20/MemoryAllocationDos.qhelp new file mode 100644 index 000000000000..cf1e9802005e --- /dev/null +++ b/go/ql/src/experimental/CWE-20/MemoryAllocationDos.qhelp @@ -0,0 +1,17 @@ + + + + + +Memory allocation size from user-controlled data can allow an attacker to uses a lot of + server memory. This can result the server is unavailable or crash due to failure to allocate huge memory. + + + + + +OWASP: Denial_of_Service. + + diff --git a/go/ql/src/experimental/CWE-20/MemoryAllocationDos.ql b/go/ql/src/experimental/CWE-20/MemoryAllocationDos.ql new file mode 100644 index 000000000000..f1f7674fdfb9 --- /dev/null +++ b/go/ql/src/experimental/CWE-20/MemoryAllocationDos.ql @@ -0,0 +1,88 @@ +/** + * @name User-controlled size of a memory allocation operation + * @description Allowing a user to influence the size of a memory allocation could lead to denial of service attacks + * @kind path-problem + * @problem.severity error + * @id go/allow-memory-size-injection + * @tag security + * experimental + * external/cwe/cwe-023 + */ + +import go +import semmle.go.security.AllocationSizeOverflow +import semmle.go.frameworks.Stdlib +import DataFlow::PathGraph + +abstract class Source extends DataFlow::Node { } + +abstract class Sanitizer extends DataFlow::Node { } + +private SsaWithFields getAComparedVar() { + exists(RelationalComparisonExpr e | + not e.getLesserOperand().isConst() or e.getLesserOperand().getIntValue() != 0 + | + result.getAUse().asExpr() = e.getGreaterOperand() + ) +} + +class CompSanitizer extends Sanitizer { + CompSanitizer() { this = getAComparedVar().similar().getAUse() } +} + +class FieldReadSanitizer extends Sanitizer { + FieldReadSanitizer() { + exists(DataFlow::FieldReadNode f | + f.asExpr() = any(RelationalComparisonExpr e).getGreaterOperand() + | + this.asInstruction() = f.asInstruction().getASuccessor+() + ) + } +} + +private predicate compCheckGuard(DataFlow::Node g, Expr e, boolean outcome) { + e = g.(DataFlow::RelationalComparisonNode).getAnOperand().asExpr() and + outcome = [true, false] +} + +class CompSanitizerGuard extends Sanitizer { + CompSanitizerGuard() { this = DataFlow::BarrierGuard::getABarrierNode() } +} + +string macaronContextPath() { result = package("gopkg.in/macaron", "") } + +class UntrustedFlowAsSource extends Source instanceof UntrustedFlowSource { } + +class MacaronInputSource extends Source, DataFlow::MethodCallNode { + MacaronInputSource() { + this.getTarget().hasQualifiedName(macaronContextPath(), "Context", ["QueryInt", "QueryInt64"]) + } +} + +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "MemoryAllocationDos" } + + override predicate isSink(DataFlow::Node sink) { + sink instanceof AllocationSizeOverflow::AllocationSize + } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(DataFlow::CallNode call | + node2 = call.getResult(0) and + call.getTarget() instanceof IntegerParser::Range and + call.getArgument(0) = node1 + ) + } + + override predicate isSanitizer(DataFlow::Node node) { + super.isSanitizer(node) or + node instanceof Sanitizer + } +} + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "Untrusted memory allocation size depends on a $@.", + source.getNode(), "user-provided value" diff --git a/go/ql/src/experimental/CWE-20/MemoryAllocationDosBad.go b/go/ql/src/experimental/CWE-20/MemoryAllocationDosBad.go new file mode 100644 index 000000000000..82f3a800f480 --- /dev/null +++ b/go/ql/src/experimental/CWE-20/MemoryAllocationDosBad.go @@ -0,0 +1,33 @@ +package server + +import ( + "net/http" + "strconv" +) + +type testStruct struct { + PageSize int +} + + +func serve_bad() { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + + c, _ := strconv.Atoi(r.Form.Get("count")) + _ = make([]int, c) + + d, _ := strconv.Atoi(r.Form.Get("count2")) + if(d <= 0){ + d = 20 + } + _ = make([]int, d) + + e, _ := strconv.Atoi(r.Form.Get("count3")) + if(e > 0){ + // some extra operation + } + _ = make([]int, e) + + }) + http.ListenAndServe(":80", nil) +} diff --git a/go/ql/src/experimental/CWE-20/MemoryAllocationDosGood.go b/go/ql/src/experimental/CWE-20/MemoryAllocationDosGood.go new file mode 100644 index 000000000000..1fdeef401a40 --- /dev/null +++ b/go/ql/src/experimental/CWE-20/MemoryAllocationDosGood.go @@ -0,0 +1,34 @@ +package server + +import ( + "net/http" + "strconv" +) + +type testStruct struct { + PageSize int +} + + +func getInt(r *http.Request) int { + ret, _ := strconv.Atoi(r.Form.Get("count3")) + return ret +} + +func serve_good() { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + + c, _ := strconv.Atoi(r.Form.Get("count")) + if c > 200 { + c = 200 + } + _ = make([]int, c) + + f, _ := strconv.Atoi(r.Form.Get("count4")) + if(f < 20){ + _ = make([]int, f) + } + + }) + http.ListenAndServe(":80", nil) +} diff --git a/go/ql/test/experimental/CWE-20/MemoryAllocationDos.expected b/go/ql/test/experimental/CWE-20/MemoryAllocationDos.expected new file mode 100644 index 000000000000..55300caa56f3 --- /dev/null +++ b/go/ql/test/experimental/CWE-20/MemoryAllocationDos.expected @@ -0,0 +1,16 @@ +edges +| MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | MemoryAllocationDosBad.go:17:19:17:19 | c | +| MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | MemoryAllocationDosBad.go:23:19:23:19 | d | +| MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | MemoryAllocationDosBad.go:29:19:29:19 | e | +nodes +| MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | semmle.label | selection of Form | +| MemoryAllocationDosBad.go:17:19:17:19 | c | semmle.label | c | +| MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | semmle.label | selection of Form | +| MemoryAllocationDosBad.go:23:19:23:19 | d | semmle.label | d | +| MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | semmle.label | selection of Form | +| MemoryAllocationDosBad.go:29:19:29:19 | e | semmle.label | e | +subpaths +#select +| MemoryAllocationDosBad.go:17:19:17:19 | c | MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | MemoryAllocationDosBad.go:17:19:17:19 | c | Untrusted memory allocation size depends on a $@. | MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | user-provided value | +| MemoryAllocationDosBad.go:23:19:23:19 | d | MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | MemoryAllocationDosBad.go:23:19:23:19 | d | Untrusted memory allocation size depends on a $@. | MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | user-provided value | +| MemoryAllocationDosBad.go:29:19:29:19 | e | MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | MemoryAllocationDosBad.go:29:19:29:19 | e | Untrusted memory allocation size depends on a $@. | MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | user-provided value | diff --git a/go/ql/test/experimental/CWE-20/MemoryAllocationDos.qlref b/go/ql/test/experimental/CWE-20/MemoryAllocationDos.qlref new file mode 100644 index 000000000000..f663cd9bce72 --- /dev/null +++ b/go/ql/test/experimental/CWE-20/MemoryAllocationDos.qlref @@ -0,0 +1 @@ +experimental/CWE-20/MemoryAllocationDos.ql \ No newline at end of file diff --git a/go/ql/test/experimental/CWE-20/MemoryAllocationDosBad.go b/go/ql/test/experimental/CWE-20/MemoryAllocationDosBad.go new file mode 100644 index 000000000000..82f3a800f480 --- /dev/null +++ b/go/ql/test/experimental/CWE-20/MemoryAllocationDosBad.go @@ -0,0 +1,33 @@ +package server + +import ( + "net/http" + "strconv" +) + +type testStruct struct { + PageSize int +} + + +func serve_bad() { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + + c, _ := strconv.Atoi(r.Form.Get("count")) + _ = make([]int, c) + + d, _ := strconv.Atoi(r.Form.Get("count2")) + if(d <= 0){ + d = 20 + } + _ = make([]int, d) + + e, _ := strconv.Atoi(r.Form.Get("count3")) + if(e > 0){ + // some extra operation + } + _ = make([]int, e) + + }) + http.ListenAndServe(":80", nil) +} diff --git a/go/ql/test/experimental/CWE-20/MemoryAllocationDosGood.go b/go/ql/test/experimental/CWE-20/MemoryAllocationDosGood.go new file mode 100644 index 000000000000..4205c4ab7e5d --- /dev/null +++ b/go/ql/test/experimental/CWE-20/MemoryAllocationDosGood.go @@ -0,0 +1,29 @@ +package server + +import ( + "net/http" + "strconv" +) + +type testStruct struct { + PageSize int +} + + +func getInt(r *http.Request) int { + ret, _ := strconv.Atoi(r.Form.Get("count3")) + return ret +} + +func serve_good() { + http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { + + c, _ := strconv.Atoi(r.Form.Get("count")) + if c > 200 { + c = 200 + } + _ = make([]int, c) + + }) + http.ListenAndServe(":80", nil) +}
+Memory allocation size from user-controlled data can allow an attacker to uses a lot of + server memory. This can result the server is unavailable or crash due to failure to allocate huge memory. +