Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#: Add experimental queries. #72

Merged
merged 5 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions csharp/src/security/CWE-099/TaintedWebClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.IO;
using System.Web;
using System.Net;

public class TaintedWebClientHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
String url = ctx.Request.QueryString["domain"];

// BAD: This could read any file on the filesystem. (../../../../etc/passwd)
using(WebClient client = new WebClient()) {
ctx.Response.Write(client.DownloadString(url));
}

// BAD: This could still read any file on the filesystem. (https://../../../../etc/passwd)
if (url.StartsWith("https://")){
using(WebClient client = new WebClient()) {
ctx.Response.Write(client.DownloadString(url));
}
}

// GOOD: IsWellFormedUriString ensures that it is a valid URL
if (Uri.IsWellFormedUriString(url, UriKind.Absolute)){
using(WebClient client = new WebClient()) {
ctx.Response.Write(client.DownloadString(url));
}
}
}
}
58 changes: 58 additions & 0 deletions csharp/src/security/CWE-099/TaintedWebClient.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The WebClient class provides a variety of methods for data transmission and
communication with a particular URI. Despite of the class' naming convention,
the URI scheme can also identify local resources, not only remote ones. Tainted
by user-supplied input, the URI can be leveraged to access resources available
on the local file system, therefore leading to the disclosure of sensitive
information. This can be trivially achieved by supplying path traversal
sequences (../) followed by an existing directory or file path.</p>

<p>Sanitization of user-supplied URI values using the
<code>StartsWith("https://")</code> method is deemed insufficient in preventing
arbitrary file reads. This is due to the fact that .NET ignores the protocol
handler (https in this case) in URIs like the following:
"https://../../../../etc/passwd".</p>

</overview>
<recommendation>

<p>Validate user input before using it to ensure that is a URI of an external
resource and not a local one.
Potential solutions:</p>

<ul>
<li>Sanitize potentially tainted paths using
<code>System.Uri.IsWellFormedUriString</code>.</li>
</ul>

</recommendation>
<example>

<p>In the first example, a domain name is read from a <code>HttpRequest</code>
and then this domain is requested using the method <code>DownloadString</code>.
However, a malicious user could enter a local path - for example,
"../../../etc/passwd" instead of a domain.
In the second example, it appears that the user is restricted to the HTTPS
protocol handler. However, a malicious user could still enter a local path,
since as explained above the protocol handler will be ignored by .net. For
example, the string "https://../../../etc/passwd" will result in the code
reading the file located at "/etc/passwd", which is the system's password file.
This file would then be sent back to the user, giving them access to all the
system's passwords.</p>

<sample src="TaintedWebClient.cs" />

</example>
<references>

<li>
OWASP:
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
</li>

</references>
</qhelp>
23 changes: 23 additions & 0 deletions csharp/src/security/CWE-099/TaintedWebClient.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @name Uncontrolled data used in a WebClient
* @description The WebClient class allows developers to request resources,
* accessing resources influenced by users can allow an attacker to access local files.
* @kind path-problem
* @problem.severity error
* @precision high
* @id githubsecuritylab/cs/webclient-path-injection
* @tags security
* external/cwe/cwe-099
* external/cwe/cwe-023
* external/cwe/cwe-036
* external/cwe/cwe-073
*/

import csharp
import TaintedWebClientLib
import TaintedWebClient::PathGraph

from TaintedWebClient::PathNode source, TaintedWebClient::PathNode sink
where TaintedWebClient::flowPath(source, sink)
select sink.getNode(), source, sink, "A method of WebClient depepends on a $@.", source.getNode(),
"user-provided value"
90 changes: 90 additions & 0 deletions csharp/src/security/CWE-099/TaintedWebClientLib.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import csharp
import semmle.code.csharp.frameworks.system.Net
import semmle.code.csharp.frameworks.System
import semmle.code.csharp.security.dataflow.flowsources.FlowSources
import semmle.code.csharp.security.Sanitizers

//If this leaves experimental this should probably go in semmle.code.csharp.frameworks.system.Net
/** The `System.Net.WebClient` class. */
class SystemNetWebClientClass extends SystemNetClass {
SystemNetWebClientClass() { this.hasName("WebClient") }

/** Gets the `DownloadString` method. */
Method getDownloadStringMethod() { result = this.getAMethod("DownloadString") }
}

