GRPC attack surface during C2 development

bopin2020
9 min readOct 14, 2021

--

Claim: There was not an English Grammer someone so please ignore any grammatical mistakes. Apologize for my mistakes in advance.

In order to have no any obfuscation when called C2, let’s have an convension =>

Server -> TeamServer <- beacon

Server side, along with GUI, an operator directly handled on it.

TeamServer, usually running in external IP machine on the wire, hackers interact with beaconto send data on it.

Beacon, Oh it’s obvious in our brains,running in compromised machine or victims

Nowadays, C2 development gets more popular rather than the past time. Hence,I gave myself into here. However I actually was lacking of important knowledges such as system as well as called windows internals things. I started to learn C# language from the last year,so developing one C2 for me is a pretty stupendous challenge and even for Server side and TeamServer side (ASP.NET Core for C# guys) those were a bit easy than beacon or implants which was developed by C/C++ or go(native language). Even @RastaMouse wrote SharpC2 beacon who called drone made by C#. That’s helpful for beginners. Thanks you very much.

Anyway, go back to our topics.

I noticed there was another similar post before I wrote this. Honestly I had read it written by RastaMouse. But it did’t show our more details about why we should choice gRPC amongs Restful API and WebSocket and gRPC. Hence I dived into it for one week in order to determine the most suitable methods for TeamServer. You could find it here

Why gRPC?

There are several of channel ways between Server and TeamServer that Restful WebAPI,Socket,WebSocket,gRPC and so on. I think we should be familiar with them as soon as possible, knowing yourself and emeny. They are different from each other on merit shortcoming.

Restful API

It was based on http and suitable for little project. Seriously I liked it very much because it always save me time for developing the same functionalities. Surely it’s not a novel technique. If you would like to know more details and I really recommented for you SharpC2 And also you could subscribed to this

Let’s have an journey about ASP.NET Core Web API

[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "1","2" };

[HttpGet("{id}")]
public string Get(int id)
{
return id.ToString();
}

Now you could check it out by openning browser within http://localhost/api/test

The return value show up below by default.

https://localhost:8443/api/test/

