Skip to content

Commit a305d48

Browse files
committed
Add CORS query to C# pack
1 parent 2756b60 commit a305d48

14 files changed

+486
-0
lines changed

csharp/lib/ghsl/ConstExpressions.qll

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import csharp
2+
3+
/**
4+
* Holds if the `Callable` c throws any exception other than `ThrowsArgumentNullException`
5+
*/
6+
predicate callableMayThrowException(Callable c) {
7+
exists(ThrowStmt thre | c = thre.getEnclosingCallable()) and
8+
not callableOnlyThrowsArgumentNullException(c)
9+
}
10+
11+
/**
12+
* Holds if any exception being thrown by the callable is of type `System.ArgumentNullException`
13+
* It will also hold if no exceptions are thrown by the callable
14+
*/
15+
predicate callableOnlyThrowsArgumentNullException(Callable c) {
16+
forall(ThrowElement thre | c = thre.getEnclosingCallable() |
17+
thre.getThrownExceptionType().hasFullyQualifiedName("System", "ArgumentNullException")
18+
)
19+
}
20+
21+
/**
22+
* Hold if the `Expr` e is a `BoolLiteral` with value true,
23+
* the expression has a predictable value == `true`,
24+
* or if it is a `ConditionalExpr` where the `then` and `else` expressions meet `isExpressionAlwaysTrue` criteria
25+
*/
26+
predicate isExpressionAlwaysTrue(Expr e) {
27+
e.(BoolLiteral).getBoolValue() = true
28+
or
29+
e.getValue() = "true"
30+
or
31+
e instanceof ConditionalExpr and
32+
isExpressionAlwaysTrue(e.(ConditionalExpr).getThen()) and
33+
isExpressionAlwaysTrue(e.(ConditionalExpr).getElse())
34+
or
35+
exists(Callable callable |
36+
callableHasAReturnStmtAndAlwaysReturnsTrue(callable) and
37+
callable.getACall() = e
38+
)
39+
}
40+
41+
/**
42+
* Holds if the lambda expression `le` always returns true
43+
*/
44+
predicate lambdaExprReturnsOnlyLiteralTrue(AnonymousFunctionExpr le) {
45+
isExpressionAlwaysTrue(le.getExpressionBody())
46+
}
47+
48+
/**
49+
* Holds if the callable has a return statement and it always returns true for all such statements
50+
*/
51+
predicate callableHasAReturnStmtAndAlwaysReturnsTrue(Callable c) {
52+
c.getReturnType() instanceof BoolType and
53+
not callableMayThrowException(c) and
54+
forex(ReturnStmt rs | rs.getEnclosingCallable() = c |
55+
rs.getNumberOfChildren() = 1 and
56+
isExpressionAlwaysTrue(rs.getChildExpr(0))
57+
)
58+
}
59+
60+
/**
61+
* Holds if `c` always returns `true`.
62+
*/
63+
predicate alwaysReturnsTrue(Callable c) {
64+
callableHasAReturnStmtAndAlwaysReturnsTrue(c)
65+
or
66+
lambdaExprReturnsOnlyLiteralTrue(c)
67+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @name Credentialed CORS Misconfiguration
3+
* @description Allowing any origin while allowing credentials may result in security issues as third party website may be able to
4+
* access private resources.
5+
* @kind problem
6+
* @problem.severity error
7+
* @security-severity 7.5
8+
* @precision high
9+
* @id cs/web/cors-misconfiguration
10+
* @tags security
11+
* external/cwe/cwe-942
12+
*/
13+
14+
import csharp
15+
import CorsMisconfigurationLib
16+
17+
/**
18+
* Holds if the application allows an origin using "*" origin.
19+
*/
20+
private predicate allowAnyOrigin(MethodCall m) {
21+
m.getTarget()
22+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
23+
"AllowAnyOrigin")
24+
}
25+
26+
/**
27+
* Holds if the application uses a vulnerable CORS policy.
28+
*/
29+
private predicate hasDangerousOrigins(MethodCall m) {
30+
m.getTarget()
31+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
32+
"WithOrigins") and
33+
exists(StringLiteral idStr |
34+
idStr.getValue().toLowerCase().matches(["null", "*"]) and
35+
TaintTracking::localExprTaint(idStr, m.getAnArgument())
36+
)
37+
}
38+
39+
from MethodCall add_policy, MethodCall child
40+
where
41+
(
42+
usedPolicy(add_policy) and
43+
// Misconfigured origin affects used policy
44+
getCallableFromExpr(add_policy.getArgument(1)).calls*(child.getTarget())
45+
or
46+
add_policy
47+
.getTarget()
48+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions",
49+
"AddDefaultPolicy") and
50+
// Misconfigured origin affects added default policy
51+
getCallableFromExpr(add_policy.getArgument(0)).calls*(child.getTarget())
52+
) and
53+
(hasDangerousOrigins(child) or allowAnyOrigin(child))
54+
select add_policy, "The following CORS policy may allow requests from 3rd party websites"
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
9+
A server can send the
10+
<code>"Access-Control-Allow-Credentials"</code> CORS header to control
11+
when a browser may send user credentials in Cross-Origin HTTP
12+
requests.
13+
14+
</p>
15+
<p>
16+
17+
When the <code>Access-Control-Allow-Credentials</code> header
18+
is <code>"true"</code>, the <code>Access-Control-Allow-Origin</code>
19+
header must have a value different from <code>"*"</code> in order to
20+
make browsers accept the header. Therefore, to allow multiple origins
21+
for Cross-Origin requests with credentials, the server must
22+
dynamically compute the value of the
23+
<code>"Access-Control-Allow-Origin"</code> header. Computing this
24+
header value from information in the request to the server can
25+
therefore potentially allow an attacker to control the origins that
26+
the browser sends credentials to.
27+
28+
</p>
29+
30+
31+
32+
</overview>
33+
34+
<recommendation>
35+
<p>
36+
37+
When the <code>Access-Control-Allow-Credentials</code> header
38+
value is <code>"true"</code>, a dynamic computation of the
39+
<code>Access-Control-Allow-Origin</code> header must involve
40+
sanitization if it relies on user-controlled input.
41+
42+
43+
</p>
44+
<p>
45+
46+
Since the <code>"null"</code> origin is easy to obtain for an
47+
attacker, it is never safe to use <code>"null"</code> as the value of
48+
the <code>Access-Control-Allow-Origin</code> header when the
49+
<code>Access-Control-Allow-Credentials</code> header value is
50+
<code>"true"</code>.
51+
52+
</p>
53+
</recommendation>
54+
55+
<example>
56+
<p>
57+
58+
In the example below, the server allows the browser to send
59+
user credentials in a Cross-Origin request. The request header
60+
<code>origins</code> controls the allowed origins for such a
61+
Cross-Origin request.
62+
63+
</p>
64+
65+
<sample src="examples/CorsBad.cs"/>
66+
67+
<p>
68+
69+
This is not secure, since an attacker can choose the value of
70+
the <code>origin</code> request header to make the browser send
71+
credentials to their own server. The use of a allowlist containing
72+
allowed origins for the Cross-Origin request fixes the issue:
73+
74+
</p>
75+
76+
<sample src="examples/CorsGood.cs"/>
77+
</example>
78+
79+
<references>
80+
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin">CORS, Access-Control-Allow-Origin</a>.</li>
81+
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials">CORS, Access-Control-Allow-Credentials</a>.</li>
82+
<li>PortSwigger: <a href="http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html">Exploiting CORS Misconfigurations for Bitcoins and Bounties</a></li>
83+
<li>W3C: <a href="https://w3c.github.io/webappsec-cors-for-developers/#resources">CORS for developers, Advice for Resource Owners</a></li>
84+
</references>
85+
</qhelp>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* @name Credentialed CORS Misconfiguration
3+
* @description Allowing any origin while allowing credentials may result in security issues as third party website may be able to
4+
* access private resources.
5+
* @kind problem
6+
* @problem.severity error
7+
* @security-severity 7.5
8+
* @precision high
9+
* @id cs/web/cors-misconfiguration-credentials
10+
* @tags security
11+
* external/cwe/cwe-942
12+
*/
13+
14+
import csharp
15+
import CorsMisconfigurationLib
16+
17+
/** A call to `CorsPolicyBuilder.AllowCredentials`. */
18+
class AllowsCredentials extends MethodCall {
19+
AllowsCredentials() {
20+
this.getTarget()
21+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
22+
"AllowCredentials")
23+
}
24+
}
25+
26+
from MethodCall add_policy, MethodCall setIsOriginAllowed, AllowsCredentials allowsCredentials
27+
where
28+
(
29+
getCallableFromExpr(add_policy.getArgument(1)).calls*(setIsOriginAllowed.getTarget()) and
30+
usedPolicy(add_policy) and
31+
getCallableFromExpr(add_policy.getArgument(1)).calls*(allowsCredentials.getTarget())
32+
or
33+
add_policy
34+
.getTarget()
35+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions",
36+
"AddDefaultPolicy") and
37+
getCallableFromExpr(add_policy.getArgument(0)).calls*(setIsOriginAllowed.getTarget()) and
38+
getCallableFromExpr(add_policy.getArgument(0)).calls*(allowsCredentials.getTarget())
39+
) and
40+
setIsOriginAllowedReturnsTrue(setIsOriginAllowed)
41+
select add_policy,
42+
"The following CORS policy may allow credentialed requests from 3rd party websites"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import csharp
2+
import DataFlow
3+
import ghsl.ConstExpressions
4+
5+
/**
6+
* Gets the actual callable corresponding to the expression `e`.
7+
*/
8+
Callable getCallableFromExpr(Expr e) {
9+
exists(Expr dcArg | dcArg = e.(DelegateCreation).getArgument() |
10+
result = dcArg.(CallableAccess).getTarget() or
11+
result = dcArg.(AnonymousFunctionExpr)
12+
)
13+
or
14+
result = e
15+
}
16+
17+
/**
18+
* Holds if SetIsOriginAllowed always returns true. This sets the Access-Control-Allow-Origin to the requester
19+
*/
20+
predicate setIsOriginAllowedReturnsTrue(MethodCall mc) {
21+
mc.getTarget()
22+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicyBuilder",
23+
"SetIsOriginAllowed") and
24+
alwaysReturnsTrue(mc.getArgument(0))
25+
}
26+
27+
/**
28+
* Holds if UseCors is called with the relevant cors policy
29+
*/
30+
predicate usedPolicy(MethodCall add_policy) {
31+
exists(MethodCall uc |
32+
uc.getTarget()
33+
.hasFullyQualifiedName("Microsoft.AspNetCore.Builder.CorsMiddlewareExtensions", "UseCors") and
34+
(
35+
// Same hardcoded name
36+
uc.getArgument(1).getValue() = add_policy.getArgument(0).getValue() or
37+
// Same variable access
38+
uc.getArgument(1).(VariableAccess).getTarget() =
39+
add_policy.getArgument(0).(VariableAccess).getTarget() or
40+
DataFlow::localExprFlow(add_policy.getArgument(0), uc.getArgument(1))
41+
)
42+
) and
43+
add_policy
44+
.getTarget()
45+
.hasFullyQualifiedName("Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions", "AddPolicy")
46+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Leaf.Middlewares;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Hosting;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Hosting;
7+
8+
namespace Leaf
9+
{
10+
public class Startup
11+
{
12+
public Startup(IConfiguration configuration)
13+
{
14+
Configuration = configuration;
15+
}
16+
17+
public IConfiguration Configuration { get; }
18+
19+
// This method gets called by the runtime. Use this method to add services to the container.
20+
public void ConfigureServices(IServiceCollection services)
21+
{
22+
services.AddControllers();
23+
//services.AddTransient<MySqlConnection>(_ => new MySqlConnection(Configuration["ConnectionStrings:Default"]));
24+
services.AddControllersWithViews()
25+
.AddNewtonsoftJson(options =>
26+
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
27+
);
28+
29+
services.AddCors(options => {
30+
options.AddPolicy("AllowPolicy", builder => builder
31+
.WithOrigins("null")
32+
.AllowCredentials()
33+
.AllowAnyMethod()
34+
.AllowAnyHeader());
35+
});
36+
37+
}
38+
39+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
40+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
41+
{
42+
app.UseRouting();
43+
44+
app.UseCors("AllowPolicy");
45+
46+
app.UseRequestResponseLogging();
47+
48+
if (env.IsDevelopment())
49+
{
50+
app.UseDeveloperExceptionPage();
51+
}
52+
53+
app.UseHttpsRedirection();
54+
55+
app.UseAuthorization();
56+
57+
app.UseEndpoints(endpoints =>
58+
{
59+
endpoints.MapControllers();
60+
});
61+
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)