//If this leaves experimental this should probably go in semmle.code.csharp.frameworks.System
//Extend the already existent SystemUriClass to not touch the stdlib.
/** The `System.Uri` class. */
class SystemUriClassExtra extends SystemUriClass {
/** Gets the `IsWellFormedUriString` method. */
Method getIsWellFormedUriStringMethod() { result = this.getAMethod("IsWellFormedUriString") }
}

//If this leaves experimental this should probably go in semmle.code.csharp.frameworks.system
/**
* A data flow source for uncontrolled data in path expression vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }

/**
* A data flow sink for uncontrolled data in path expression vulnerabilities.
*/
abstract class Sink extends DataFlow::ExprNode { }

/**
* A sanitizer for uncontrolled data in path expression vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::ExprNode { }

/**
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
*/
private module TaintedWebClientConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }

predicate isSink(DataFlow::Node sink) { sink instanceof Sink }

predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}

/**
* A taint-tracking module for uncontrolled data in path expression vulnerabilities.
*/
module TaintedWebClient = TaintTracking::Global<TaintedWebClientConfig>;

/**
* DEPRECATED: Use `ThreatModelSource` instead.
*
* A source of remote user input.
*/
deprecated class RemoteSource extends DataFlow::Node instanceof RemoteFlowSource { }

/** A source supported by the current threat model. */
class ThreatModelSource extends Source instanceof ActiveThreatModelSource { }

/**
* A path argument to a `WebClient` method call that has an address argument.
*/
class WebClientSink extends Sink {
WebClientSink() {
exists(Method m | m = any(SystemNetWebClientClass f).getAMethod() |
this.getExpr() = m.getACall().getArgumentForName("address")
)
}
}

/**
* A call to `System.Uri.IsWellFormedUriString` that is considered to sanitize the input.
*/
class RequestMapPathSanitizer extends Sanitizer {
RequestMapPathSanitizer() {
exists(Method m | m = any(SystemUriClassExtra uri).getIsWellFormedUriStringMethod() |
this.getExpr() = m.getACall().getArgument(0)
)
}
}

private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }

private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
51 changes: 51 additions & 0 deletions csharp/src/security/CWE-1004/CookieWithoutHttpOnly.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>
Cookies without <code>HttpOnly</code> flag are accessible to JavaScript running in the same origin. In case of
Cross-Site Scripting (XSS) vulnerability the cookie can be stolen by malicious script.
</p>
</overview>

<recommendation>
<p>
Protect sensitive cookies, such as related to authentication, by setting <code>HttpOnly</code> to <code>true</code> to make
them not accessible to JavaScript. In ASP.NET case it is also possible to set the attribute via <code>&lt;httpCookies&gt;</code> element
of <code>web.config</code> with the attribute <code>httpOnlyCookies="true"</code>.
</p>
</recommendation>

<example>

<p>
In the example below <code>Microsoft.AspNetCore.Http.CookieOptions.HttpOnly</code> is set to <code>true</code>.
</p>

<sample src="httponlyflagcore.cs" />

<p>
In the following example <code>CookiePolicyOptions</code> are set programmatically to configure defaults.
</p>

<sample src="cookiepolicyoptions.cs" />

<p>
In the example below <code>System.Web.HttpCookie.HttpOnly</code> is set to <code>true</code>.
</p>

<sample src="httponlyflag.cs" />

</example>

<references>

<li><a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.cookieoptions.httponly">CookieOptions.HttpOnly Property,</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">Set-Cookie</a> Header,</li>
<li><a href="https://msdn.microsoft.com/en-us/library/system.web.httpcookie.httponly(v=vs.110).aspx">HttpCookie.HttpOnly Property,</a></li>
<li><a href="https://msdn.microsoft.com/library/ms228262%28v=vs.100%29.aspx">httpCookies Element,</a></li>

</references>
</qhelp>
104 changes: 104 additions & 0 deletions csharp/src/security/CWE-1004/CookieWithoutHttpOnly.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* @name 'HttpOnly' attribute is not set to true
* @description Omitting the 'HttpOnly' attribute for security sensitive data allows
* malicious JavaScript to steal it in case of XSS vulnerability. Always set
* 'HttpOnly' to 'true' to authentication related cookie to make it
* not accessible by JavaScript.
* @kind problem
* @problem.severity warning
* @precision high
* @id githubsecuritylab/cs/web/cookie-httponly-not-set
* @tags security
* external/cwe/cwe-1004
*/