[“1”,”2"]

On the one hand, I believed it’s easy to find out its advantages for C# developer. On the other hand, its communication efficiency is very low due to http protocol. Meanwhile if Server disconnected from TeamServer, we know nothing about it.

Socket / WebSocket

Now please focus on Socket communication. Whenever it is an efficient approach between TeamServer and Server. And also if Server disconnected from TeamServer, we will catch this message immediately(in a moment).

https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.close?view=net-5.0

Socket.Close() Closes the Socket connection and releases all associated resources

So It seems as if that were our candidates but if we choose socket to develop that means we should encapsulate everything data type and define by ourselves. It looks like a huge work. I learned from CobaltStrike that Server send some commands or instruction using TeamQueue and TeamSocket to TeamServer which handled everything. For example:

Send("listener.create",Object[] args,Delegate callback);

That’s a good idea indeed. Server side merely send less data to TeamServer, the latter process and invoke the real functionality. We also encapsulated our data type into one static function using C#

public static bool ListenerService(String command,Object[] args,Delegate callback)
{
// some codes
}

However, it is not a better choice for me on Server and TeamServer development. So I was looking for another way to implement my idea and efficient approach. gRPC goes into my eyes by accident!

gRPC ASP.NET Core

gRPC

gRPC is a modern, open source, high-performance remote procedure call (RPC) framework that can run anywhere. gRPC enables client and server applications to communicate transparently, and simplifies the building of connected systems.

it based on http2 and binary data type named proto buffers that not plain text like json. There are so many nuget packages for C# just like Grpc.AspNetCore.Server Grpc.Net.Client. Importantly, based on stream handling request for real time communication channels

Introduce from here: https://docs.microsoft.com/en-us/aspnet/core/grpc/?view=aspnetcore-5.0

You could have an experience online https://grpchttpapi.azurewebsites.net/

Specially credits to @JamesNK

Simple Example

Proto buffers

Before this part begins, you should learn a bit about proto buffers format

http://www.showsql.com/2019/03/18/protobuf-data-type/#proto3%E6%96%B0%E5%A2%9E%E7%9A%84%E7%89%B9%E6%80%A7

Then you got some basic know .proto file, you can find a collection protos file on google project https://github.com/googleapis/googleapis/tree/master/google/api. Remember: syntax = “proto3” for C# is the first step

Show you an example with annovations

syntax = "proto3";
// package name
package greet;

// service name (called this is a interface)
service Auth{
// BearerTokenAuth will be an override method
// AuthRequest as method signatures as well as parameter
rpc BearerTokenAuth (AuthRequest) returns (AuthResponse);
}

// the same as "public class AuthRequest {}"
message AuthRequest{
// data type defined just among double,int32,int64,bool,string,bytes,message,Struct,ListValue,Any and so on
string username = 1;
string password = 2;
}

// the same as "public class AuthResponse {}"
message AuthResponse{
string username = 1;
string Token = 2;
}

Familiar with proto syntax is the first step for learning gRPC that what I think.

There were also many post about tutorials how to use gRPC using .NET or ASP.NET Core. I never plan to repeat these works. Ok this post is just a faster introduction for gRPC beginners and as evalution and summary.

ASP.NET Core (TeamServer side)

greet.proto you can find here

BTW you could define more that one .proto file and also define more that one service in one same proto file. In a nutshell there is no limited about how many service and even you could define more method under the same service

service Auth{
// BearerTokenAuth will be an override method
// AuthRequest as method signatures as well as parameter
rpc BearerTokenAuth (AuthRequest) returns (AuthResponse);
rpc Test1 (AuthRequest) returns (AuthResponse);
rpc Test2 (AuthRequest) returns (AuthResponse);
}
// rpc just like a keywork in programming languages
public class AuthService : Auth.AuthBase
{
// BearerTokenAuth this method name was defined from proto file service Auth\ rpc xxx;
public override Task<AuthResponse> BearerTokenAuth(AuthRequest request, ServerCallContext context)
{
string tokenkey = "This is a tokenkey";


var auth = new BearerTokenAuth(tokenkey);
string resultToken = auth.Authenticate(request.Username, request.Password);

// Task.FromResult(
return Task.FromResult(new AuthResponse
{
Username = request.Username,
Token = resultToken
});
}
}

Server side

To be honest, it gets a bit complex due to adding authentication. But I will show you more details with annovations

// GrpcChannel.ForAddress static function to Create a GrpcChannel instance
using var channel = GrpcChannel.ForAddress("https://localhost:5001");

// Auth appear with proto file within service inside
// AutheClient XXXClient endwith "Client" as a keyword just like Async of C#
var _auth = new Auth.AuthClient(channel);
// BearerTokenAuth is a method appeared inside profo file service Auth defined by rpc keyword
AuthResponse Authreply = await _auth.BearerTokenAuthAsync(
new AuthRequest
{
Username = "test2",
Password = "password2"
});
// CallCredentials.FromInterceptor() for authentication
// Recommented for https://dotnetcorecentral.com/blog/streaming-and-authentication-in-grpc/
var credentials = CallCredentials.FromInterceptor((c, m) =>
{
m.Add("Authorization",
$"Bearer {Authreply.Token}");
return Task.CompletedTask;
});

#endregion
// Once the instance of CallCredentials is created, I will pass it as a part of the GrpcChannelOptions
// parameter to the GrpcChannel.ForAddress

using var channel_auth = GrpcChannel.ForAddress("https://localhost:5001",
new GrpcChannelOptions {
Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
});

gRPC Streaming

https://dotnetcorecentral.com/blog/streaming-and-authentication-in-grpc/

gRPC supports four types of remote procedure calls

service Greeter {
rpc SayHello ( .greet.HelloRequest ) returns ( .greet.HelloReply );
rpc StreamingBothWays ( stream .greet.HelloRequest ) returns ( stream .greet.HelloReply );
rpc StreamingFromClient ( stream .greet.HelloRequest ) returns ( .greet.HelloReply );
rpc StreamingFromServer ( .greet.HelloRequest ) returns ( stream .greet.HelloReply );
}
  • Firstly Unary remote procedure calls. just think of that Server send HelloRequest object to TeamServer and then reply HelloReply object as one time communication. Just like http protocol, the one request, the other response.
  • Secondly Streaming Server. It sends a continuous stream of response back to the client. TeamServer have the ability that take the initiative to send data by itself
  • Thridly Streaming Client. It sends a continuous stream of request to the TeamServer.
  • Fourthly Stream double side between TeamServer and Server

ASP.NET Core

public override async Task StreamingBothWays(IAsyncStreamReader<HelloRequest> requestStream, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
{
var readTask = Task.Run(async () =>
{
await foreach (var message in requestStream.ReadAllAsync())
{
Console.WriteLine(message.Message + ":\t" + message.Name);
await responseStream.WriteAsync(new HelloReply() { Name = "stream double", Message = "1" });
}
});
// send responses until the client signals that it is complete
while (!readTask.IsCompleted)
{
await responseStream.WriteAsync(new HelloReply() { Name = "stream double", Message = "2" });
await Task.Delay(TimeSpan.FromSeconds(1), context.CancellationToken);
}
}

Server side

var reply = client.StreamingBothWays();

var readTask = Task.Run(async () =>
{
await foreach (var item in reply.ResponseStream.ReadAllAsync())
{
Console.WriteLine(item.Name + ":\t" + item.Message);
}
});

while (!readTask.IsCompleted)
{
await reply.RequestStream.WriteAsync(new HelloRequest
{
Name = "GreeterClient",
Message = "whoami"
});

await Task.Delay(TimeSpan.FromSeconds(1));
}

Authentication gRPC

https://dotnetcorecentral.com/blog/streaming-and-authentication-in-grpc/

Firstly, It’s recommented that you for this https://github.com/fullstorydev/grpcui

It’s an interactive web UI for gRPC that similar with ASP.NET Core SWagger.

Configure proto file

package greet;
service Auth{
rpc BearerTokenAuth (AuthRequest) returns (AuthResponse);
}

message AuthRequest{
string username = 1;
string password = 2;
}

message AuthResponse{
string username = 1;
string Token = 2;
}

add UseAuthorization and Authentication() in Startup.cs IApplicationBuilder instance

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>();
// Add Service
endpoints.MapGrpcService<AuthService>();
}
}

