Building mini c2c in Pascal – Part 1

Introduction

Over a while, the development of c2c has increased rapidly, including the number of new commercial frameworks, which I will not mention because I am not here to advertise any, and there are awesome open-sourced projects such as sliver, Covenant, and many more.

In this article, I will shed light on the uncovered offensive side of FreePascal for malware development and share insightful design and code snippets to build a mini command and control demo.
For you as a reader, I am going to divide the series into multiple parts, let’s consider it a workshop for now, and I will try my best to make the timeline relevant.

Requirements

you should have the following for the development environment

  • Lazarus-IDE x64 bit
  • Virtualbox/VM (windows for testing), 1GB RAM / 2 core
  • any Linux box for the team server development.(you need to install Lazarus-ide also )

The architecture design

I have divided the project into three parts.

  • TeamServer: it should handle the whole operation; for the current workshop, it will be for HTTP/HTTPS only. The team server will stand as JSON restful API high-performance server using FPC components and libraries.
  • Operator: a graphical interface to login into the Team server and manage the connected decoys.
  • Decoy: highly customized HTTP agent will work to communicate with a restful API end-point only.

Let’s look first into the whole workflow and then draw the needed basic end-points for communications between the three components.

As the previous figure shows, the agent will first connect to the heart-beat end-point to establish the initial verification with the server. After that, the server will assign a task created by the operator to the connected decoy, and then the decoy will execute the pending actions and exfiltrate the results to sever. So below is the execution playbook diagram of the demo.


Team server development

Let’s start focusing on building the team server; our goal is to achieve a functional team server that should be running over the HTTPS protocol type and using SQLite for storage. And support authentication for multiple registered users(Operators).

By using Multi-threading in Pascal, we could run two server instances simultaneously; one should be for operators and the second for C2 communications with decoys. Below is the suggested diagram for this method.

(Fig 3 )

in other words, our server will have the following classes

TChild = class(TThread)thread class to spawn a new thread for each listener and create threaded pools.
TPasserver = class(TCustomApplication)The main console application program execution flow, it will stand for our API server.
TMyHttpApplication = class(TCustomHTTPApplication)Custom HTTP Application with threading.

Let’s first declare the thread class and invoke TCustomHTTPApplication Class, which also supports threading, but since we developing the team server on Linux, we need to load both units cthreads,cmem

{ RESTful API application } TPasserver = class(TCustomApplication) protected procedure DoRun; override; public constructor Create(TheOwner: TComponent); override; destructor Destroy; override; end; {Child Servers } type TChild = class(TThread) type TMyListeners = class(TCustomHTTPApplication); end; { Parent Server} type TMyHttpApplication = class(TCustomHTTPApplication) protected end;
Code language: Delphi (delphi)

This purpose is to create two functions with different HTTP routers and handlers for our API server, which will be useful to separate the operator’s instance from the decoy’s instance.

Databases

selecting and creating a database engine is required for our API server data storage, for this demonstration, i am going to use SQLite databases, which will have the following tables for now. and you can also use any other database technologies; I just found it easier using sqlite3.

Tasks to store created tasks information
Users this should be used for team server’s operators

SQL Worker

to ensure high server performance, I created the SQL connectivity and procedures in a new process thread. Which will help to handle errors and performance issues.
In other words, if the thread is closed or crashed, the main API server will still be active and create a new SQL worker if needed. The code below covers some of the functions we need for this stage of development.