import csharp
import semmle.code.asp.WebConfig
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.microsoft.AspNetCore
import security.dataflow.flowsources.AuthCookie

from Expr httpOnlySink
where
exists(Assignment a, Expr val |
httpOnlySink = a.getRValue() and
val.getValue() = "false" and
(
exists(ObjectCreation oc |
getAValueForProp(oc, a, "HttpOnly") = val and
(
oc.getType() instanceof SystemWebHttpCookie and
isCookieWithSensitiveName(oc.getArgument(0))
or
exists(MethodCall mc, MicrosoftAspNetCoreHttpResponseCookies iResponse |
oc.getType() instanceof MicrosoftAspNetCoreHttpCookieOptions and
iResponse.getAppendMethod() = mc.getTarget() and
isCookieWithSensitiveName(mc.getArgument(0)) and
// there is no callback `OnAppendCookie` that sets `HttpOnly` to true
not OnAppendCookieHttpOnlyTracking::flowTo(_) and
// Passed as third argument to `IResponseCookies.Append`
exists(DataFlow::Node creation, DataFlow::Node append |
CookieOptionsTracking::flow(creation, append) and
creation.asExpr() = oc and
append.asExpr() = mc.getArgument(2)
)
)
)
)
or
exists(PropertyWrite pw |
(
pw.getProperty().getDeclaringType() instanceof MicrosoftAspNetCoreHttpCookieBuilder or
pw.getProperty().getDeclaringType() instanceof
MicrosoftAspNetCoreAuthenticationCookiesCookieAuthenticationOptions
) and
pw.getProperty().getName() = "HttpOnly" and
a.getLValue() = pw and
DataFlow::localExprFlow(val, a.getRValue())
)
)
)
or
exists(Call c |
httpOnlySink = c and
(
exists(MicrosoftAspNetCoreHttpResponseCookies iResponse, MethodCall mc |
// default is not configured or is not set to `Always`
not getAValueForCookiePolicyProp("HttpOnly").getValue() = "1" and
// there is no callback `OnAppendCookie` that sets `HttpOnly` to true
not OnAppendCookieHttpOnlyTracking::flowTo(_) and
iResponse.getAppendMethod() = mc.getTarget() and
isCookieWithSensitiveName(mc.getArgument(0)) and
(
// `HttpOnly` property in `CookieOptions` passed to IResponseCookies.Append(...) wasn't set
exists(ObjectCreation oc |
oc = c and
oc.getType() instanceof MicrosoftAspNetCoreHttpCookieOptions and
not isPropertySet(oc, "HttpOnly") and
exists(DataFlow::Node creation |
CookieOptionsTracking::flow(creation, _) and
creation.asExpr() = oc
)
)
or
// IResponseCookies.Append(String, String) was called, `HttpOnly` is set to `false` by default
mc = c and
mc.getNumberOfArguments() < 3
)
)
or
exists(ObjectCreation oc |
oc = c and
oc.getType() instanceof SystemWebHttpCookie and
isCookieWithSensitiveName(oc.getArgument(0)) and
// the property wasn't explicitly set, so a default value from config is used
not isPropertySet(oc, "HttpOnly") and
// the default in config is not set to `true`
not exists(XmlElement element |
element instanceof HttpCookiesElement and
element.(HttpCookiesElement).isHttpOnlyCookies()
)
)
)
)
select httpOnlySink, "Cookie attribute 'HttpOnly' is not set to true."
12 changes: 12 additions & 0 deletions csharp/src/security/CWE-1004/cookiepolicyoptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
public class Startup
{
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCookiePolicy(new CookiePolicyOptions()
{
Secure = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always,
HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always
});
}
}
7 changes: 7 additions & 0 deletions csharp/src/security/CWE-1004/httponlyflag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class MyController : Controller
{
void Login()
{
var cookie = new System.Web.HttpCookie("cookieName") { HttpOnly = true };
}
}
8 changes: 8 additions & 0 deletions csharp/src/security/CWE-1004/httponlyflagcore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class MyController : Controller
{
void Login()
{
var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions() { HttpOnly = true };
Response.Cookies.Append("auth", "secret", cookieOptions);
}
}
Loading
Loading