AddAuthentication in Startup.cs Servcie.Configure

// var key = Encoding.ASCII.GetBytes("This is my test private key");
var tokenKey = Configuration.GetValue<string>("TokenKey");
var key = Encoding.ASCII.GetBytes("This is a tokenkey");

services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddAuthorization();

services.AddSingleton<IJWTAuthenticationManager>(new BearerTokenAuth_(tokenKey));

Add AuthService.cs

[AllowAnonymous]
public class AuthService : Auth.AuthBase
{
private readonly ILogger<AuthService> _logger;
public AuthService(ILogger<AuthService> logger)
{
_logger = logger;
}

public override Task<AuthResponse> BearerTokenAuth(AuthRequest request, ServerCallContext context)
{
string tokenkey = "This is a tokenkey";


var auth = new BearerTokenAuth_(tokenkey);
string resultToken = auth.Authenticate(request.Username, request.Password);

// Task.FromResult(
return Task.FromResult(new AuthResponse
{
Username = request.Username,
Token = resultToken
});
}
}

Finally, define auth core handler, you could set any functionality you wanna implement it.

public interface IJWTAuthenticationManager
{
string Authenticate(string username, string password);
}
/*
Step
1. Install nuget package Microsoft.AspNetCore.Authentication.JwtBearer
2. Update to the Startup class
*/
public class BearerTokenAuth_ : IJWTAuthenticationManager
{
IDictionary<string, string> users = new Dictionary<string, string>
{
{ "test1", "password1" },
{ "test2", "password2" }
};

private readonly string tokenKey;

public BearerTokenAuth_(string tokenKey)
{
this.tokenKey = tokenKey;
}

public string Authenticate(string username, string password)
{
if (!users.Any(u => u.Key == username && u.Value == password))
{
return null;
}

var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(tokenKey);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
}

Demonstrate

grpcui.exe localhost:5001

We gain JWT token from AuthResponse

And then we add “Authorization”

It’s ok,we finished authentication successfully!

This post was just a beginning for C2 TeamServer, I am willing to be diving into gRPC due to its efficience.

If you like this post,let me know! Your feedback is greatly appriciated!

Reference

https://github.com/protocolbuffers/protobuf

https://docs.microsoft.com/zh-cn/aspnet/core/grpc/?view=aspnetcore-5.0

https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-5.0&tabs=visual-studio

https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/tutorials/grpc/grpc-start/sample

https://cloud.tencent.com/developer/article/1821250

https://github.com/grpc-ecosystem/grpc-gateway

https://grpchttpapi.azurewebsites.net/

https://github.com/googleapis/googleapis/tree/master/google/api

https://github.com/fullstorydev/grpcurl

https://offensivedefence.co.uk/posts/grpc-attack-surface/

https://docs.microsoft.com/en-us/aspnet/core/grpc/test-tools?view=aspnetcore-5.0

https://github.com/fullstorydev/grpcui

https://github.com/grpc/grpc-dotnet

https://docs.microsoft.com/en-us/aspnet/core/grpc/authn-and-authz?view=aspnetcore-5.0

https://dotnetcorecentral.com/blog/streaming-and-authentication-in-grpc/

http://www.showsql.com/2019/03/18/protobuf-data-type/#proto3%E6%96%B0%E5%A2%9E%E7%9A%84%E7%89%B9%E6%80%A7

--

--

bopin2020

Learner | C2 developer | infosec researcher | redteamer