unit SQL_Worker; {$mode ObjFPC}{$H+} interface uses Classes,cthreads, SysUtils,sqlite3conn, sqldb, db,base64; type TSQL = Class(Tthread) SQLite3Connection: TSQLite3Connection; SQLTransaction : TSQLTransaction; SQL_Query : TSQLQuery; protected // procedure execute; virtual; public constructor Create; procedure connect; procedure add_task(UUID,task_name,task_data:string); procedure check_creds(username:string;password:string; out isvalid:boolean; out token:string); procedure isdecoy(UUID:string; out isvalid:string); end; implementation constructor TSQL.create; begin SQLite3Connection := TSQLite3Connection.Create(nil); SQLTransaction := TSQLTransaction.Create(nil); SQL_Query := TSQLQuery.Create(nil); connect; end; procedure TSQL.connect; var tmp : string; begin SQLite3Connection.DatabaseName:=getcurrentdir+'/database/mydb.db'; try SQLite3Connection.Open; writeln('[+] Successfully connected '); except on E: ESQLDatabaseError do writeln(E.Message); end; end; procedure TSQL.check_creds(username:string;password:string; out isvalid:boolean; out token:string); var sql,usr,pwd :string; count: integer; begin isvalid := false; if SQLite3Connection.Connected then begin //database assignment SQL_query.DataBase:= SQLite3Connection; SQL_query.Transaction:= SQLtransaction; SQLtransaction.DataBase := SQLite3Connection; sql := 'SELECT * FROM users '; sql += 'WHERE username ='+'"'+username+'" '; sql += 'AND password ='+'"'+password+'"'; SQL_query.SQL.Text := sql; //Count := 0; try SQL_query.Open; // dbSQLQuery.First; // while not dbSQLQuery.EOF do begin // Inc(Count); usr := SQL_query.FieldByName('username').AsString; pwd := SQL_query.FieldByName('password').AsString; if ( trim(username) = usr) AND ( trim(password) = pwd) then begin isvalid := true; token := EncodeStringBase64(usr+':'+pwd); // dbSQLQuery.Next; // end; SQL_query.Close; end; except on E: ESQLDatabaseError do begin writeln(E.Message); end; end; end; end; procedure TSQL.isdecoy(UUID:string; out isvalid:string); var task,query :string; count:integer; begin if SQLite3Connection.Connected then begin //database assignment SQL_query.DataBase:= SQLite3Connection; SQL_query.Transaction:= SQLtransaction; SQLtransaction.DataBase := SQLite3Connection; // Create query Query := 'SELECT UUID FROM decoys WHERE UUID = '+'"'+UUID+'"'; // Query the database SQL_query.SQL.Text := Query; Count := 0; try SQL_query.Open; SQL_query.First; while not SQL_query.EOF do begin Inc(Count); // task := dbSQLQuery.FieldByName('res_body').AsString; UUID := SQL_query.FieldByName('UUID').AsString; isvalid := UUID; SQL_query.Next; end; SQL_query.Close; except on E: ESQLDatabaseError do begin writeln(E.Message); end; end; end; end; procedure TSQL.add_task(UUID,task_name,task_data:string); var sql,task_status :string; task_id : integer; begin Randomize; task_status := 'PENDING'; task_id := random(100) + 10000; Sql := 'INSERT INTO tasks (UUID,task_name,task_data,task_status,task_id) '; Sql += 'VALUES ("' + UUID+'","' + task_name+ '","'+ task_data+'","'+ task_status+'","'+ inttostr(task_id)+'")'; try SQL_query.DataBase:= SQLite3Connection; SQL_query.Transaction:= SQLtransaction; SQLtransaction.DataBase := SQLite3Connection; SQL_query.SQL.Text := Sql; SQL_query.ExecSQL; SQLtransaction.Commit; except on E: ESQLDatabaseError do writeln(E.Message); end; end; end.
Code language: Delphi (delphi)

Building the API

Now that we have a clear overview of the application architecture, let’s start building our server API and integrate it with our database. and will cover the remaining parts in the next blog post.

/authstands for a basic authentication mechanism
/decoys/list list all connected decoys
/listeners/createcreate listeners
/tasks/addadd new tasks
/tasks/updateupdate specific tasks

First, we need to create the HTTPS server application, which will handle the Team server API for operators, and for that reason, I have implemented the following code snippet, which will give the team server the functionality of JSON REST API.

function Team_server: TMyHttpApplication; begin if not Assigned(_Parent) then begin _Parent := TMyHttpApplication.Create(nil); _Parent.Port := 8000; // listening port // if using SSL, need self-sign or valid SSL cert _Parent.UseSSL:=true; _Parent.CertificateData.HostName := 'zux0x3a-virtual-machine'; _Parent.CertificateData.KeyPassword:='123456'; _Parent.CertificateData.PrivateKey.FileName := getcurrentdir+'/key.pem'; _Parent.CertificateData.Certificate.FileName := getcurrentdir+'/cert.pem'; with LAZ do begin try // authentication HTTPRouter.RegisterRoute('/auth/',@auth); // LAZ client will connect and assign a task for decoy (Protected) HTTPRouter.RegisterRoute('/tasks/add/', @add_task); _Parent.HostName:='192.168.33.135'; _Parent.UseSSL:=true; _Parent.Threaded := True; _Parent.Initialize; except on E : Exception do begin writeln(E.message); end; end; end; Result := _Parent; end; end;
Code language: PHP (php)

