Add LDAP Injection query (incomplete)

This commit is contained in:
Maiky 2023-05-25 22:51:25 +02:00
Родитель 202037e925
Коммит 026d94c457
6 изменённых файлов: 261 добавлений и 8 удалений

Просмотреть файл

@ -212,8 +212,7 @@ module FileSystemWriteAccess {
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `FileSystemPermissionModification::Range` instead.
*/
class FileSystemPermissionModification extends DataFlow::Node instanceof FileSystemPermissionModification::Range
{
class FileSystemPermissionModification extends DataFlow::Node instanceof FileSystemPermissionModification::Range {
/**
* Gets an argument to this permission modification that is interpreted as a
* set of permissions.
@ -469,8 +468,7 @@ module Http {
}
}
private class RequestInputAccessAsRemoteFlowSource extends RemoteFlowSource::Range instanceof RequestInputAccess
{
private class RequestInputAccessAsRemoteFlowSource extends RemoteFlowSource::Range instanceof RequestInputAccess {
override string getSourceType() { result = this.(RequestInputAccess).getSourceType() }
}
@ -959,8 +957,7 @@ module Path {
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CookieSecurityConfigurationSetting::Range` instead.
*/
class CookieSecurityConfigurationSetting extends DataFlow::Node instanceof CookieSecurityConfigurationSetting::Range
{
class CookieSecurityConfigurationSetting extends DataFlow::Node instanceof CookieSecurityConfigurationSetting::Range {
/**
* Gets a description of how this cookie setting may weaken application security.
* This predicate has no results if the setting is considered to be safe.
@ -1040,8 +1037,7 @@ module Cryptography {
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `CryptographicOperation::Range` instead.
*/
class CryptographicOperation extends SC::CryptographicOperation instanceof CryptographicOperation::Range
{
class CryptographicOperation extends SC::CryptographicOperation instanceof CryptographicOperation::Range {
/** DEPRECATED: Use `getAlgorithm().isWeak() or getBlockMode().isWeak()` instead */
deprecated predicate isWeak() { super.isWeak() }
}
@ -1129,3 +1125,69 @@ module TemplateRendering {
abstract DataFlow::Node getTemplate();
}
}
/**
* A data-flow node that constructs a LDAP query.
*
* Often, it is worthy of an alert if an LDAP query is constructed such that
* executing it would be a security risk.
*
* If it is important that the query is executed, use `LdapExecution`.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `LdapConstruction::Range` instead.
*/
class LdapConstruction extends DataFlow::Node instanceof LdapConstruction::Range {
/** Gets the argument that specifies the query to be constructed. */
DataFlow::Node getQuery() { result = super.getQuery() }
}
/** Provides a class for modeling new LDAP query construction APIs. */
module LdapConstruction {
/**
* A data-flow node that constructs a LDAP query.
*
* Often, it is worthy of an alert if an LDAP query is constructed such that
* executing it would be a security risk.
*
* If it is important that the query is executed, use `LdapExecution`.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `LdapConstruction` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets the argument that specifies the query to be constructed. */
abstract DataFlow::Node getQuery();
}
}
/**
* A data-flow node that executes LDAP queries.
*
* If the context of interest is such that merely constructing a LDAP query
* would be valuable to report, consider using `LdapConstruction`.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `LdapExecution::Range` instead.
*/
class LdapExecution extends DataFlow::Node instanceof LdapExecution::Range {
/** Gets the argument that specifies the query to be executed. */
DataFlow::Node getQuery() { result = super.getQuery() }
}
/** Provides a class for modeling new LDAP query execution APIs. */
module LdapExecution {
/**
* A data-flow node that executes LDAP queries.
*
* If the context of interest is such that merely constructing a LDAP query
* would be valuable to report, consider using `LdapConstruction`.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `LdapExecution` instead.
*/
abstract class Range extends DataFlow::Node {
//** Gets the argument that specifies the query to be executed. */
abstract DataFlow::Node getQuery();
}
}

Просмотреть файл

@ -32,3 +32,4 @@ private import codeql.ruby.frameworks.Slim
private import codeql.ruby.frameworks.Sinatra
private import codeql.ruby.frameworks.Twirp
private import codeql.ruby.frameworks.Sqlite3
private import codeql.ruby.frameworks.Ldap

Просмотреть файл

@ -0,0 +1,79 @@
/**
* Provides modeling for `net-ldap` a ruby library for LDAP.
*/
private import ruby
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.Concepts
private import codeql.ruby.CFG
private import codeql.ruby.AST
private import codeql.ruby.Concepts
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.internal.DataFlowDispatch
private import codeql.ruby.dataflow.internal.DataFlowPrivate
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.Stdlib
private import codeql.ruby.frameworks.Core
/**
* Provides modeling for `net-ldap` a ruby library for LDAP.
*/
module NetLdap {
/**
* Flow summary for `Net::LDAP.new`. This method establishes a connection to a LDAP server.
*/
private class LdapSummary extends SummarizedCallable {
LdapSummary() { this = "Net::LDAP.new" }
override MethodCall getACall() { result = any(LdapConnection l).asExpr().getExpr() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
private class LdapConnection extends DataFlow::CallNode {
LdapConnection() {
this = API::getTopLevelMember("Net").getMember("LDAP").getAnInstantiation() or
this = API::getTopLevelMember("Net").getMember("LDAP").getAMethodCall(["open"])
}
}
/** A call to ` Net::LDAP::Filter.eq`, considered as a LDAP construction. */
private class NetLdapConstruction extends LdapConstruction::Range, DataFlow::CallNode {
DataFlow::Node query;
NetLdapConstruction() {
this =
API::getTopLevelMember("Net").getMember("LDAP").getMember("Filter").getAMethodCall(["eq"]) and
query = this.getArgument([0, 1])
}
override DataFlow::Node getQuery() { result = query }
}
/** A call considered as a LDAP execution. */
private class NetLdapExecution extends LdapExecution::Range, DataFlow::CallNode {
DataFlow::Node query;
NetLdapExecution() {
// detecta cuando la query es una string pej
// ldap.search(base: "ou=#{name},dc=example,dc=com"
exists(LdapConnection ldapConnection |
this = ldapConnection.getAMethodCall("search") and
query = this.getKeywordArgument(_)
)
or
// ignora esta parte
exists(LdapConnection ldapConnection, NetLdapConstruction ldapConstruction |
this = ldapConnection.getAMethodCall("search") and
ldapConstruction = this.getKeywordArgument(_) and
query = ldapConstruction
)
}
override DataFlow::Node getQuery() { result = query }
}
}

Просмотреть файл

@ -0,0 +1,64 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* LDAP Injections, as well as extension points for adding your own
*/
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.BarrierGuards
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.ApiGraphs
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.CFG
/**
* Provides default sources, sinks and sanitizers for detecting
* LDAP Injections, as well as extension points for adding your own
*/
module LdapInjection {
/** A data flow source for LDAP Injection vulnerabilities */
abstract class Source extends DataFlow::Node { }
/** A data flow sink for LDAP Injection vulnerabilities */
abstract class Sink extends DataFlow::Node { }
/** A sanitizer for LDAP Injection vulnerabilities. */
abstract class Sanitizer extends DataFlow::Node { }
/**
* Additional taint steps for "LDAP Injection" vulnerabilities.
*/
predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(API::Node n, API::Node n2 |
n = API::getTopLevelMember("Net").getMember("LDAP").getMember("Filter")
|
n2 = API::getTopLevelMember("Net").getMember("LDAP") and
nodeTo = n2.getAMethodCall(["new"]).getAMethodCall(["search"]) and
nodeFrom = n.getAMethodCall(["eq"]).getArgument([0, 1])
)
}
/**
* A source of remote user input, considered as a flow source.
*/
private class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
/**
* An LDAP query execution considered as a flow sink.
*/
private class LdapExecutionAsSink extends Sink {
LdapExecutionAsSink() { this = any(LdapExecution l).getQuery() }
}
/**
* A comparison with a constant string, considered as a sanitizer-guard.
*/
private class StringConstCompareAsSanitizerGuard extends Sanitizer, StringConstCompareBarrier { }
/**
* An inclusion check against an array of constant strings, considered as a
* sanitizer-guard.
*/
private class StringConstArrayInclusionCallAsSanitizer extends Sanitizer,
StringConstArrayInclusionCallBarrier { }
}

Просмотреть файл

@ -0,0 +1,26 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* LDAP Injections, as well as extension points for adding your own
*/
private import codeql.ruby.DataFlow
private import codeql.ruby.TaintTracking
import LdapInjectionCustomizations
import LdapInjectionCustomizations::LdapInjection
/**
* A taint-tracking configuration for detecting LDAP Injections vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "LdapInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node source) { source instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
LdapInjection::isAdditionalTaintStep(node1, node2)
}
}

Просмотреть файл

@ -0,0 +1,21 @@
/**
* @name LDAP Injection
* @description Building an LDAP query from user-controlled sources is vulnerable to insertion of
* malicious LDAP code by the user.
* @kind path-problem
* @problem.severity error
* @security-severity 9.8
* @precision high
* @id rb/ldap-injection
* @tags security
* external/cwe/cwe-090
*/
import codeql.ruby.DataFlow
import codeql.ruby.security.LdapInjectionQuery
import DataFlow::PathGraph
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "This LDAP query depends on a $@.", source.getNode(),
"user-provided value"