With blew two functions, we can magically implement REST JSON API with Authentication. I would like to thank Marcus Fernström for this article.

procedure TPasserver.jsonResponse(res: TResponse; JSON: TJSONObject; httpCode: integer); begin res.Content := JSON.AsJSON; res.Code := httpCode; res.ContentType := 'application/json'; res.ContentLength := length(res.Content); res.SendContent; end; procedure TPasserver.rerouteRoot(aRequest: TRequest; aResponse: TResponse); begin aResponse.Code := 301; //aResponse.SetCustomHeader('Location', fileLocation + '/index.html'); aResponse.SendContent; end; procedure TPasserver.validateRequest(aRequest: TRequest); var headerValue, b64decoded, username, password,usr,pwd,token: string; magic:string; isvalid:boolean; begin headerValue := aRequest.Authorization; writeln(headervalue); if length(headerValue) = 0 then raise Exception.Create('This endpoint requires authentication'); if ExtractWord(1, headerValue, [' ']) <> 'Basic' then raise Exception.Create('Only Basic Authentication is supported'); b64decoded := DecodeStringBase64(ExtractWord(2, headerValue, [' '])); username := ExtractWord(1, b64decoded, [':']); password := ExtractWord(2, b64decoded, [':']); magic := extractword(2,headervalue,[' ']); with DB do begin check_creds(username,password,isvalid,token); // will perform validation and output the token end; if (token <> magic ) then // if token match the submitted header, then hola raise Exception.Create('Invalid API credentials'); end;
Code language: JavaScript (javascript)

After that, I created the two required functions for adding tasks, and the authentication end-point, which the operator will use to log in and assign tasks into decoys.

procedure TPasserver.auth(req: TRequest; res: Tresponse); var jObject : TJSONobject; username,password,token : string; okay:boolean; httpCode: integer; begin okay := false; jObject := TJSONObject.Create; try username := req.contentfields.values['user']; password := req.ContentFields.Values['pwd']; with DB do begin check_creds(username,password,okay,token); if (okay = true ) then begin jObject.Add('token',token); jsonresponse(res,Jobject,httpCode); end; end; finally jobject.Free; end; end;
Code language: JavaScript (javascript)

As shown in the figure below, the server will respond with a token that will be used for authentication purposes; since the authentication bearer type is Basic, we may need to upgrade that in the upcoming series.

However, we can also protect any end-point with valid authentication, and that’s what I have done for the /tasks/add end-point.

procedure TPasserver.add_task(req: Trequest; res: TResponse); var jObject : TJSONobject; UUID,Res_str,task_name,task_data,validation:string; httpcode:integer; begin jObject := TJSONObject.Create; try try validateRequest(req); except on E: Exception do begin jObject.Add('success', False); jObject.Add('reason', E.message); httpCode := 401; end; end; jsonresponse(res,Jobject,httpcode); UUID := req.contentfields.values['UUID']; task_name := req.contentfields.values['task_name']; task_data := req.contentfields.Values['task_data']; with DB do begin isdecoy(UUID,validation); if (validation = UUID) then begin add_task(UUID,task_name,task_data); end; end; jObject.Add('UUID',UUID); jObject.Add('task_name',task_name); Jobject.Add('task_data',task_data); jsonresponse(res,Jobject,httpcode); finally jobject.Free; end; end;
Code language: PHP (php)


by adding a request validation code statement at the begining of the add_task API procedure, a request authentication mechanism is required to access the resources.

And in case the authorization token is invalid, the server will respond with invalid API credentials, as shown below.

What’s next?

As I don’t want the information to be too boring and long, I am going to divide the series into multiple parts, so in the next article, I will cover the following :

  • Complete the whole team server.
  • Start building the operator GUI interface.

Github repository

I will share the development stages for the PAS mini c2c project in workshop style, so each article will have a different folder to ensure enjoying the learning experience rather than getting the code and compiling it.

https://github.com/0xsp-SRD/PAS-mini-c2c-/tree/main/DEV-01

Please follow and like us: